自定义完美的智能指针 AutoPtr

STL 的 auto_ptr 已经弃用, 为了解决手动释放指针内存的问题, 自定义一个好使的 AutoPtr 包装类, 不足的一点是所有被包装的类必须继承自 BaseType, 因为 BaseType 中有当前被包装类的对象的引用计数。

每有一个 AutoPtr 对象的 mPtr 指针指向被包装类的对象, 那么该被包装类的对象的引用计数就 + 1;每有一个 AutoPtr 对象析构, 那么 AutoPtr 对象的 mPtr 指针指向被包装类的对象的引用计数就 -1, 当执行AutoPtr 对象的析构函数发现 mPtr 指针指向的被包装类的对象的引用计数变为 0 时, 就 delete 掉 mPtr 指针, 释放被包装类的对象的内存, 从而解决了手动 delete 释放内存的问题。

代码如下 :

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>

using namespace std;

//自己编写智能指针代理类 AutoPtr
//mTotalAutoPtrCount 目前只能针对一种 Tp 类型
static int mTotalAutoPtrCount = 0; //使用 mCurCount 判断当前是 auto_p1 还是 auto_p2

template<typename Type>
class AutoPtr;

class BaseType{
public:
    //AutoPtr * autoPtr;
    int curRefCount = 0; //当前对象的 AutoPtr 引用计数

    BaseType(){

    }
};

//如何让 Type 继承自 BaseType ?

template<typename Type>
class AutoPtr {
private:
    //外部的数据
    BaseType* mPtr = nullptr;
    int curCount = 0;

public:
    explicit AutoPtr() throw() {
        processCurCount("AutoPtr() Constructor");
    }

    explicit AutoPtr(BaseType *_p) throw(): mPtr(_p) {
        ((*_p).curRefCount) ++; //AutoPtr 对象引用 BaseType 对象
        processCurCount("AutoPtr(BaseType *_p) Constructor");
    }

    //拷贝构造函数        //注意 : 这么写不好使, 必须是 const AutoPtr<Type>& autoPtr 拷贝构造函数才会执行 !
//    AutoPtr(const AutoPtr<BaseType>& autoPtr): mPtr(autoPtr.mPtr){
//        cout << "*************** Auto CopyConstructor *******************" << endl;
//        (autoPtr.value() -> curRefCount) ++;
//        processCurCount("AutoPtr CopyConstructor");
//    }

    //拷贝构造函数
    AutoPtr(const AutoPtr<Type>& autoPtr): mPtr(autoPtr.mPtr){
        cout << "*************** Auto CopyConstructor *******************" << endl;
        (autoPtr.value() -> curRefCount) ++;
        processCurCount("AutoPtr CopyConstructor");
    }

//    ~AutoPtr() {
//        count--;
//       if(count < 1){
//           delete _M_ptr;
//       }
//    }

    ~AutoPtr() {
        cout << "-------------- ~AutoPtr() Pre ----------"<< endl;
        mTotalAutoPtrCount --;
        cout << "AutoPtr deConstructor, auto_p" << curCount
                << " mTotalAutoPtrCount = " << mTotalAutoPtrCount << endl << endl;
        processMPtr("AutoPtr deConstructor");
        cout << "-------------- ~AutoPtr() Back ----------"<< endl;
    }

    //如果要进行 * 号操作, 那么直接返回里面的 _M_ptr
    BaseType* operator*() {
        return mPtr;
    }

    AutoPtr* operator=(AutoPtr& tp) {
        cout << "operator=(AutoPtr& tp)" << endl;
        (tp.mPtr) -> curRefCount ++;
        processMPtr("AutoPtr operator=(AutoPtr& tp)");
        mPtr = tp.mPtr;
        return this;
    }

    AutoPtr* operator=(BaseType& tp) {
        cout << "operator=(BaseType& tp)" << endl;
        tp.curRefCount ++;
        processMPtr("AutoPtr operator=(BaseType& tp)");
        mPtr = &tp;
        return this;
    }

    Type * value() const {
        if(mPtr == nullptr){
            return nullptr;
        }
        return reinterpret_cast<Type*>(mPtr);; //(Type*)(mPtr);
    }

private:
    void processCurCount(const string& preFix){
        printf("processCurCount Pre, preFix = %s, mTotalAutoPtrCount = %d \n", preFix.c_str(), mTotalAutoPtrCount);
        mTotalAutoPtrCount ++;
        for(int i = 0; i < 10; ++i){
            if(mTotalAutoPtrCount == i){
                curCount = mTotalAutoPtrCount;
            }
        }
        printf("processCurCount Back, preFix = %s, auto_p%d \n", preFix.c_str(), curCount);
    }

