在C++中,伴随类的有三个已经写好的特殊函数,它们是析构函数、复制构造函数和operator=。在许多情况下,都可以采用编译器提供的默认操作,有些时候却不行。
1,析构函数
析构函数是每个类中必要的函数,一般不需要单独定义,在类形成的时候会自动定义。当一个对象超出其作用域或者执行delete时,就调用析构函数。通常,析构函数的作用是释放使用对象时占用的所有资源,这其中包括每一个相应的new调用delete,以及关闭所有打开的文件等。默认操作是对每一个成员变量都使用析构函数。析构函数的固定形式是~ClassName();。
2,复制构造函数
在我们定义类的对象的时候,一般都会用到复制构造函数,复制构造函数一般可以通过下面三种方式调用(例如Test对象):
- 声明的时候初始化,例如 Test B = A; Test B( A ); 而不是 B = A;。
- 使用按值调用传递(而不是通过&或constant &)的对象无论如何都应该减少使用。
- 通过值(而不是通过&或const &)返回对象。
默认情况下,复制构造函数操作的对象是类中每一个成员变量,简单数据类型(int、char或指针 )的变量,直接赋值就好,对于本身又是类对象的数据成员,它本身的复制构造函数又会作用于其每一个数据成员。
3,operator=
当=作用于两个已经构造的函数时,调用复制赋值运算符operator=。它的默认情况跟复制构造函数一样,也是作用于每一个数据成员。
4,浅拷贝( shallow copy),深拷贝(deep copy)
一般情况下,类的数据成员是由基本数据类型(int、double、vector<int>、string甚至是vector<string>)构成时三大函数的默认值都是适用的,但当类中存在数据成员是指针,而且这个指针指向一个动态分配地址的对象时,,默认的析构函数不会对指针进行任何操作(一个很好的理由就是释放这个指针就必须删除自身)。而且,复制构造函数和operator=都不复制指针指向的对象,而是简单的复制指针的值,这样的话,就会导致所有的指针都指向同一个对象,这被称为浅拷贝,而我们一般期望的是深拷贝。
下面代码展示了一个浅拷贝的示例:
#include <iostream>
using namespace std;
class Test
{
public:
explicit Test(int initValue = 0)
{
storedValue = new int (initValue);
}
int read() const
{
return *storedValue;
}
void write(int x)
{
*storedValue = x;
}
private:
int * storedValue; //数据成员是指针,默认值不能用
};
int main()
{
Test a(2);
Test b = a;
Test c;
c = b;
a.write(3);
cout << a.read() << endl << b.read() << endl << c.read() << endl;
return 0;
}
上述代码逻辑上只有a为3,但实际上输出了3个3,问题就在于默认的operator=和复制构造函数都只是复制指针storedValue,导致三个对象的指针storedValue均指向了同一个int型变量,这些复制都是浅复制。下述代码通过三大函数来解决这个问题。
#include <iostream>
using namespace std;
class Test
{
public:
explicit Test( int initValue = 0 );
Test( const Test & rhs ); //复制拷贝函数
~Test(); //析构函数
const Test & operator = ( const Test & rhs ); //operator=
int read();
void write( int x );
private:
int * storedValue; //数据成员是指针,默认值不能用
};
Test::Test(int initValue)
{
storedValue = new int( initValue );
}
Test::Test( const Test & rhs)
{
storedValue = new int( *rhs.storedValue );
}
Test::~Test()
{
delete storedValue;
}
const Test & Test::operator=( const Test & rhs)
{
if( this != &rhs) //判断 = 两侧是否是同一对象
*storedValue = *rhs.storedValue;
return *this;
}
int Test::read()
{
return *storedValue;
}
void Test::write(int x)
{
*storedValue = x;
}
int main()
{
Test a(2);
Test b = a;
Test c;
c = b;
a.write(3);
cout << a.read() << endl << b.read() << endl << c.read() << endl;
return 0;
}
上面的输出结果是3,2,2。三个对象均有独立的变量空间,而不是上面的三个指针指向同一个变量。所以,当一个类的数据成员为指针并且深拷贝很重要的时候,一般的做法就是自己实现三大函数,