天天看点

【C++】鱼的一生, 智能指针的实现

作者:海洋饼干叔叔
【C++】鱼的一生, 智能指针的实现

本节将实现一个名为SmartPointer的简化版本的shared_ptr模板类,相关C++代码如下。

知识产权协议

允许以教育/培训为目的向学生或受众进行免费引用,展示或者讲述,无须取得作者同意。

不允许以电子/纸质出版为目的进行摘抄或改编。

//Project - SmartPointer
#include <iostream>
using namespace std;

class Fish {
public:
    string sName;
    Fish(const string& name){
        sName = name;
        cout << "Fish Constructor called: " << sName << endl;
    }

    void sayHello(){
        cout << "Aloha: " << sName << endl;
    }

    ~Fish(){
        cout << "Fish Destructor called:  "  << sName << endl;
    }
};

template <typename T>
class SmartPointer {
private:
    T* ptr;             //原始指针
    int* refCount;      //指向引用计数的指针
    void releaseReference(){   //释放对对象的引用
        if (!ptr)
            return;
        (*refCount)--;
        if (*refCount==0){
            delete ptr; ptr = nullptr;
            delete refCount; refCount = nullptr;
        }
    }

public:
    SmartPointer(T* p=nullptr){
        ptr = p;
        refCount = new int;
        *refCount = 1;
    }

    ~SmartPointer(){
        releaseReference();
    }

    //拷贝构造函数
    SmartPointer(const SmartPointer& r) {
        cout << "Copy constructor of SmartPointer." << endl;
        ptr = r.ptr;
        refCount = r.refCount;
        (*refCount)++;
    }

    //重载=操作符
    SmartPointer& operator=(const SmartPointer& r){
        cout << "operator=(const SmartPointer&)." << endl;
        if (&r == this)
            return *this;
        releaseReference();
        ptr = r.ptr;
        refCount = r.refCount;
        (*refCount)++;
        return *this;
    }

    //重载*操作符
    T& operator*(){
        cout << "operator*() of SmartPointer." << endl;
        if (ptr)
            return *ptr;
        throw exception();
    }

    //重载->操作符
    T* operator->(){
        cout << "operator->() of SmartPointer." << endl;
        if (ptr)
            return ptr;
        throw exception();
    }

    //查询引用计数
    int referenceCount(){
        return *refCount;
    }
};

int main() {
    SmartPointer<Fish> f1(new Fish("Dora"));
    SmartPointer<Fish> f2(new Fish("Tom"));
    auto f3 = f1;      //调用f3的拷贝构造函数,以f1为实参
    f2 = f1;           //调用f2的operator=()函数,以f1为实参,间接导致Tom鱼被释放
    (*f2).sayHello();  //调用f2的operator*()函数
    f2->sayHello();    //调用f2的operator->()函数
    cout << "Refernce count of Dora fish: " << f2.referenceCount() << endl;
    return 0;
}           

上述代码的执行结果为:

Fish Constructor called: Dora
Fish Constructor called: Tom
Copy constructor of SmartPointer.
operator=(const SmartPointer&).
Fish Destructor called:  Tom
operator*() of SmartPointer.
Aloha: Dora
operator->() of SmartPointer.
Aloha: Dora
Refernce count of Dora fish: 3
Fish Destructor called:  Dora           

类似于第7章中介绍的模板函数,SmartPointer被设计为一个模板类,其类型参数T需要在使用该类时于<>内提供。

第22 ~ 26行:SmartPointer对象包含两个私有属性,其中,ptr为指向动态对象的原始指针;refCount则为指向引用计数的指针。如前所述,该引用计数用于记录指向动态对象的存活智能指针对象的个数。

第27 ~ 35行:下述情况之一,releaseReference()成员函数将被调用,以释放该指针对动态对象的引用。

•智能指针对象析构时;

•智能指针准备绑定其它对象前。

第28 ~ 29行:如果智能指针是空指针,直接返回。

第30行:引用计数递减。

第31 ~ 34行:如果引用计数归零,说明该指针所指向的动态对象已经不被需要,将其释放,同时释放引用计数。

第38 ~ 42行:构造函数接受一个动态对象的指针作为参数。函数体内,原始指针得到保存,引用计数被创建并置1。

第44 ~ 46行:析构函数内调用releaseReference()函数释放指针对动态对象的引用。如果该智能指针是指向该动态对象的最后一个存活的智能指针,该函数会释放动态对象。

第49 ~ 54行:拷贝构造函数。该函数通过复制来初始化一个新的智能指针对象。函数体完成了下述工作:

•从源对象复制原始指针及引用计数指针。

• 引用计数递增,因为又产生了一个新的指向该动态对象的智能指针。

第57 ~ 65行:operator=()操作符函数用于对一个已经存在的智能指针对象进行赋值。

第59 ~ 60行:如果智能指针对象自己赋值给自己,什么也不做,直接返回*this做为表达式的值。请读者注意,a = a这种表达式在语法上是合法的,虽然多数编译器会给出警告信息。

第61行:在绑定到新的动态对象之前,先释放对原动态对象的引用。

第62 ~ 64行:绑定至新的动态对象,递增引用计数。

第65行:返回*this作为表达式的值。

第69 ~ 74行:间接操作符*的重载函数。当对一个智能指针使用间接操作符时,该函数会被调用,以获得其所指向的动态对象的引用。

第71 ~ 72行:如果原始指针不为空,返回所指向的动态对象的引用。

第73行:对一个空指针使用间接操作符是非法的,抛出异常(exception)▲。

第77 ~ 82行:指向操作符->的重载函数。当对一个智能指针使用指向操作符时,该函数会被调用。该函数预期返回原始指针。

第79 ~ 80行:如果原始指针非空,返回原始指针。

第81行:对一个空指针应用指向操作符是非法的,抛出异常。

第85 ~ 87行:查询智能指针所指向的动态对象的引用计数,该引用计数表明当前指向该动态对象的智能指针的数量。

接下来,main()函数演示了上述SmartPointer类型的使用方法。

第91 ~ 92行:创建Dora鱼、Tom鱼及相关智能指针。执行结果的第1 ~ 2行对应两条鱼的构造输出。

第93行:f3的拷贝构造函数被执行,以f1为实参。执行结果的第3行对应拷贝构造函数的输出。

第94行:该行代码执行前f2已存在,故f2的operator=()函数被执行,以f1为实参。执行结果的第4行对应operator=()函数的输出。当前,f2是指向Tom鱼的唯一智能指针,f2的被赋值将间接导致Tom鱼被释放,执行结果的第5行对应Tom鱼的析构输出。

第95行:对智能指针f2应用间接操作符以获取f2所指向的动态对象的引用,本行事实上导致了f2的operator*()函数的执行,该函数出的输出见执行结果的第6行。以返回的Dora鱼对象的引用为基础,sayHello()成员函数被执行,执行结果的第7行可以看到来自Dora鱼的问候。

第96行:对智能指针f2应用指向操作符,本行事实上导致了f2的operator->()函数的执行,其输出对应执行结果的第8行。以该函数返回的Dora鱼的原始指针为基础,sayHello()成员函数被执行,执行结果的第9行可再次看到来自Dora鱼的问候。

第97行:目前为止,共有f1、f2和f3共三个智能指针指向Dora鱼,referenceCount()返回Dora鱼的引用计数,其值应为3,见执行结果的第10行。

到了main()函数的结尾,智能指针f1、f2和f3作为栈对象,相继被析构并释放对Dora鱼的引用。其中,最后一个被析构的智能指针发现Dora鱼的引用计数归零,将其释放。执行结果的第11行,对应Dora鱼的析构输出。

本案例节选自作者编写的教材及配套实验指导书。

《C++编程基础及应用》(高等教育出版社,出版过程中)

《Python编程基础及应用》,高等教育出版社

《Python编程基础及应用实验教程》,高等教育出版社

【C++】鱼的一生, 智能指针的实现

高校教师同行如果期望索取样书,教学支持资料,加群,请私信作者,联系时请提供学校及个人姓名为盼,各高校在读学生勿扰为谢。

青少年读者们如果期望系统性地学习Python及C/C++程序设计语言,欢迎尝试下述今日头条(西瓜)免费视频课程。

C/C++从入门到放弃(重庆大学现场版)

Python编程基础及应用(重庆大学现场版)

【C++】鱼的一生, 智能指针的实现

继续阅读