    void processMPtr(const string& preFix){
        printf("processMPtr(const string& preFix) mPtr = %x \n", mPtr);
        if(mPtr != nullptr){
            (*mPtr).curRefCount --;
            printf("processMPtr(const string& preFix)  (*mPtr).curRefCount = %d \n", (*mPtr).curRefCount);

            if((*mPtr).curRefCount == 0){ //不用 < 1, 防止多线程出毛病
                printf("%s auto_p%d.curRefCount <= 1, delete auto_p%d mPtr= %x \n", preFix.c_str(), curCount, curCount, mPtr);
                delete mPtr;
                mPtr = nullptr;
            }
        }
    }
};

class Test : public BaseType{
public:

    //Test* test;           //为了让 AutoPtr 来管理指针, 就不要使用 Test* 了
    AutoPtr<Test>* mTest;   //测试循环引用

    //类的非静态成员变量, 在类的实例化过程中(构造对象)才在栈区或者堆区为其分配内存
    //因为 AutoPtr<Test> 中存储的 Test* 指向的对象必定是在堆区中创建的, 所以 Test 类中的 mTestRef 即使不使用指针也不会在栈区里创建
    //顶多调用一次 AutoPtr 的拷贝构造函数
    //AutoPtr<Test> mTestRef;

    string content;

    Test() {
        cout << "Test Constructor" << endl;
    }

    Test(string content) : content(content) {
        cout << "Test Constructor, content = " << content << endl;
    }

    Test(Test &t) {
        cout << "Test CopyConstructor" << endl;
    }

    ~Test() {
        cout << "Test DeConstructor, this addr = " << this << endl;
    }
};

//测试一次赋值
void test_auto_ptr1_simple_assign(){
    AutoPtr<Test> auto_p1(new Test("Test1"));
    AutoPtr<Test> auto_p2(new Test("Test2"));
    auto_p1 = auto_p2;

    cout << "auto_p1 content = " << auto_p1.value()->content << endl;
}

/**
    Test Constructor
    AutoPtr Constructor(), auto_p1
    Test Constructor
    AutoPtr Constructor(), auto_p2
    AutoPtr operator=(AutoPtr& tp)  auto_p1.curRefCount <= 1, delete auto_p1 mPtr=0xe65eb0
    AutoPtr deConstructor, auto_p2 mTotalAutoPtrCount = 1
    AutoPtr deConstructor, auto_p1 mTotalAutoPtrCount = 0
    AutoPtr deConstructor auto_p1.curRefCount <= 1, delete auto_p1 mPtr=0xe65ed0

    Process finished with exit code 0
 */

//测试多次赋值
void test_auto_ptr2_multi_assign(){
    AutoPtr<Test> auto_p1(new Test());
    AutoPtr<Test> auto_p2(new Test());
    AutoPtr<Test> auto_p3(new Test());
    auto_p1 = auto_p2;
    auto_p3 = auto_p2;
    auto_p1 = auto_p3;
}

/**
    Test Constructor
    AutoPtr Constructor(), auto_p1
    Test Constructor
    AutoPtr Constructor(), auto_p2
    Test Constructor
    AutoPtr Constructor(), auto_p3
    AutoPtr operator=(AutoPtr& tp)  auto_p1.curRefCount <= 1, delete auto_p1 mPtr=0x1015eb0
    AutoPtr operator=(AutoPtr& tp)  auto_p3.curRefCount <= 1, delete auto_p3 mPtr=0x1015ef0
    AutoPtr deConstructor, auto_p3 mTotalAutoPtrCount = 2
    AutoPtr deConstructor, auto_p2 mTotalAutoPtrCount = 1
    AutoPtr deConstructor, auto_p1 mTotalAutoPtrCount = 0
    AutoPtr deConstructor auto_p1.curRefCount <= 1, delete auto_p1 mPtr=0x1015ed0

    Process finished with exit code 0
 */

//测试循环引用 (没毛病)
void test_auto_ptr3_cycle_assign(){
    AutoPtr<Test> auto_p1(new Test("Test1"));
    AutoPtr<Test> auto_p2(new Test("Test2"));
    auto_p1.value() -> mTest = &auto_p2;
    auto_p2.value() -> mTest = &auto_p1;
    cout << "auto_p1 content = " << auto_p1.value()->content << endl;
    cout << "auto_p2 content = " << auto_p2.value()->content << endl;
}

