C++:Copy & Reference Count

浅拷贝、深拷贝

一般,咱们会按以下方式书写拷贝构造函数:函数

class LiF {
public:
    LiF(int _lif = 0) : lif(_lif) {} // 默认构造函数
    LiF(const LiF& l) : lif(l.lif) {} // 拷贝构造函数
private:
    int lif;
};

这是正确的。可是,若是数据成员包含指针类型的话,这种写法就很危险了。this

class LiF {
public:
    LiF() { lif = new int(0); } // 为lif动态分配内存
    LiF(const LiF& l) : lif(l.lif) {} // 拷贝构造函数
    ~LiF() { // 析构函数
        delete lif; // 释放分配给lif的资源
        lif = nullptr; // 置空
    }
private:
    int* lif;
};

LiF l1;
LiF l2(l1); // 程序结束析构l2时,程序将崩溃

在拷贝l1生成l2的时候,咱们的构造函数只是简单的把l1lif成员的值赋予了l2lif,也就是说,它们保存的都是l1构造时分配的地址,当二者之中的某个对象被销毁时,构造函数正常执行,资源被释放,但以后若是另外一个对象也被析构,lif的资源就会被重复释放,lif也就变成野指针。这种拷贝方式也称为浅拷贝,即只拷贝空间,不拷贝资源。指针

为了防止指针类型的数据成员出现野指针错误,对应地便有了深拷贝操做,即在拷贝对象内容的同时为拷贝的内容分配新的资源。code

class LiF {
public:
    LiF() { lif = new int(0); } // 为lif动态分配内存
    LiF(const LiF& l) : lif(new int(*l.lif)) {} // 深拷贝构造函数
    ~LiF() { // 析构函数
        delete lif; // 释放分配给lif的资源
        lif = nullptr; // 置空
    }
private:
    int* lif;
};

LiF l1;
LiF l2(l1);

注意到,在上面的拷贝构造函数中,咱们为新对象的lif成员分配了一块新的内存,即完成了深拷贝。对象

类的行为

从上面的例子能够获得两种抽象的类行为:行为像值行为像指针内存

行为像值的类

即类提供的构造函数是深拷贝,类的每一个对象都有本身的一份拷贝。对于这样的类,它显然须要:一个深拷贝构造函数、一个深拷贝赋值运算符重载、一个能够释放成员占用的资源的析构函数。资源

class LiF {
public:
    LiF(const int& _lif = 0): lif(new int(_lif)) {}
    LiF(const LiF& l) : lif(new int(*l.lif)) {}
    LiF& operator= (const LiF& l) {
        lif = new int(*l.lif);
        return *this;
    }
    ~LiF() {
        delete lif;
        lif = nullptr;
    }
private:
    int* lif;
};

行为像指针的类

即类提供的是浅拷贝,但因为可能有多个对象成员值相同一段内存,因此咱们不能在析构时简单地释放资源。为了解决浅拷贝带来的野指针问题,须要引入一种技术——引用计数(reference count)。这也是C++11的智能指针shared_ptr的实现。class

引用计数:构造函数

  • 在每一个构造函数初始化对象时,额外建立一个引用计数并置为1,用以记录有多少对象正在共享资源。
  • 在执行拷贝操做时进行浅拷贝,同时拷贝计数器,并递增计数器,指出共享的对象增长了一个。
  • 在进行拷贝赋值时比较特殊但也很容易理解:须要递增右侧对象的计数器并递减左侧对象的计数器。若左侧对象引用计数归零,则释放资源。
  • 在析构对象时,并不直接释放共享的资源,而是递减计数器,直至计数器归零才释放资源。
class LiF {
public:
    LiF(const int& _lif = 0): lif(new int(_lif)), referenceCount(new unsigned(1)) {}
    
    LiF(const LiF& l) : 
        lif(l.lif), referenceCount(l.referenceCount) {
        ++ *referenceCount;
    }
    
    LiF& operator= (const LiF& l) {
        ++ *l.referenceCount;
        if (-- *referenceCount == 0) {
            delete lif;
            delete referenceCount;
        }
        lif = l.lif;
        referenceCount = l.referenceCount;
        return *this;
    }
    
    ~LiF() {
        if (-- *referenceCount == 0) {
            delete lif;
            delete referenceCount;
        }
    }
private:
    int *lif;
    unsigned *referenceCount;
};
相关文章
相关标签/搜索