13.5 管理指針成員
復制指針時只復制指針中的地址,而不會復制指針指向的對象。
大多數C++類采用以下三種方法之一管理指針成員:
(1)指針成員采用常規指針型行為。這樣的類具有指針的所有缺陷但無需特殊的復制控制。
(2)類可以實現所謂的"智能指針"行為。指針所指向的對象是共享的,但類能夠防止垂懸指針。
(3)類采取值型行為。指針所指向的對象是唯一的,由每個類對象獨立管理。
1. 一個帶指針成員的簡單類
class HasPtr{
public:
HasPtr(int *p, int i):ptr(p), val(i){}
int *get_ptr() const{ return ptr; }
int get_int() const{ return val; }
void set_ptr(int *p){ ptr=p; }
void set_int(int i){ val=i; }
int get_ptr_val() const{ return *ptr;}
void set_ptr_val(int val) const {*ptr = val;}
private:
int *ptr;
int val;
};
2. 默認復制/賦值與指針成員
int obj = 0;
HasPtr ptr1(&obj, 42);
HasPtr ptr2(ptr1);
具有指針成員且使用默認合成復制構造函數的類具有普通指針的所有缺陷。尤其是,類本身無法避免垂懸指針。
3. 指針共享同一對象
ptr1.set_ptr_val(45);
cout << ptr2.get_ptr_val() << endl; //45
兩個指針指向同一對象時,其中任意一個都可以改變共享對象的值。
4. 可能出現垂懸指針
int *p = new int(42);
HasPtr ptr3(p, 42);
delete p;
13.5.1 定義智能指針類
1. 引入使用計數
定義智能指針的通用技術是采用一個使用計數(use count)。智能指針類將一個計數器與類指向的對象相關聯。使用計數跟蹤該類有多少個對象共享同一指針。使用計數為0時,刪除對象。使用計數有時也稱為引用計數(reference count)。
2. 使用計數類
class U_Ptr
{
friend class HasPtr;
int *ip;
size_t use;
U_Ptr(int *p):ip(p),use(1){}
~U_Ptr(){ delete ip; }
};
3. 使用計數類的使用
class HasPtr
{
public:
HasPtr(int *p, int i):ptr(new U_Ptr(p)), val(i){}
HasPtr(const HasPtr &orig):ptr(orig.ptr), val(orig.val){++ ptr-> use;}
HasPtr &operator= (const HasPtr &);
~HasPtr(){
if(--ptr->use == 0)
delete ptr;
}
private:
U_Ptr *ptr;
int val;
};
4. 賦值與使用計數
HasPtr &HasPtr::operator= (const HasPtr &hasptr)
{
++hasptr.ptr->use;
if(--ptr->use==0) //delete the old ptr if it's reference count is 1.
delete ptr;
ptr = hasptr.ptr; //copy the new ptr value.
val = hasptr.val;
return *this;
}
這個賦值操作符在減少左操作數的使用計數之前使rhs的使用計數加1,從而防止自身賦值。
5. 改變其他成員
class HasPtr
{
public:
HasPtr(int *p, int i):ptr(new U_Ptr(p)), val(i){}
HasPtr(const HasPtr &orig):ptr(orig.ptr), val(orig.val){++ ptr-> use;}
HasPtr &operator= (const HasPtr &);
~HasPtr(){
if(--ptr->use == 0)
delete ptr;
}
int *get_ptr() const{ return ptr->ip; }
int get_int() const{ return val; }
void set_ptr(int *p){ ptr->ip=p; }
void set_int(int i){ val=i; }
int get_ptr_val() const{ return *(ptr->ip);}
void set_ptr_val(int val) const {*(ptr->ip) = val;}
private:
U_Ptr *ptr;
int val;
};
為了管理具有指針類型成員的類,必須定義三個復制控制成員:復制構造函數、賦值操作符和析構函數。這些成員可以定義指針成員的指針型行為或值型行為。
值型類將指針成員所指基礎值的副本給每個對象。復制構造函數分配新元素並從復制對象處復制值,賦值操作符撤銷所保存的原對象並從右操作數向左操作數復制值,析構函數撤銷對象。
摘自 xufei96的專欄