日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

管理类的指针成员

發(fā)布時間:2024/4/13 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 管理类的指针成员 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

設計具有指針成員的類時,類設計者必須首先需要決定的是該指針應提供什么行為。
將一個指針復制到另一個指針時,兩個指針指向同一對象。
當兩個指針指向同一對象時,可能使用任一指針改變基礎對象。
類似地,很可能一個指針刪除了一對象時,另一指針的用戶還認為基礎對象仍然存在。
指針成員默認具有與指針對象同樣的行為。

然而,通過不同的復制控制策略,可以為指針成員實現(xiàn)不同的行為。
大多數(shù) C++ 類采用以下三種方法之一管理指針成員:

1. 指針成員采取常規(guī)指針型行為。這樣的類具有指針的所有缺陷但無需特殊的復制控制。
2. 類可以實現(xiàn)所謂的“智能指針”行為。指針所指向的對象是共享的,但類能夠防止懸垂指針。
3. 類采取值型行為。指針所指向的對象是唯一的,由每個類對象獨立管理。

[1. 一個帶指針成員的簡單類]

為了闡明所涉及的問題,我們將實現(xiàn)一個簡單類,該類包含一個 int 值和一個指針:

// class that has a pointer member that behaves like a plain pointerclass HasPtr {public:// copy of the values we're givenHasPtr(int *p, int i): ptr(p), val(i) { }// const members to return the value of the indicated data memberint *get_ptr() const { return ptr; }int get_int() const { return val; }// non const members to change the indicated data membervoid set_ptr(int *p) { ptr = p; }void set_int(int i) { val = i; }// return or change the value pointed to, so ok for const objectsint get_ptr_val() const { return *ptr; }void set_ptr_val(int val) const { *ptr = val; }private:int *ptr;int val;};

1.1 默認復制/賦值與指針成員

因為 HasPtr 類沒有定義復制構造函數(shù),所以復制一個 HasPtr 對象將復制兩個成員:

int obj = 0;HasPtr ptr1(&obj, 42); // int* member points to obj, val is 42HasPtr ptr2(ptr1); // int* member points to obj, val is 42

復制之后,ptr1 和 ptr2 中的指針指向同一對象且兩個對象中的 int 值相同。

但是,因為指針的值不同于它所指對象的值,這兩個成員的行為看來非常不同。

復制之后,int 值是清楚和獨立的,而指針則糾纏在一起。

注意:具有指針成員且使用默認合成復制構造函數(shù)的類具有普通指針的所有缺陷。

?????????尤其是,類本身無法避免懸垂指針。

1.2 指針共享同一對象

復制一個算術值時,副本獨立于原版,可以改變一個副本而不改變另一個:

ptr1.set_int(0); // changes val member only in ptr1ptr2.get_int(); // returns 42ptr1.get_int(); // returns 0

復制指針時,地址值是可區(qū)分的,但指針指向同一基礎對象。
如果在任一對象上調用 set_ptr_val,則二者的基礎對象都會改變:

ptr1.set_ptr_val(42); // sets object to which both ptr1 and ptr2 pointptr2.get_ptr_val(); // returns 42

兩個指針指向同一對象時,其中任意一個都可以改變共享對象的值。

1.3 懸垂指針(Dangling Pointers)

因為類直接復制指針,會使用戶面臨潛在的問題:HasPtr 保存著給定指針。
用戶必須保證只要 HasPtr 對象存在,該指針指向的對象就存在:

int *ip = new int(42); // dynamically allocated int initialized to 42HasPtr ptr(ip, 10); // Has Ptr points to same object as ip doesdelete ip; // object pointed to by ip is freedptr.set_ptr_val(0); // disaster: The object to which Has Ptr points was freed!

這里的問題是 ip 和 ptr 中的指針指向同一對象。
刪除了該對象時,ptr 中的指針不再指向有效對象。
然而,ptr 卻沒有辦法得知對象已經不存在了。

[2. 定義智能指針類]

