作者: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
调用析构函数
调用析构函数
调用析构函数