基于C++中的默认拷贝函数的使用详解
在C++中,当我们定义了一个类,并对其进行实例化时,如果没有手动定义拷贝构造函数或拷贝赋值运算符,那么编译器会自动为该类提供默认的拷贝构造函数和拷贝赋值运算符。本文将详细讲解这两种默认拷贝函数的使用。
默认拷贝函数的定义
默认拷贝函数的定义如下:
class MyClass {
public:
MyClass(const MyClass& other); // 拷贝构造函数
MyClass& operator=(const MyClass& other); // 拷贝赋值运算符
//...
};
其中,拷贝构造函数用于在创建一个新的对象时,以已有对象的值为基础创建一个新的对象,并完成拷贝,其定义方式为:
MyClass(const MyClass& other);
拷贝赋值运算符用于在已有对象的基础上,将另一个对象的值拷贝到该对象中,其定义方式为:
MyClass& operator=(const MyClass& other);
默认地,这两个函数都执行浅拷贝(即只拷贝指针,不拷贝指针所指向的内容),并且,在使用默认拷贝函数时,我们的类必须满足具有默认拷贝函数的 三大法则。
三大法则
三大法则是指,在使用默认拷贝函数时,我们的类必须满足以下三个条件:
- 若该类没有手动定义拷贝构造函数,则编译器会为其自动生成拷贝构造函数;
- 若该类没有手动定义拷贝赋值运算符,则编译器会为其自动生成拷贝赋值运算符;
- 若该类定义了析构函数,则它必须能够正确地释放该类对象中的所有资源。
如果我们的类不满足这三个条件中的任何一个,就不能使用默认拷贝函数。
示例一:浅拷贝的问题
考虑下面这个示例:
class ShallowCopy {
public:
ShallowCopy(int size) {
p_data = new int[size];
m_size = size;
}
~ShallowCopy() {
delete[] p_data;
}
private:
int* p_data;
int m_size;
};
ShallowCopy a(10); // 声明一个对象a
ShallowCopy b = a; // 声明一个对象b,并将a的值拷贝给b
上面的代码使用了默认的拷贝构造函数,将a的值拷贝给了b,但是这个拷贝过程只是拷贝了指针,并没有拷贝指针所指向的内存内容。结果是,当a和b对象同时离开作用域时,它们都会释放指向同一内存块的指针,导致程序崩溃。
为了解决这个问题,我们需要手动定义拷贝构造函数,完成自己的深拷贝过程。修改后的代码如下:
class DeepCopy {
public:
DeepCopy(int size) {
p_data = new int[size];
m_size = size;
}
DeepCopy(const DeepCopy& other) { // 手动定义拷贝构造函数
m_size = other.m_size;
p_data = new int[m_size];
for (int i = 0; i < m_size; ++i) {
p_data[i] = other.p_data[i];
}
}
~DeepCopy() {
delete[] p_data;
}
private:
int* p_data;
int m_size;
};
DeepCopy a(10);
DeepCopy b = a;
这个示例中,我们手动定义了拷贝构造函数,并在其中完成了拷贝构造过程。由于我们完成了数据的深拷贝,当a和b对象同时离开作用域时,程序就不会崩溃了。
示例二:手动定义拷贝赋值运算符
看一下下面这个示例:
class MyClass {
public:
MyClass(int value) {
m_value = new int(value);
}
~MyClass() {
delete m_value;
}
void setValue(int value) {
*m_value = value;
}
protected:
int* m_value;
};
MyClass a(10); // 声明一个对象a
MyClass b(20); // 声明一个对象b
b = a; // 将a的值赋值给b
上述代码中,我们使用了默认的拷贝赋值运算符,将a的值赋值给了b,但是这个赋值过程只是拷贝了指针,并没有拷贝指针所指向的内存内容。结果是,当a和b对象同时离开作用域时,它们都会释放指向同一内存块的指针,导致程序崩溃。
为了解决这个问题,我们需要手动定义拷贝赋值运算符,并在其中完成自己的深拷贝过程。修改后的代码如下:
class MyClass {
public:
MyClass(int value) {
m_value = new int(value);
}
~MyClass() {
delete m_value;
}
void setValue(int value) {
*m_value = value;
}
MyClass& operator=(const MyClass& other) { // 手动定义拷贝赋值运算符
if (this == &other) {
return *this;
}
delete m_value;
m_value = new int(*other.m_value);
return *this;
}
protected:
int* m_value;
};
MyClass a(10);
MyClass b(20);
b = a;
这个示例中,我们手动定义了拷贝赋值运算符,并在其中完成深拷贝过程。当a和b对象同时离开作用域时,程序就不会崩溃了。
总结
本文讲解了C++中默认拷贝函数的使用,介绍了默认拷贝函数的定义、三大法则,以及使用示例。同时,我们认识到了浅拷贝的问题,以及解决浅拷贝问题的方法。为保证程序的稳定性和高效性,请合理使用拷贝构造函数和拷贝赋值运算符,避免使用默认拷贝函数。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:基于c++中的默认拷贝函数的使用详解 - Python技术站