国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > C++ > 正文

C++如何設(shè)計(jì)一個(gè)含指針的類(Boolan筆記第二周)

2019-11-11 02:15:20
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

C++里面設(shè)計(jì)一個(gè)含指針的類比設(shè)計(jì)一個(gè)不含指針的類要復(fù)雜很多,因?yàn)樾枰紤]Big Three,也就是三個(gè)特殊函數(shù): 拷貝構(gòu)造函數(shù)(copy constructor), 拷貝賦值函數(shù)(copy assignment Operator) 析構(gòu)函數(shù)(destructor)

為什么不含指針的類不需要考慮Big Three呢?因?yàn)镃++編譯器會(huì)生成缺省拷貝構(gòu)造函數(shù)和缺省拷貝賦值函數(shù),這些函數(shù)采用memberwise copy,也就是淺拷貝。如果類不含指針的話,這也就夠用了。編譯器也會(huì)生成缺省的析構(gòu)函數(shù),但該析構(gòu)函數(shù)不做任何操作。如果類不含指針,當(dāng)對(duì)象生命期結(jié)束時(shí)析構(gòu)也不用做什么事情,所以也沒問題。

但是類里面含有指針的話,C++編譯器生成的缺省拷貝構(gòu)造函數(shù)和缺省拷貝賦值函數(shù)會(huì)采用淺拷貝將一個(gè)object的成員一項(xiàng)一項(xiàng)的拷貝給另一個(gè)object,這樣會(huì)導(dǎo)致同一個(gè)類的兩個(gè)object里面的指針指向同一個(gè)地址。這樣,如果object1的指針值賦值給了object2,那object2的指針原來(lái)指向的內(nèi)存就沒人管了,造成內(nèi)存泄漏。那如果object2的指針之前是NULL行不行呢?也不行。因?yàn)槿绻鹢bject 1修改了這個(gè)指針的內(nèi)容,或者刪除了這個(gè)指針,object 2就躺著中槍了。

所以,類里面有指針的話,我們要自己設(shè)計(jì)Big Three。其中的 拷貝構(gòu)造函數(shù)和拷貝賦值函數(shù)會(huì)生成新的指針,然后把指針指向的內(nèi)容拷貝過來(lái)。這就是深拷貝。   以class string為例,代碼如下: class String { public: String(const char* cstr = 0); //構(gòu)造函數(shù) String(const String& str); //拷貝構(gòu)造函數(shù) String& operator=(const String& str); //拷貝賦值函數(shù) ~String(); //析構(gòu)函數(shù) char* get_c_str() const {return m_data;} PRivate: char* m_data; }

先討論拷貝構(gòu)造函數(shù)(copy ctor),代碼例子如下:

inline String::String(const String& str) { m_data = new char[ strlen(str.m_data)+1]; strcpy(m_data, str.m_data); }

注意,拷貝構(gòu)造函數(shù)里m_data是新創(chuàng)建的指針,它指向的內(nèi)容是拷貝過來(lái)的。

拷貝構(gòu)造函數(shù)的用法如下: String s1(“hello”); String s2(s1);   // line 1  String s2=s1; // line 2 上面的line 1和line 2都會(huì)調(diào)用拷貝構(gòu)造函數(shù)將s1的字符串賦給s2。注意s2的m_data跟s1的m_data的值不同,但指向的內(nèi)容是一樣的。

下面來(lái)談?wù)?strong>拷貝賦值函數(shù)(copy assignment operator),代碼例子如下: inline String& String::operator=(const String& str) { if (this==&str) return *this; delete[] m_data; m_data=new char[ strlen(str.m_data)+1]; strcpy(m_data, str.m_data); return *this; }

這里有很多地方需要注意: 1) 拷貝賦值函數(shù)必須要檢查this指針是否已經(jīng)是準(zhǔn)備拷貝的對(duì)象里面的指針,換句話說(shuō)就是不能把obj1賦值給obj1自己。因?yàn)槿绻粰z查的話,接下來(lái)的delete[]就把這個(gè)指針指向的內(nèi)存給釋放了,這個(gè)指針就成了野指針。 再細(xì)想一下,為什么前面的拷貝賦值函數(shù)不需要檢查this指針呢?這是因?yàn)橛每截愘x值函數(shù)的時(shí)候,對(duì)象還沒有創(chuàng)建,所以this指針為空。

2) 為什么要delete[] m_data呢? 因?yàn)檫@里s2已經(jīng)存在,它的m_data已經(jīng)指向一段內(nèi)存,里面已經(jīng)有東西。直接把s1的m_data指向的字符串的內(nèi)容拷貝給s2的m_data指向的內(nèi)存是危險(xiǎn)的,一是后者長(zhǎng)度很可能不一樣,再一個(gè)可能會(huì)覆蓋了什么重要東西。所以我們要先把m_data指向的內(nèi)容清除掉, 再重新創(chuàng)建m_data指針。

