C++面向对象(十):拷贝构造函数
拷贝构造函数
- 拷贝构造函数是构造函数的一种
- 当利用已存在的对象创建一个新对象时(类似于拷贝),就会调用新对象的拷贝构造函数进行初始化
- 拷贝构造函数的格式是固定,接收一个 const 引用作为参数
#include <iostream>
using namespace std;
class Car {
int m_price;
int m_length;
public:
// 构造函数
Car(int price = 0, int length = 0) :m_price(price), m_length(length){
cout << "Car(int price = " << m_price << " int length =" << m_length << endl;
}
// 拷贝构造函数
// 默认情况下,没必要写这个函数
Car (const Car &car) :m_price(car.m_price), m_length(car.m_length) {
cout << "Car(cosnt Car &car)" << endl;
}
void display () {
cout << "price=" << m_price << ", length=" << m_length << endl;
}
};
int main() {
Car car1(666, 6);
// 利用已经存在的car3对象创建了一个car4对象
// car4初始化时会调用拷贝构造函数
Car car2(car1);
// 没有拷贝函数时,类似于这种操作:
/*car4.m_price = car3.m_price;
car4.m_length= car3.m_length;*/
car2.display();
getchar();
return 0;
}
何时调用:
class Car {
int m_price;
int m_length;
public:
// 构造函数
Car(int price = 0, int length = 0) :m_price(price), m_length(length){
cout << "Car(int price = " << m_price << " int length =" << m_length << ")\n";
}
// 拷贝构造函数
Car (const Car &car) :m_price(car.m_price), m_length(car.m_length) {
cout << "Car(cosnt Car &car)" << endl;
}
void display () {
cout << "price=" << m_price << ", length=" << m_length << endl;
}
};
int main () {
// 创建了四个对象
Car car1 (6, 666); // 非拷贝构造
Car car2 (car1); // 拷贝构造
Car car3 = car2; // 与上面的等价,拷贝构造
Car car4; // 非拷贝构造
car4 = car3; // 是一个赋值操作(默认是浅复制),并不会调用拷贝构造函数
// 打印为 6, 666
car4.display ();
getchar ();
return 0;
}
调用父类的拷贝构造函数
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
Person(int age = 0) :m_age(age) {}
Person(const Person &person) :m_age(person.m_age) {}
};
class Student : public Person {
public:
int m_score;
Student(int age = 0, int score = 0) :Person(age), m_score(score) {}
// 如果不调用父类的拷贝函数成员,就不会进行拷贝: Person(student)
Student (const Student &student) : Person(student), m_score(student.m_score) {}
};
int main () {
Student stu1 (6, 666);
// 默认就会拷贝,旧对象的值会将新对象中的所有数据覆盖掉
Student stu2 (stu1);
// 如果不调用父类的拷贝函数成员,就不会进行拷贝
cout << stu2.m_age << endl;
cout << stu2.m_score << endl;
getchar ();
return 0;
}
浅拷贝和深拷贝
浅拷贝
编译器默认的提供的拷贝是浅拷贝(shallow copy)
- 将一个对象中所有成员变量的值拷贝到另一个对象
- 如果某个成员变量是个指针,只会拷贝指针中存储的地址值,并不会拷贝指针指向的内存空间
- 可能会导致堆空间多次free的问题
#include <iostream>
using namespace std;
class Car {
int m_price;
char *m_name;
public:
Car (int price = 0, char *name = NULL) :m_price(price), m_name(name) {
}
void display () {
// 危险,m_name后面的值可能被回收,之后就变成了垃圾数据
cout << "price=" << m_price << ", length=" << m_name<< endl;
}
};
Car *g_car;
void test () {
char name[] = { 'B', 'M', 'W', '\0', };
// 这样堆空间的指针(name)指向栈空间的数据(BMW),很危险
// 因为不能控制栈空间的生命周期可能会变成野指针
g_car = new Car (100, name);
}
int main () {
test ();
g_car->display ();
getchar ();
return 0;
}
深拷贝
为了避免这种情况,而在对空间申请同样大小的空间,并且将栈空间的数据拷贝过来就行了:
class Car {
int m_price;
char *m_name;
// 定义一个拷贝函数
void copyName (const char *name) {
if (name == NULL) return;
// 申请新的对空间,需要额外的1存储\0,加上{}将对堆空间的数据初始化为0
// 以保证最后一个一定为0
m_name = new char[strlen (name) + 1]{};
// 拷贝字符串数据到新的堆空间,直到遇上\0为止
// 会报错,我执意要用这个,所以右键项目->属性->C/C++->命令行
// 加上-D"_CRT_SECURE_NO_WARNINGS"
strcpy (m_name, name);
}
public:
// 重写构造函数
Car (int price = 0, const char *name = NULL) :m_price (price) {
copyName (name);
}
// 重写拷贝构造函数
Car (const Car &car) :m_price(car.m_price) {
copyName (car.m_name);
}
// double free: 如果有两个对象,就会delete两次
~Car () {
if (m_name == NULL) return;
delete[] m_name;
m_name = NULL;
}
void display () {
cout << "price=" << m_price << ", length=" << m_name << endl;
}
};
- 如果需要实现深拷贝(deep copy),就需要自定义拷贝构造函数
- 将指针类型的成员变量所指向的内存空间,拷贝到新的内存空间