/**
    AutoPtr() Constructor auto_p1
    AutoPtr(BaseType *_p) Constructor auto_p2
    AutoPtr() Constructor auto_p3
    AutoPtr(BaseType *_p) Constructor auto_p4
    auto_p1 content = Test1
    auto_p2 content = Test2
    AutoPtr deConstructor, auto_p4 mTotalAutoPtrCount = 3
    AutoPtr deConstructor auto_p4.curRefCount <= 1, delete auto_p4 mPtr=0x1025f00
    AutoPtr deConstructor, auto_p2 mTotalAutoPtrCount = 2
    AutoPtr deConstructor auto_p2.curRefCount <= 1, delete auto_p2 mPtr=0x1025eb0
    
    Process finished with exit code 0
 */

//测试返回值, AutoPtr 对象从函数返回后, 函数的作用域消失, 函数内部的 AutoPtr 对象析构会影响返回的 AutoPtr 对象吗? 不会
AutoPtr<Test> return_auto_ptr(){
    AutoPtr<Test> auto_p1(new Test("Test1"));
    AutoPtr<Test> auto_p2;
    cout << "auto_p2 = auto_p1 Pre" << endl;
    auto_p2 = auto_p1;
    cout << "auto_p2 = auto_p1 End" << endl;
    return auto_p2;
}

void test_auto_ptr4_return(){
    AutoPtr<Test> autoPtr = return_auto_ptr(); //这里执行拷贝构造函数
    cout << "test_auto_ptr4_return(), autoPtr content = " << autoPtr.value()->content << endl;
}

/**
    AutoPtr() Constructor auto_p1
    AutoPtr(BaseType *_p) Constructor auto_p2
    AutoPtr() Constructor auto_p3
    auto_p2 = auto_p1 Pre
    auto_p2 = auto_p1 End
    AutoPtr deConstructor, auto_p2 mTotalAutoPtrCount = 2
    test_auto_ptr4_return(), autoPtr content = Test1
    AutoPtr deConstructor, auto_p3 mTotalAutoPtrCount = 1
    AutoPtr deConstructor auto_p3.curRefCount <= 1, delete auto_p3 mPtr=0xfd5eb0

    Process finished with exit code 0
*/


//测试初始化 (好使)
void test_auto_ptr_init(){
    AutoPtr<Test> tempPtr = AutoPtr<Test>(new Test("test_auto_ptr_init"));
    cout << "tempPtr2 = tempPtr Pre, tempPtr value refCount = " << tempPtr.value()->curRefCount << endl;
    AutoPtr<Test> tempPtr2 = tempPtr;
    //这里不会执行 operator = 函数, 但是会执行拷贝构造函数
    cout << "tempPtr2 = tempPtr Back, tempPtr value refCount = " << tempPtr.value()->curRefCount << endl;
}

/**
    Test Constructor, content = test_auto_ptr_init
    processCurCount Pre, preFix = AutoPtr(BaseType *_p) Constructor, mTotalAutoPtrCount = 0
    processCurCount Back, preFix = AutoPtr(BaseType *_p) Constructor, auto_p1
    tempPtr2 = tempPtr Pre, tempPtr value refCount = 1
    *************** Auto CopyConstructor *******************
    processCurCount Pre, preFix = AutoPtr CopyConstructor, mTotalAutoPtrCount = 1
    processCurCount Back, preFix = AutoPtr CopyConstructor, auto_p2
    tempPtr2 = tempPtr Back, tempPtr value refCount = 2
    -------------- ~AutoPtr() Pre ----------
    AutoPtr deConstructor, auto_p2 mTotalAutoPtrCount = 1

    processMPtr(const string& preFix) mPtr = 6a5ed0
    processMPtr(const string& preFix)  (*mPtr).curRefCount = 1
    -------------- ~AutoPtr() Back ----------
    -------------- ~AutoPtr() Pre ----------
    AutoPtr deConstructor, auto_p1 mTotalAutoPtrCount = 0

    processMPtr(const string& preFix) mPtr = 6a5ed0
    processMPtr(const string& preFix)  (*mPtr).curRefCount = 0
    AutoPtr deConstructor auto_p1.curRefCount <= 1, delete auto_p1 mPtr= 6a5ed0
    -------------- ~AutoPtr() Back ----------
    请按任意键继续. . .

    Process finished with exit code 0
 */

//测试多线程
AutoPtr<Test> globalPtr = AutoPtr<Test>(new Test("thread AutoPtr"));
mutex mut;

