C++类中经常含有指针,但是如何处理这个问题,本文针对<<C++ Primer>>和<<C++沉思录>>的方法进行了一点小结,本文只是浅尝辄止,深了自己都不懂了,且本文的例子漏洞百出,本文旨在总结这几种处理方法。
类中含有指针带来的一些问题
设计具有指针成员的类时,类设计者必须首先需要决定的是该指针应提供什么行为。将一个指针复制到另一个指针时,两个指针指向同一个对象。当两个指针指向同一对象时,可以实用任一指针改变对象。类似地,很可能一个指针了一个对象时,另一个指针的用户还没有发现。
通过不同的复制控制策略,可以为指针成员实现不同的行为。通常有以下五种策略(个人之见):
(1)指针成员采取常规指针行为。这样的类具有所有指针缺限,但是不需要用户编写复制控制程序;
(2)类采取值行为。复制的时候,另外分配空间,但是值与被复制对象一致;
(3)采取拥有权转移。即被复制对象释放指针的引用。标准库的auto_ptr;
(4)实现所谓的“智能指针”。共享对象,但是能防止“悬垂指针”;
(5)“写时复制”技术。在修改的时候才去重新分配空间。
策略一:常规指针行为
这种方式的赋值操作符/复制构造函数采用系统默认的,两个复制的对象共用相同的空间,这会带来一些析构时遇到的问题。
#include<iostream> using namespace std; class HasPtr{ public: HasPtr(int *p):ptr(p){} int* getPtr()const {return ptr;} void setPtr(int *p) {ptr = p;} int& operator*()//返回引用可以对其赋值 {return *ptr;} const int& operator*()const {return *ptr;} int* operator->() {return ptr;} const int* operator->()const {return ptr;} ~ HasPtr(){delete ptr;} private: int *ptr; }; int main() { HasPtr p1(new int(5)); HasPtr p2(p1); *p1 = 12; cout<<*p1<<endl;//5 cout<<*(p2.operator->())<<endl;//12 system("pause"); return 0; }
策略二:“值复制”行为
这种方式在复制的时候,采用“值复制”的方式。即复制的时候不复制空间指针,而是重新分配空间,但是其“值”与被复制的对象“值”一样。一般笔试时,string可以采用这种类型。
/*copyright@ CNV && lsj*/ #include<iostream> using namespace std; class HasPtr{ public: HasPtr(int *p):ptr(p){} HasPtr(const HasPtr& other):ptr(new int(*other)) {} HasPtr& operator=(const HasPtr& other) { *ptr = *other; return *this; } int& operator*()//返回引用可以对其赋值 {return *ptr;} const int& operator*()const {return *ptr;} int* operator->() {return ptr;} const int* operator->()const {return ptr;} ~ HasPtr(){delete ptr;} private: int *ptr; }; int main() { HasPtr p1(new int(5)); HasPtr p2(p1); *p2 = 13; cout<<*p1<<endl;//5 cout<<*p2<<endl;//13 system("pause"); return 0; }
策略三:拥有权转移行为
被复制的对象释放对指针的拥有权,由左边对象接管指针的拥有权。标准库auto_ptr采用了这种策略。
/*copyright@ CNV && lsj*/ #include<iostream> using namespace std; class HasPtr{ public: HasPtr(int *p):ptr(p){} HasPtr(const HasPtr& other) { ptr = other.getPtr(); other.setPtr(NULL); } HasPtr& operator=(const HasPtr&other) { if(&other!=this){ ptr = other.getPtr(); other.setPtr(NULL); } return *this; } int getPtr()const {return ptr;} void setPtr(int *p) {ptr = p;} ~ HasPtr() { if(ptr)delete ptr; } private: int *ptr; };
策略四:智能指针行为
这种策略虽然还是采用多个对象共享同一份地址空间,但是通过给类添加计数器,能够有效的防止“悬垂指针”。实现智能指针有两种方案(C++ Primer):计数伙伴类和句柄类。
方案1:设计伙伴计数类。
/*copyright@ CNV && lsj*/ #include<iostream> using namespace std; class HasPtr; class Uptr{//计数伙伴类 friend class HasPtr; int *ip;//本来应该包含这个指针 size_t use; Uptr(int *p):ip(p),use(1) {} ~Uptr(){delete ip;} }; class HasPtr{ public: HasPtr(int *p):ptr(new Uptr(p)) {} HasPtr(const HasPtr& other){ ptr = other.ptr; ++ptr->use; } HasPtr operator=(const HasPtr&other){ ++other.ptr->use; if(--ptr->use==0) delete ptr; ptr = other.ptr; return *this; } int& operator *() { return *(ptr->ip);} ~ HasPtr(){ If(--ptr->use==0) delete ptr; //将调用伙伴类的析构函数 } private: Uptr *ptr;//将int类型的指针封装到伙伴类中 }; int main() { HasPtr p1(new int(3)); HasPtr p2 = p1; *p2 = 5; cout<<*p1<<endl;//5 system("pause"); return 0; }
方案2:计数器封装到当前类中,但是计数器use必须采用指针,这样各个指向同一个指针的对象共享同一份空间,都可以去改这个计数器
/*copyright@ CNV && lsj*/ #include<iostream> using namespace std; class HasPtr{ public: HasPtr(int*p):ptr(p),use(new int(1)) {} HasPtr(constHasPtr& other) { ptr= other.ptr; use= other.use; ++*use; } HasPtr&operator=(const HasPtr& other){ ++*other.use; if(--*use==0){ deleteptr; deleteuse; } ptr= other.ptr; use= other.use; return*this; } int& operator *() { return *ptr; } ~HasPtr() { if(--*use==0) { deleteptr; deleteuse; } } private: int *ptr;//所包含元素的指针 //一定要用指针,这样大家共用一份空间,都可以改写 int *use; };
策略五:写时复制计数
有时候用户需要对象与对象之间的空间独立,但是每次都复制开销又很大。采用的策略是先不急着复制空间,拖到当另一个类要改写共享空间的时候,复制出来一份改写。
/*copyright@ CNV && lsj*/ #include<iostream> using namespace std; class HasPtr{ public: HasPtr(int*p):ptr(p),use(new int(1)) {} HasPtr(constHasPtr& other){ ptr= other.ptr; use= other.use; ++*use; } HasPtr& operator=(const HasPtr& other){ ++*other.use; if(--*use==0){ delete ptr; delete use; } ptr= other.ptr; use= other.use; return *this; } int& operator *() { return*ptr; } ~HasPtr() { if(--*use==0) { delete ptr; delete use; } } //这个接口要改写共享空间,此时复制一份改写 void setValue(intval) { //判断是不是只有当前对象持有共享空间 //如果是,则不用复制,直接改 if(*use!=1){ --*use; use= new int(1); ptr= new int(val); } *ptr= val; } private: int *ptr;//所包含元素的指针 //一定要用指针,这样大家共用一份空间,都可以改写 int *use; }; int main() { HasPtr p1(newint(3)); HasPtr p2= p1; p2.setValue(5); cout<<*p1<<endl;//5 system("pause"); return 0; }