`
mmdev
  • 浏览: 12916346 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

C++学习-智能指针(8)

 
阅读更多
作者:gzshun. 原创作品,转载请标明出处!
来源:http://blog.csdn.net/gzshun


在《C++ Primer中文版第4版》中,有这么一个小节"管理指针成员"。有点好奇,咱也学学,刚开始有点不理解其目的,经过反复验证,才知道所以然。以前一直有个错误的习惯,看书从第一章看到了最后一章,有点愚昧。现在已经把那种不好的习惯给改了,我想学哪一章,哪一节都行,学我想学的部分即可,要不书这么多,还这么厚,看到猴年马月,说不定哪天阿基米德找到支点,把地球给撬起来。
在这本书中,有两种方法来管理指针成员:定义智能指针类和值型类

一、智能指针的引子

每个事件的发生,都有一个因果关系,这里出现了智能指针的解决方案,是处理什么情况呢?也就是管理指针。。。
先来个小例子:
指针共享同一对象
1.一个类中存在一个指针成员;
2.声明A对象,再声明B对象,最后用A对象作为B对象的初始值;(复制构造函数)
3.释放A对象,释放了指针所指的空间;

4.B对象继续访问类中的指针成员???问题来了

#include <iostream>

using namespace std;

class CObj
{
public:
    CObj(int *p) : mPtr(p)
    {
    }
    ~CObj()
    {
        delete mPtr;
    }
    void ShowData() const
    {
        cout << *mPtr << endl;
    }
private:
    int *mPtr;
};

int main()
{
    int *p = new int(2);

    CObj *A = new CObj(p);
    CObj B(*A);

    cout << "A与B中的指针指向同一个int对象" << endl;
    A->ShowData();
    B.ShowData();

    delete A; //删除指针所指向的内容
    cout << "释放指针所指向的空间" << endl;

    B.ShowData(); //错误,访问了已经被删除的内容
    return 0;
}

执行结果:

A与B中的指针指向同一个int对象
2
2
释放指针所指向的空间
0

这里我看了结果,惊叹了,GCC编译器也优化得太多了,访问被删除的空间都不提示错误,无语。我试过了C语言与C++错误的例子,GCC与G++编译都是成功的,后来网上一搜,有人说:老版本的GCC编译器会提示错误,最新版本的GCC做了优化,数组越界与访问悬垂指针都不报错。
但"B.ShowData();"语句是错误的,必须要避免。


二、定义智能指针类--引入使用计数
虽然叫智能指针类,但也智能不到哪里去。该智能指针负责删除共享对象,用户同样将动态分配的一个对象地址传给了类中的指针。但绝对不能释放指针所指的空间。(不然也不叫智能指针了)
以下是《C++ Primer中文版第4版》书中的一个例子,被我修改了,增加了一些打印信息,更直观的了解智能指针类的使用与目的。

#include <iostream>

using namespace std;

class U_Ptr
{
    friend class HasPtr;
    int *ip;
    size_t use;
    U_Ptr(int *p) : ip(p), use(1)
    {
    }
    ~U_Ptr()
    {
        delete ip;
    }
};

class HasPtr
{
public:
    HasPtr(int *p, int i) : ptr(new U_Ptr(p)), val(i)
    {
        cout << "默认构造函数" << endl;
    }
    HasPtr(const HasPtr &orig) : ptr(orig.ptr), val(orig.val)
    {
        ++ptr->use;
        cout << "复制构造函数" << endl;
    }
    HasPtr & operator=(const HasPtr &rhs)
    {
        ++rhs.ptr->use;
        if (--ptr->use == 0)
        {
            cout << "赋值操作符释放ptr指针" << endl;
            delete ptr;
        }
        ptr = rhs.ptr;
        val = rhs.val;
        return *this;
    }
    ~HasPtr()
    {
        cout << "调用析构函数" << endl;
        if (--ptr->use == 0)
        {
            delete ptr;
            cout << endl << "析构函数释放ptr指针" << endl;
        }
    }
    void GetUse() const
    {
        cout << "使用计数 = " << ptr->use << endl;
    }
private:
    U_Ptr *ptr;
    int val;
};

int main()
{
    int *p = new int(2);
    HasPtr A(p, *p);
    cout << "创建了A对象: ";
    A.GetUse();
    cout << endl;

    HasPtr B(A);
    cout << "创建了B对象: ";
    B.GetUse();
    cout << endl;

    HasPtr C(p, *p);
    cout << "创建了C对象: ";
    C.GetUse();
    cout << endl;
    C = A;
    C.GetUse();
    cout << endl;
    return 0;
}

执行结果:

默认构造函数
创建了A对象: 使用计数 = 1

复制构造函数
创建了B对象: 使用计数 = 2

默认构造函数
创建了C对象: 使用计数 = 1

赋值操作符释放ptr指针
使用计数 = 3

调用析构函数
调用析构函数
调用析构函数

析构函数释放ptr指针


三、定义值型类
值型类,类中的指针不会共享同一个对象,对副本所做的改变不会反映到原有对象上。复制构造函数不再复制指针,而是分配一个新的int对象,并初始化该对象以保存被复制对象相同的值。

#include <iostream>

using namespace std;

class U_Ptr
{
    friend class HasPtr;
    int *ip;
    size_t use;
    U_Ptr(int *p) : ip(p), use(1)
    {
    }
    ~U_Ptr()
    {
        delete ip;
    }
};

class HasPtr
{
public:
    HasPtr(const int &p, int i) : ptr(new int(p)), val(i)
    {
        cout << "调用构造函数" << endl;
    }
    HasPtr(const HasPtr &orig) : ptr(new int(*orig.ptr)), val(orig.val)
    {
        cout << "调用复制构造函数" << endl;
    }
    HasPtr & operator=(const HasPtr &ths)
    {
        cout << "调用赋值操作符" << endl;
        *ptr = *ths.ptr;
        val = ths.val;
        return *this;
    }
    ~HasPtr()
    {
        cout << "调用析构函数" << endl;
        delete ptr;
    }
    void ShowData() const
    {
        cout << "*ptr = " << *ptr << endl
             << "val = " << val << endl;
    }
private:
    int *ptr;
    int val;
};

int main()
{
    int *p = new int(2);

    HasPtr A(*p, *p);
    A.ShowData();
    cout << endl;

    HasPtr B(A);
    B.ShowData();
    cout << endl;

    HasPtr C(*p, *p);
    C = A;
    C.ShowData();
    cout << endl;
    return 0;
}

执行结果:

调用构造函数
*ptr = 2
val = 2

调用复制构造函数
*ptr = 2
val = 2

调用构造函数
调用赋值操作符
*ptr = 2
val = 2

调用析构函数
调用析构函数
调用析构函数


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics