c++编程提倡使用标准库,一个原因是标准库大胆减少对指针的使用。但是许多程序是离不开指针的。包含指针的类需要特别注意复制控制,原因是复制指针时只复制指针中的地址,而不复制指针所指向的对象。这样当把一个对象复制给另一个对象后,当改变一个对象后,另一个对象也会收到牵连。另外一个对象释放掉后,其指针已经被释放掉。而另一个对象还不知道,其实该对象中的指针已经成为悬垂指针。这样再操作就会出现错误。
1. 定义智能指针类
原理:定义一个计数的类,所有复制的都是指向这一个类,每复制一次,该类加1一次;每析构一次,该类减1一次。当次数为0时,释放掉动态申请的空间。
图例:
1)定义一个对象
2)复制一个对象
#include <iostream> #include <string> using namespace std; class HasPtr;
//定义计数类 class U_Ptr { //没有访问标号,默认为private,保证类的隐私,但是友员类可以随意访问(包括private) friend class HasPtr; U_Ptr(int *p) : ip(p), use(1) {} ~U_Ptr() { cout << "hello" << use << endl; delete ip; } int *ip; size_t use; //次数 }; class HasPtr { public: HasPtr(int *p, int v) : ptr(new U_Ptr(p)), val(v) {} HasPtr(const HasPtr &orig) : ptr(orig.ptr), val(orig.val) { ++ptr->use; } HasPtr& operator=(const HasPtr &orig); ~HasPtr() { cout << "hahha::" << ptr->use << endl; if(--ptr->use == 0) delete ptr; } private: U_Ptr *ptr; int val; }; HasPtr& HasPtr::operator=(const HasPtr &orig) { ++orig.ptr->use; if(--ptr->use == 0) delete ptr; ptr = orig.ptr; val = orig.val; return *this; } int main() { int i = 56, j = 12; int *p = new int(56); HasPtr hasptr(p, j); HasPtr hasptr2(hasptr); }
2. 定义值型类
给指针成员提供语义值。具有值语义的类所定义的对象,其行为很像算术类型的对象:复制值型对象时,会得到一个不同的版本。对副本的改变不会反映在原有对象上。
#include <iostream> #include <string> using namespace std; class HasPtr { public: HasPtr(const int &p, int v) : ptr(new int(p)), val(v) {} HasPtr(const HasPtr &orig) : ptr(new int(*orig.ptr)), val(orig.val) { } //复制值型对象时,会得到一个不同的版本 HasPtr& operator=(const HasPtr &orig); ~HasPtr() { delete ptr; } private: int *ptr; int val; }; HasPtr& HasPtr::operator=(const HasPtr &orig) { *ptr = *orig.ptr; //赋值时,只改变指针指向的值,但是指针是不变的 val = orig.val; return *this; } int main() { int i = 56, j = 12; HasPtr hasptr(i, j); HasPtr hasptr2(hasptr); hasptr2 = hasptr; }