析构函数、复制构造函数,operator=以及深拷贝浅拷贝问题

        在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。三个对象均有独立的变量空间,而不是上面的三个指针指向同一个变量。所以,当一个类的数据成员为指针并且深拷贝很重要的时候,一般的做法就是自己实现三大函数,


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值