智能指針除了增加功能外,其行為像普通指針一樣。
本例中讓智能指針負責刪除共享對象。
用戶將動態(tài)分配一個對象并將該對象的地址傳給新的 HasPtr 類。
用戶仍然可以通過普通指針訪問對象,但絕不能刪除指針。?

HasPtr 類將保證在撤銷指向對象的最后一個 HasPtr 對象時刪除對象。
具體而言,復制對象時,副本和原對象將指向同一基礎對象,
如果通過一個副本改變基礎對象,則通過另一對象訪問的值也會改變。?

新的 HasPtr 類需要一個析構函數(shù)來刪除指針,但是,析構函數(shù)不能無條件地刪除指針。
如果兩個 HasPtr 對象指向同一基礎對象,
那么在兩個對象都撤銷之前,我們并不希望刪除基礎對象。
為了編寫析構函數(shù),需要知道這個 HasPtr 對象是否為指向給定對象的最后一個。

2.1 引入使用計數(shù)

定義智能指針的通用技術是采用一個使用計數(shù)(或引用計數(shù))。
智能指針類將一個計數(shù)器與類指向的對象相關聯(lián)。
使用計數(shù)跟蹤該類有多少個對象共享同一指針。
使用計數(shù)為 0 時,刪除對象。每次創(chuàng)建類的新對象時,初始化指針并將使用計數(shù)置為 1。
當對象作為另一對象的副本而創(chuàng)建時,復制構造函數(shù)復制指針并增加與之相應的使用計數(shù)的值。
對一個對象進行賦值時,
賦值操作符減少左操作數(shù)所指對象的使用計數(shù)的值(如果使用計數(shù)減至 0,則刪除對象),
并增加右操作數(shù)所指對象的使用計數(shù)的值。
最后,調用析構函數(shù)時,析構函數(shù)減少使用計數(shù)的值,如果計數(shù)減至 0,則刪除基礎對象。
唯一的創(chuàng)新在于決定將使用計數(shù)放在哪里。
計數(shù)器不能直接放在 HasPtr 對象中,為什么呢?考慮下面的情況:

int obj;HasPtr p1(&obj, 42);HasPtr p2(p1); // p1 and p2 both point to same int objectHasPtr p3(p1); // p1, p2, and p3 all point to same int object

如果使用計數(shù)保存在 HasPtr 對象中,創(chuàng)建 p3 時怎樣更新它?
可以在 p1 中將計數(shù)增量并復制到 p3,但怎樣更新 p2 中的計數(shù)?

2.2 使用計數(shù)類

實現(xiàn)使用計數(shù)有兩種經典策略,另一種方法將在后續(xù)章節(jié)中講述。
這里所用的方法中,需要定義一個單獨的具體類用以封閉使用計數(shù)和相關指針:

// private class for use by HasPtr onlyclass U_Ptr {friend class HasPtr;int *ip;size_t use;U_Ptr(int *p): ip(p), use(1) { }~U_Ptr() { delete ip; }};

這個類的所有成員均為 private。

我們不希望用戶使用 U_Ptr 類,所以它沒有任何 public 成員。
將 HasPtr 類設置為友元,使其成員可以訪問 U_Ptr 的成員。
U_Ptr 類保存指針和使用計數(shù),每個 HasPtr 對象將指向一個 U_Ptr 對象,
使用計數(shù)將跟蹤指向每個 U_Ptr 對象的 HasPtr 對象的數(shù)目。
U_Ptr 定義的構造函數(shù)復制指針,而析構函數(shù)刪除它。
構造函數(shù)還將使用計數(shù)置為 1,表示一個 HasPtr 對象指向這個 U_Ptr 對象。
假定剛從指向 int 值 42 的指針創(chuàng)建一個 HasPtr 對象,可以畫出這些對象,如下圖:

如果復制這個對象,則對象如下圖所示。

2.3 使用計數(shù)類的使用

新的 HasPtr 類保存一個指向 U_Ptr 對象的指針,U_Ptr 對象指向實際的 int 基礎對象。
必須改變每個成員以說明的 HasPtr 類指向一個 U_Ptr 對象而不是一個 int* 值。
先看看構造函數(shù)和復制控制成員:

