构造函数和析构函数
构造函数:给对象进行初始化,主要为对象的成员属性。
析构函数:清理对象。
必须有构造函数和析构函数,一般控制权限都是public。如果用户不提供,则使用编译器提供的构造函数和析构函数,编译器提供的构造函数和析构函数是空实现(对成员属性什么都不做)。
构造函数和析构函数编译器自动调用
先创建构造函数的实例后调用析构函数
构造函数:
语法:类名( ) { }
- 没有返回值,也不需要写void
- 函数名称和类名相同
- 构造函数可以有参数,也可以重载
- 调用对象时自动调用,且只调用一次。
//析构函数
Person(string _name){
name=_name;
}
Person(string _name,int _age){
name=_name;
age=_age;
}
分类:
按参数分:有参构造和无参(默认)构造
Person(){
cout<<"无参构造"<<endl;
}
Person(string name){
cout<<"有参构造"<<endl;
}
按类型分:普通构造和拷贝(copy)构造
拷贝构造函数中必须使用引用
原因:如果通过值传递的方式,调用拷贝函数时就要进行实例化一个对象,实例化对象又要调用拷贝函数,形成无限递归。
Person(const Person& p){
name = p.name;
age = p.age;
}
拷贝构造函数的作用:
- 使用一个创建完成的对象来初始化另一个对象
- 值传递时给函数传参
- 函数返回一个对象
默认情况:
-
默认构造函数,无参,函数体为空
-
默认析构函数,无参,函数体为空
-
默认拷贝构造函数,对属性进行值拷贝
注意事项:
- 如果用户定义有参构造函数,则编译器不再提供上述1的默认构造函数
- 如果用户定义拷贝构造函数,则编译器不再提供上述1和3提供的构造函数
深拷贝和浅拷贝
浅拷贝:值拷贝,编译器提供的默认拷贝函数就是浅拷贝
class Person {
public:
string name;
int* age;
Person(string _name, int _age) {
name = _name;
age = new int(_age);
}
~Person() {
if (age != NULL) {
delete age;
age = NULL;
}
cout << "析构函数启动" << endl;
}
};
.
.
.
Person p1("killer", 23);
cout << *(p1.age) << endl;
Person p2(p1);
cout << *(p2.age) << endl;//报错
return 0;
深拷贝:在堆区重新申请空间,进行拷贝操作
class Person {
public:
string name;
int* age;
Person(string _name, int _age) {
name = _name;
age = new int(_age);
}
//用户定义的深拷贝函数
Person(const Person& p) {
name = p.name;
age = new int(*(p.age));
}
~Person() {
if (age != NULL) {
delete age;
age = NULL;
}
cout << "析构函数启动" << endl;
}
};
调用:
- 括号法
Person p0;//无参构造
Person p1 ("张三");//有参构造
Person p2 (p1);//拷贝构造
注意事项:
1. 调用无参构造时Person p0 ( );//错误,编译器会认为这是一个函数声明
- 显示法
Person p1 = Person("张三");//有参构造
Person p2 = Person(p1);//拷贝构造
注意事项:
1. Person("张三")是一个匿名对象,在当前行执行完后,编译器就会释放该匿名对象。
2. 不要利用拷贝构造函数初始化一个匿名对象
Person (p3);//错误,Person (p3);相当于Person p3;
3.隐式转换法
string str = "张三";
Person p1 = str;//相当于Person p1 = Person("张三");
Person p2 = p1;//相当于Person p2 = Person(p1);
析构函数
语法:~类名( ) { }
- 没有返回值,也不需要void
- 函数名称与类名相同,在名称前加符号~
- 析构函数没有参数,不能重载
- 在对象销毁前自动调用,且只调用一次。
~Person(){
cout<<"end"<<endl;
}