【C++】3. 与构造函数等相关

1、构造函数和析构函数

  • 如果程序员没有提供这两函数,系统会自动提供。

  • 这俩都得在public下
    (1)构造函数

    相当于初始化
    与类名相同,没有返回值,不写void,可以重载(不同的参数)
    构造函数由编译器自动调用而不是手动,而且只会调用一次

class Person{  
public:  
    Person(){  
        cout<<"无参的构造函数"<<endl;  
    }};  
void test(){  
    Person p1;//这里只是初始化了p1这个对象。但构造函数和析构函数都会执行  
}

(2)析构函数

相当于**清理**
与类名相同,类名前面加一个符号“~”,也没有返回值所以不写void,**不可以有参数(不能重载)**
自动调用,只调用一次
~Person(){  
    cout<<"析构函数"<<endl;  
};

2、构造函数的分类及调用

  • 按照参数进行分类:无参(默认),有参
  • 按照类型进行分类:普通构造函数,拷贝构造函数
    (1)拷贝构造函数
Person(const Person &p){  
    age=p.age;//拷贝别人数据  
    cout<<"拷贝构造函数调用了!"<<endl;  
}

ps:默认构造函数不要加()
(2)调用方法有:

  • 括号法调用
  • 显示法调用
    • Person p4=Person(100);
      Person(100)叫匿名对象,匿名对象待定。如果编译器发现了对象是匿名的,那么在这行代码结束后就释放这个对象
    • ![[Pasted image 20220930214937.png]]![[Pasted image 20220930215021.png]]
  • 隐式类型转换相当于调用Person p=Person(100)
    • ![[Pasted image 20220930215723.png]]

3、拷贝构造函数调用时机

  1. 用已经创建好的对象来初始化新的对象(很常用)
Person p1;  
p1.age=100;  
Person p2(p1);//  用已经创建好的对象来初始化新的对象
  1. 以值传递的方式给函数参数传值
void doWork(Person p1){//相当于Person p1=Person(p)  
  
}
void test02(){  
    Person p;  
    p.age=1000;  
    doWork(p);//相当于Person p1=Person(p)  
}
  1. 以值的方式返回局部对象(不是很常用)
Person doWork2(){  
    Person p1;  
    return p1;  
}  
void test03(){  
    Person p=doWork2(); //Person p=Person(p1) 
}

4、构造函数的调用规则

  1. 系统默认提供的:无参构造,拷贝构造,析构函数.
  2. 当我们提供了有参的构造函数(且我们没写默认无参构造函数),那么系统就不会给我们提供默认构造函数了. 但是系统还会提供默认的拷贝构造函数,进行简单的值拷贝.
  3. 当我们提供了拷贝构造(且我们没写默认无参构造函数),那系统就不会提供其他构造了.

5、深拷贝和浅拷贝

如果属性里有指向堆区空间的数据,那么浅拷贝会导致重复释放内存的异常!
  1. 浅拷贝:只把地址拷贝过来,系统简单的值拷贝
class Person{  
public:  
    char * name;  
    int age;  

    Person(char * iname,int iage){  
        name= (char *)malloc(strlen(iname)+1);//属性开辟到堆上了  
        strcpy(name,iname);  
        age=iage;  
    }       
  
    ~Person(){  
        cout<<"析构函数"<<endl;  
        if(name!=NULL){  //name属性开辟到堆上了,要释放  
            free(name);  
            name=NULL;  
        }    }  
};

![[Pasted image 20221005205735.png]]

void test01(){  
    Person p1("王志",23);  
    Person p2(p1);//拷贝构造!  
}
  1. 深拷贝:开辟一个新的空间
    ![[Pasted image 20221005205833.png]]
  • 要自己提供拷贝构造,因为浅拷贝会释放堆区空间两次,会奔溃
  • 深拷贝,这里为新的name创建了新的空间
    Person(const Person &p){  
        name= (char *)malloc(strlen(p.name)+1);//创建了新的空间  
        strcpy(name,p.name);  
        age=p.age;  
    }

6.多个对象的构造和析构

(1)初始化列表

构造函数:属性(参数),属性(参数),属性(参数){}

①有参构造函数这样写:
Person(int ia,int ib,int ic):a(ia),b(ib),c(ic){}
利用初始化列表来初始化数据.相当于

Person(int ia,int ib,int ic){  
    a=ia;  
    b=ib;  
    c=ic;  
}

②无参构造函数这样写:
Person():a(200),b(3000),c(1){}
相当于写死了.

(2)类对象作为成员的案例

  • 类对象作为类成员的时候,构造顺序先将类对象一一构造,然后构造自己.
  • 析构的顺序是相反的.

7.explicit关键字

防止构造函数中的隐式类型转换

8.动态对象创建

new与delete配套->堆区
malloc与free配套->栈区

(1)new

C++中解决动态内存分配的方案是把创建一个对象所需要的操作都结合在一个称为new的运算符里。
new是运算符!

当用new创建一个对象时,它就在堆里为对象分配内存调用构造函数完成初始化:
Person* person = new Person;//堆区开辟
Person p1;//栈区开辟

  • 所有new出来的对象,都会返回该类型的指针,所以要用ClassName * 去接
  • malloc返回的是void *,所以必须在malloc之前用(ClassName *)强转
  • malloc不会调用构造函数;new会调用构造函数.
  • new是运算符,malloc是函数

(2)delete

delete用来释放堆区空间.

(3)用于数组的new和delete

  • 写法:
    • className * arrayName=new className[size];
    • delete[] arrayName;
//创建字符数组
char* pStr = new char[100];
//创建整型数组
int* pArr1 = new int[100]; 
//创建整型数组并初始化
int* pArr2 = new int[10]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

//释放数组内存
delete[] pStr;
delete[] pArr1;
delete[] pArr2;
  • 注意事项:
    • 创建堆上对象数组必须提供默认的构造函数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值