/* smart pointer class: takes ownership of the dynamically allocated* object to which it is bound* User code must dynamically allocate an object to initialize a HasPtr* and must not delete that object; the HasPtr class will delete it*/class HasPtr {public:// HasPtr owns the pointer; pmust have been dynamically allocatedHasPtr(int *p, int i): ptr(new U_Ptr(p)), val(i) { }// copy members and increment the use countHasPtr(const HasPtr &orig):ptr(orig.ptr), val(orig.val) { ++ptr->use; }HasPtr& operator=(const HasPtr&);// if use count goes to zero, delete the U_Ptr object~HasPtr() { if (--ptr->use == 0) delete ptr; }
private:U_Ptr *ptr; // points to use-counted U_Ptr classint val;};

接受一個指針和一個 int 值的 HasPtr 構造函數(shù)使用其指針形參創(chuàng)建一個新的 U_Ptr 對象。
HasPtr 構造函數(shù)執(zhí)行完畢后,HasPtr 對象指向一個新分配的 U_Ptr 對象,該 U_Ptr 對象存儲給定指針。
新 U_Ptr 中的使用計數(shù)為 1,表示只有一個 HasPtr 對象指向它。
復制構造函數(shù)從形參復制成員并增加使用計數(shù)的值。
復制構造函數(shù)執(zhí)行完畢后,新創(chuàng)建對象與原有對象指向同一 U_Ptr 對象,該 U_Ptr 對象的使用計數(shù)加 1。
析構函數(shù)將檢查 U_Ptr 基礎對象的使用計數(shù)。
如果使用計數(shù)為 0,則這是最后一個指向該 U_Ptr 對象的 HasPtr 對象,
在這種情況下,HasPtr 析構函數(shù)刪除其 U_Ptr 指針。
刪除該指針將引起對 U_Ptr 析構函數(shù)的調用,U_Ptr 析構函數(shù)刪除 int 基礎對象。

2.4 賦值與使用計數(shù)

賦值操作符比復制構造函數(shù)復雜一點:

HasPtr& HasPtr::operator=(const HasPtr &rhs){++rhs.ptr->use; // increment use count on rhs firstif (--ptr->use == 0)delete ptr; // if use count goes to 0 on this object, delete itptr = rhs.ptr; // copy the U_Ptr objectval = rhs.val; // copy the int memberreturn *this;}

在這里,首先將右操作數(shù)中的使用計數(shù)加 1,然后將左操作數(shù)對象的使用計數(shù)減 1 并檢查這個使用計數(shù)。
如果這是指向 U_Ptr 對象的最后一個對象,就刪除該對象,這會依次撤銷 int 基礎對象。
將左操作數(shù)中的當前值減 1(可能撤銷該對象)之后,
再將指針從 rhs 復制到這個對象。賦值照常返回對這個對象的引用。
這個賦值操作符在減少左操作數(shù)的使用計數(shù)之前使 rhs 的使用計數(shù)加 1,從而防止自身賦值。

注意:如果左右操作數(shù)相同,賦值操作符的效果將是 U_Ptr 基礎對象的使用計數(shù)加 1 之后立即減 1。

2.5 改變其他成員

現(xiàn)在需要改變訪問 int* 的其他成員,以便通過 U_Ptr 指針間接獲取 int:

class HasPtr {public:// copy control and constructors as before// accessors must change to fetch value from U_Ptr objectint *get_ptr() const { return ptr->ip; }int get_int() const { return val; }// change the appropriate data membervoid set_ptr(int *p) { ptr->ip = p; }void set_int(int i) { val = i; }// return or change the value pointed to, so ok for const objects// Note: *ptr->ip is equivalent to *(ptr->ip)int get_ptr_val() const { return *ptr->ip; }void set_ptr_val(int i) { *ptr->ip = i; }private:U_Ptr *ptr; // points to use-counted U_Ptr classint val;};

那些使用指針操作的函數(shù)必須對 U_Ptr 解引用,以便獲取 int* 基礎對象。
復制 HasPtr 對象時,int 成員的行為與第一個類中一樣。
所復制的是 int 成員的值,各成員是獨立的,副本和原對象中的指針仍指向同一基礎對象,
對基礎對象的改變將影響通過任一 HasPtr 對象所看到的值。
然而,HasPtr 的用戶無須擔心懸垂指針,只要他們讓 HasPtr 類負責釋放對象,
HasPtr 類將保證只要有指向基礎對象的 HasPtr 對象存在,基礎對象就存在。

為了管理具有指針成員的類,
必須定義復制構造函數(shù)、賦值操作符和析構函數(shù)這三個復制控制成員。
值型類將指針成員所指基礎值的副本給每個對象。
復制構造函數(shù)分配新元素并從被復制對象處復制值,
賦值操作符撤銷所保存的原對象并從右操作數(shù)向左操作數(shù)復制值,析構函數(shù)撤銷對象。?

“智能指針”的類在對象間共享同一基礎值,從而提供了指針型行為。
使用計數(shù)是管理智能指針類的通用技術。
同一基礎值的每個副本都有一個使用計數(shù)。
復制構造函數(shù)將指針從舊對象復制到新對象時,會將使用計數(shù)加 1。
賦值操作符將左操作數(shù)的使用計數(shù)減 1 并將右操作數(shù)的使用計數(shù)加 1,
如果左操作數(shù)的使用計數(shù)減至 0,賦值操作符必須刪除它所指向的對象,
最后,賦值操作符將指針從右操作數(shù)復制到左操作數(shù)。
析構函數(shù)將使用計數(shù)減 1,并且,如果使用計數(shù)減至 0,就刪除基礎對象。

?[3. 定義值型類]

處理指針成員的另一個方法是給指針成員提供值語義。
具有值語義的類所定義的對象,其行為很像算術類型的對象:
復制值型對象時,會得到一個不同的新副本。
對副本所做的改變不會反映在原有對象上,反之亦然。string 類是值型類的一個例子。
要使指針成員表現(xiàn)得像一個值,復制 HasPtr 對象時必須復制指針所指向的對象:

/** Valuelike behavior even though HasPtr has a pointer member:* Each time we copy a HasPtr object, we make a new copy of the* underlying int object to which ptr points.*/class HasPtr {public:// no point to passing a pointer if we're going to copy it anyway// store pointer to a copy of the object we're givenHasPtr(const int &p, int i): ptr(new int(p)), val(i) {}// copy members and increment the use countHasPtr(const HasPtr &orig):ptr(new int (*orig.ptr)), val(orig.val) { }HasPtr& operator=(const HasPtr&);~HasPtr() { delete ptr; }// accessors must change to fetch value from Ptr objectint get_ptr_val() const { return *ptr; }int get_int() const { return val; }// change the appropriate data membervoid set_ptr(int *p) { ptr = p; }void set_int(int i) { val = i; }// return or change the value pointed to, so ok for const objectsint *get_ptr() const { return ptr; }void set_ptr_val(int p) const { *ptr = p; }private:int *ptr; // points to an intint val;};

復制構造函數(shù)不再復制指針,它將分配一個新的 int 對象,并初始化該對象以保存與被復制對象相同的值。
每個對象都保存屬于自己的 int 值的不同副本,所以析構函數(shù)將無條件刪除指針。
賦值操作符不需要分配新對象,
它只是必須記得給其指針所指向的對象賦新值,而不是給指針本身賦值:

HasPtr& HasPtr::operator=(const HasPtr &rhs){// Note: Every HasPtr is guaranteed to point at an actual int;// We know that ptr cannot be a zero pointer*ptr = *rhs.ptr; // copy the value pointed toval = rhs.val; // copy the int
return *this;}

換句話說,改變的是指針所指向的值,而不是指針。
即使要將一個對象賦值給它本身,賦值操作符也必須總是保證正確。
本例中,即使左右操作數(shù)相同,操作本質上也是安全的,因此,不需要顯式檢查自身賦值。

轉載于:https://www.cnblogs.com/yshl-dragon/p/3227707.html

總結

以上是生活随笔為你收集整理的管理类的指针成员的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。