3) 還有一個(gè)問題,為什么要用delete[]而不是delete呢?其實(shí)這里是可以用delete的,因?yàn)閏har是基本數(shù)據(jù)類型,不涉及到析構(gòu)函數(shù)。delete[]和delete都是把m_data指向的那個(gè)字符串的內(nèi)容給釋放了,效果是一樣的。但是為了規(guī)范起見,new[]應(yīng)該和delete[]對(duì)應(yīng)。

拷貝賦值函數(shù)的用法如下: String s1(“hello”); String s2(s1); s2=s1; //line 3 這里s2會(huì)拷貝s1的字符串內(nèi)容,注意s2和s1的m_data指向不同的地址。這里要特別注意的是: s2=s1 和上面的line2 String s2=s1; 不一樣,后者是調(diào)用的拷貝構(gòu)造函數(shù)。在line3里面,s2這個(gè)object已經(jīng)創(chuàng)建,里面的m_data指向的字符串已經(jīng)有內(nèi)容,而在line2里面,s2還沒有被創(chuàng)建。

下面來(lái)談?wù)?strong>析構(gòu)函數(shù)(dtor)。析構(gòu)函數(shù)在以下三個(gè)地方會(huì)被用到。 1.object生命周期結(jié)束,被銷毀時(shí); 2.delete指向object的指針,或delete指向object的基類類型指針并且基類析構(gòu)函數(shù)是虛函數(shù)時(shí); 3.object 1是object 2的成員,object 2的析構(gòu)函數(shù)被調(diào)用時(shí),object 1的析構(gòu)函數(shù)也被調(diào)用。

String類的析構(gòu)函數(shù)的例子如下: inline String::~String() { delete[] m_data; } 這里析構(gòu)函數(shù)把m_data指向的字符串的內(nèi)存給釋放了。注意這里因?yàn)閏har是基本類型,所以用delete m_data其實(shí)是可以的。但是為了規(guī)范,new[]和delete[]必須要對(duì)應(yīng)。另外,delete[]或者delete怎么知道要釋放多少內(nèi)存呢?因?yàn)閚ew[]的時(shí)候編譯器已經(jīng)知道長(zhǎng)度了。

下面談?wù)刵ew/delete和內(nèi)存以及構(gòu)造函數(shù)和析構(gòu)函數(shù)的關(guān)系: new: 先分配內(nèi)存(這里會(huì)調(diào)用malloc),再調(diào)用ctor delete: 先調(diào)用dtor,再釋放內(nèi)存(這里會(huì)調(diào)用free) 要注意的是,new[]一定要搭配delete[]。這里需要注意的是,對(duì)于C++的基本數(shù)據(jù)類型比如int,char的數(shù)組,因?yàn)椴簧婕拔鰳?gòu)操作(內(nèi)部無(wú)指針),delete和delete[]效果是一樣的,都是釋放內(nèi)存。但是對(duì)于一個(gè)class的object數(shù)組,delete和delete[]效果就不一樣了,舉例如下: String *p=new String[3];

delete[] p; 會(huì)調(diào)用String的析構(gòu)函數(shù)三次,分別析構(gòu)p[0],p[1],p[2],然后釋放掉p[]的所有內(nèi)存。

delete p; 只會(huì)調(diào)用String的析構(gòu)函數(shù)一次,析構(gòu)p[0],然后釋放掉p[]的所有內(nèi)存。那么這里問題就來(lái)了,p[1],p[2]都清掉了,p[1],p[2]的m_data所指向的內(nèi)存也就沒人管了,造成2處內(nèi)存泄漏。

關(guān)于delete p或delete[] p還要注意的一點(diǎn)是它只釋放指針指向的內(nèi)存,至于指針的值它不會(huì)置為NULL。所以有時(shí)候?yàn)榱税踩鹨姡€要把p設(shè)置為NULL。

另外,關(guān)于析構(gòu)函數(shù)還需要注意一點(diǎn),Base class的析構(gòu)函數(shù)一定要是虛函數(shù)。在Effective C++的條款14指出,當(dāng)由基類指針刪除一個(gè)派生類對(duì)象是,如果基類的析構(gòu)函數(shù)非虛,結(jié)果為未定義。舉例來(lái)說(shuō),假如Base的析構(gòu)函數(shù)非虛的話: Base *p = new Derived();  // 此處會(huì)先調(diào)用基類的構(gòu)造函數(shù),然后調(diào)用子類的構(gòu)造函數(shù) delete p;  //注意,此處只會(huì)調(diào)用基類的析構(gòu)函數(shù)。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表

圖片精選

主站蜘蛛池模板: 嘉黎县| 云南省| 余江县| 东乌珠穆沁旗| 建湖县| 阳信县| 灵石县| 永川市| 吉木乃县| 济阳县| 阳山县| 怀来县| 台东县| 左权县| 双牌县| 梅河口市| 赤峰市| 桃园市| 桐乡市| 平潭县| 东乡| 潢川县| 苗栗市| 新闻| 汤原县| 东乌珠穆沁旗| 霸州市| 岢岚县| 庆阳市| 吴忠市| 尼勒克县| 华坪县| 南召县| 加查县| 贵港市| 屏南县| 建德市| 五常市| 南涧| 大埔县| 黑水县|