作者:gzshun. 原创作品,转载请标明出处!
来源:http://blog.youkuaiyun.com/gzshun
在《C++ Primer中文版第4版》中,有这么一个小节"管理指针成员"。有点好奇,咱也学学,刚开始有点不理解其目的,经过反复验证,才知道所以然。以前一直有个错误的习惯,看书从第一章看到了最后一章,有点愚昧。现在已经把那种不好的习惯给改了,我想学哪一章,哪一节都行,学我想学的部分即可,要不书这么多,还这么厚,看到猴年马月,说不定哪天阿基米德找到支点,把地球给撬起来。
在这本书中,有两种方法来管理指针成员:定义智能指针类和值型类
一、智能指针的引子
每个事件的发生,都有一个因果关系,这里出现了智能指针的解决方案,是处理什么情况呢?也就是管理指针。。。
先来个小例子:
指针共享同一对象
1.一个类中存在一个指针成员;
2.声明A对象,再声明B对象,最后用A对象作为B对象的初始值;(复制构造函数)
3.释放A对象,释放了指针所指的空间;
执行结果:
A与B中的指针指向同一个int对象
2
2
释放指针所指向的空间
0
执行结果:
默认构造函数
创建了A对象: 使用计数 = 1
复制构造函数
创建了B对象: 使用计数 = 2
默认构造函数
创建了C对象: 使用计数 = 1
赋值操作符释放ptr指针
使用计数 = 3
调用析构函数
调用析构函数
调用析构函数
析构函数释放ptr指针
执行结果:
调用构造函数
*ptr = 2
val = 2
调用复制构造函数
*ptr = 2
val = 2
调用构造函数
调用赋值操作符
*ptr = 2
val = 2
调用析构函数
调用析构函数
调用析构函数
来源:http://blog.youkuaiyun.com/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
调用析构函数
调用析构函数
调用析构函数