void thread_func(int num) {
    //加锁解决当前同步写的问题
    mut.lock();

    std::cout << "------ thread_func Pre ------" << endl;

    std::cout << "thread_func num = " << num << endl; //cout 不是线程安全的

    AutoPtr<Test> innerPtr = globalPtr; //内部有风险,取数据无所谓,如果是写数据

    std::cout << "globalPtr content = " << globalPtr.value() -> content << std::endl;

    std::cout << "------ thread_func Back ------" << endl;

    mut.unlock();
}

//测试多线程 (好使)
void test_auto_ptr5_multi_thread(){
    std::thread t1(thread_func, 1);
    std::thread t2(thread_func, 2);
    std::thread t3(thread_func, 3);
    std::thread t4(thread_func, 4);
    std::thread t5(thread_func, 5);
    t1.join();
    t2.join(); //如果只有 t1.join(), 那么 exit code 就是 3 而不是 0
    t3.join();
    t4.join();
    t5.join();
}

/**
    Test Constructor, content = thread AutoPtr
    processCurCount Pre, preFix = AutoPtr(BaseType *_p) Constructor, mTotalAutoPtrCount = 0
    processCurCount Back, preFix = AutoPtr(BaseType *_p) Constructor, auto_p1
    ------ thread_func Pre ------
    thread_func num = 1
    *************** Auto CopyConstructor *******************
    processCurCount Pre, preFix = AutoPtr CopyConstructor, mTotalAutoPtrCount = 1
    processCurCount Back, preFix = AutoPtr CopyConstructor, auto_p2
    globalPtr content = thread AutoPtr
    ------ thread_func Back ------
    -------------- ~AutoPtr() Pre ----------
    AutoPtr deConstructor, auto_p2 mTotalAutoPtrCount = 1

    processMPtr(const string& preFix) mPtr = fb5eb0
    processMPtr(const string& preFix)  (*mPtr).curRefCount = 1
    -------------- ~AutoPtr() Back ----------
    ------ thread_func Pre ------
    thread_func num = 2
    *************** Auto CopyConstructor *******************
    processCurCount Pre, preFix = AutoPtr CopyConstructor, mTotalAutoPtrCount = 1
    processCurCount Back, preFix = AutoPtr CopyConstructor, auto_p2
    globalPtr content = thread AutoPtr
    ------ thread_func Back ------
    -------------- ~AutoPtr() Pre ----------
    AutoPtr deConstructor, auto_p2 mTotalAutoPtrCount = 1------ thread_func Pre ------
    thread_func num = 3


    processMPtr(const string& preFix) mPtr = fb5eb0
    processMPtr(const string& preFix)  (*mPtr).curRefCount = 1
    -------------- ~AutoPtr() Back ----------
    *************** Auto CopyConstructor *******************
    processCurCount Pre, preFix = AutoPtr CopyConstructor, mTotalAutoPtrCount = 1
    processCurCount Back, preFix = AutoPtr CopyConstructor, auto_p2
    globalPtr content = thread AutoPtr
    ------ thread_func Back ------
    -------------- ~AutoPtr() Pre ----------
    AutoPtr deConstructor, auto_p2 mTotalAutoPtrCount = 1

    processMPtr(const string& preFix) mPtr = fb5eb0
    processMPtr(const string& preFix)  (*mPtr).curRefCount = 1
    -------------- ~AutoPtr() Back ----------
    请按任意键继续. . .

    -------------- ~AutoPtr() Pre ----------
    AutoPtr deConstructor, auto_p1 mTotalAutoPtrCount = 0

    processMPtr(const string& preFix) mPtr = fb5eb0
    processMPtr(const string& preFix)  (*mPtr).curRefCount = 0
    AutoPtr deConstructor auto_p1.curRefCount <= 1, delete auto_p1 mPtr= fb5eb0
    -------------- ~AutoPtr() Back ----------

    Process finished with exit code 0
*/

int main() {
//    test_auto_ptr1_simple_assign();
//    test_auto_ptr2_multi_assign();
//    test_auto_ptr3_cycle_assign();
//    test_auto_ptr4_return();
    test_auto_ptr5_multi_thread();

//    test_auto_ptr_init();

    system("pause");
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,603评论 5 465
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,422评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,478评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,116评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,037评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,832评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,075评论 3 389
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,685评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,976评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,028评论 2 315
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,830评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,584评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,101评论 3 301
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,229评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,521评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,121评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,314评论 2 339