13.1 拷贝、赋值与销毁
13.1.1 copy constructor
拷贝构造函数
第一个参数是引用类型
Foo(const Foo&);
- 直接初始化和拷贝初始化
Person p1("kavin", 23); // 直接初始化,调用自定义的构造函数
Person p2(p1); // 直接初始化,调用拷贝构造函数
Person p3 = p1; // 拷贝初始化,调用拷贝构造函数
使用拷贝初始化时,我们要求编译器将右侧运算对象拷贝到正在创建的对象中,如果需要的话还要进行类型转换
class Person {
private:
string name;
int age;
public:
Person(string name, int age) : name(name), age(age) {
cout << "constructor" << endl;
}
Person(const Person& rhs) {
cout << "copy constructor" << endl;
name = rhs.name;
age = rhs.age;
}
};
int main() {
Person p1("kavin", 23); // 调用自定义构造函数
Person *ptr = new Person("kavin", 34); // 调用自定义构造函数
Person p2(p1); // 调用拷贝构造函数直接初始化
Person p3 = p1; // 调用拷贝构造函数拷贝初始化
return 0;
}
// constructor
// constructor
// copy constructor
// copy constructor
用等号来构造对象就是拷贝初始化,后面带个括号的是直接初始化
拷贝初始化不仅在我们用=
定义变量时发生,在下列情况下也会发生
1、将一个对象作为实参传递给一个非引用类型的形参
void func(Person p) {
cout << "Entering func" << endl;
return;
}
int main() {
Person p1("kavin", 23);
func(p1);
return 0;
}
// constructor
// copy constructor 调用了拷贝构造函数
// Entering func
也说明了拷贝构造函数必须是引用类型,否则,实参传递给拷贝构造函数的形参时就得再调用拷贝构造函数,就会陷入循环
2、 从一个返回类型为非引用类型的函数返回一个对象
Person func() {
Person *p = new Person("lisa", 34);
return *p;
}
int main() {
Person p1 = func();
return 0;
}
// constructor
// copy constructor 调用了拷贝构造函数
13.1.2 copy-assignment operator
拷贝赋值运算符
参数是类类型的const
引用
返回值是指向其左侧运算对象的引用
Foo& operator=(const foo&);
代码示例
class Person {
private:
string name;
int age;
public:
Person() {}
Person(string name, int age) : name(name), age(age) {}
Person& operator=(const Person& rhs) {
name = rhs.name;
age = rhs.age;
return *this;
}
};
int main() {
Person p1("kavin", 23); // 调用自定义构造函数
Person p2; // 调用默认构造函数
p2 = p1; // 调用拷贝赋值运算符
return 0;
}
这里是给对象赋值,不是构造对象,所以调用拷贝赋值运算符,而不是拷贝构造函数
13.3.3 destructor
析构函数
- 不接受参数,也没有返回值
- 不能被重载
- 对一个给定类,只会有唯一一个析构函数
- 当指向一个对象的引用或指针离开作用域时,『不会调用析构函数』
~Foo();
在一个析构函数中,首先执行函数体,然后销毁成员,成员按初始化顺序的逆序销毁
如果一个类需要析构函数,那肯定也需要拷贝构造函数和拷贝赋值运算符,尤其是含有动态分配内存的类,因为合成析构函数不会delete
一个指针数据成员