引言
先看一段带有拷贝构造函数的示例代码:
#include <iostream>
using namespace std;
class Car{
public:
string c_brand;
float c_acceleration;
Car() {
c_brand.clear();
c_acceleration = 0.0;
cout << "Car constructor" << endl;
}
Car(const Car &c) {
c_brand = c.c_brand;
c_acceleration = c.c_acceleration;
cout << "Car copy constructor" << endl;
}
~Car() {cout << "Car destructor" << endl;}
void show() {cout << "Brand: " << c_brand << ", Acceleration: " << c_acceleration << endl;}
};
int main() {
Car c1;
c1.c_brand = "Toyota";
c1.c_acceleration = 10.0;
Car c2 = c1;
c2.show();
return 0;
}
现在这个拷贝构造函数
和编译器提供的拷贝构造函数
功能是一样的,唯一的不同就是多了一行打印信息。运行结果如下:
Car constructor
Car copy constructor
Brand: Toyota, Acceleration: 10
Car destructor
Car destructor
目前,Car
类里面的成员都是普通变量,用的是栈
内存,不存在浅拷贝的问题。
浅拷贝
给Car
类增加一个指针成员c_ptr
,计划使用堆
内存。示例代码如下:
#include <iostream>
using namespace std;
class Car{
public:
string c_brand;
float c_acceleration;
int* c_ptr; // 添加的指针成员变量
Car() {
c_brand.clear();
c_acceleration = 0.0;
c_ptr = nullptr; // 初始化指针为空
cout << "Car constructor" << endl;
}
Car(const Car &c) {
c_brand = c.c_brand;
c_acceleration = c.c_acceleration;
c_ptr = c.c_ptr; // 直接拷贝指针,这在大多数情况下是不安全的
cout << "Car copy constructor" << endl;
}
~Car() {
delete c_ptr; c_ptr = nullptr;
// delete 一个空指针 nullptr 在 C++ 中是安全的,因此不会产生运行时错误。
// 在释放后,将 c_ptr 设置为 nullptr,防止后续操作使用悬空指针。
cout << "Car destructor" << endl;
}
void show() {cout << "Brand: " << c_brand
<< ", Acceleration: " << c_acceleration
<< ", c_ptr: " << c_ptr
<< ", *c_ptr: " << *c_ptr << endl;
}
};
int main() {
Car c1;
c1.c_brand = "Toyota";
c1.c_acceleration = 10.0;
Car c2(c1);
c2.show();
return 0;
}
运行结果如下:
Car constructor
Car copy constructor
Segmentation fault (core dumped)
运行程序会报错,原因在于:对空指针解引用
是非法的
。注释掉对应代码:
void show() {cout << "Brand: " << c_brand
<< ", Acceleration: " << c_acceleration
/*<< ", c_ptr: " << c_ptr
<< ", *c_ptr: " << *c_ptr */<< endl;
}
运行结果如下:
Car constructor
Car copy constructor
Brand: Toyota, Acceleration: 10
Car destructor
Car destructor
给c.c_ptr
赋值,并取消注释:
int main() {
Car c1;
c1.c_brand = "Toyota";
c1.c_acceleration = 10.0;
c1.c_ptr = new int(5);
Car c2(c1);
c2.show();
return 0;
}
运行结果如下:
Car constructor
Car copy constructor
Brand: Toyota, Acceleration: 10, c_ptr: 0x55656a210ec0, *c_ptr: 5
Car destructor
free(): double free detected in tcache 2
Aborted (core dumped)
报错的原因在于,第二次调用析构函数时,再次释放了同一块内存。只有一个对象销毁成功了,由于另一个对象在销毁的时候操作了野指针,所以程序崩溃。
在目前这个程序中,浅拷贝
造成了程序的崩溃,还引起了堆区
数据的混乱。
把指针A
的值直接赋值给指针B
,那么A
和B
的指向是同一块内存,这就是浅拷贝
。通过一个指针改变内存
中的数据
,另一个指针获取的数据肯定也会改变
;其中一个指针释放了内存,另一个指针就成了野指针,调用析构函数释放野指针,程序会崩溃。
深拷贝
指针A
和指针B
指向不同的内存
,把指针A
管理的内存中的数据
拷贝到指针B
管理的内存
中。拷贝后,各自管理各自的内存,不会有任何冲突。
编译器提供的拷贝构造函数是浅拷贝
,深拷贝
的代码如下:
#include <iostream>
#include <cstring>
using namespace std;
class Car{
public:
string c_brand;
float c_acceleration;
int* c_ptr; // 添加的指针成员变量
Car() {
c_brand.clear();
c_acceleration = 0.0;
c_ptr = nullptr; // 初始化指针为空
cout << "Car constructor" << endl;
}
Car(const Car &c) {
c_brand = c.c_brand;
c_acceleration = c.c_acceleration;
c_ptr = new int; // 分配内存
// *c_ptr = *(c.c_ptr); // 拷贝数据
memcpy(c_ptr, c.c_ptr, sizeof(int)); // 两种拷贝数据的方式均可
cout << "Car copy constructor" << endl;
}
~Car() {
delete c_ptr; c_ptr = nullptr;
cout << "Car destructor" << endl;
}
void show() {cout << "Brand: " << c_brand
<< ", Acceleration: " << c_acceleration
<< ", c_ptr: " << c_ptr
<< ", *c_ptr: " << *c_ptr << endl;
}
};
int main() {
Car c1;
c1.c_brand = "AUDI";
c1.c_acceleration = 5.0;
c1.c_ptr = new int(66);
c1.show();
Car c2(c1);
*c2.c_ptr = 888;
c2.show();
c1.show();
return 0;
}
运行效果如下:
Car constructor
Brand: AUDI, Acceleration: 5, c_ptr: 0x55afe2c0eec0, *c_ptr: 66
Car copy constructor
Brand: AUDI, Acceleration: 5, c_ptr: 0x55afe2c0eee0, *c_ptr: 888
Brand: AUDI, Acceleration: 5, c_ptr: 0x55afe2c0eec0, *c_ptr: 66
Car destructor
Car destructor
感谢浏览,一起学习!