亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

高質量C++/C編程指南 -- 第9章 類的構造函數、析構函數與賦值函數

2019-11-17 05:18:18
字體:
來源:轉載
供稿:網友

  構造函數、析構函數與賦值函數是每個類最基本的函數。它們太普通以致讓人輕易麻痹大意,其實這些貌似簡單的函數就象沒有頂蓋的下水道那樣危險。 每個類只有一個析構函數和一個賦值函數,但可以有多個構造函數(包含一個拷貝構造函數,其它的稱為普通構造函數)。對于任意一個類A,假如不想編寫上述函數,C++編譯器將自動為A產生四個缺省的函數,如A(void); // 缺省的無參數構造函數A(const A &a); // 缺省的拷貝構造函數~A(void); // 缺省的析構函數A & Operate =(const A &a); // 缺省的賦值函數這不禁讓人迷惑,既然能自動生成函數,為什么還要程序員編寫?原因如下:(1)假如使用“缺省的無參數構造函數”和“缺省的析構函數”,等于放棄了自主“初始化”和“清除”的機會,C++發明人Stroustrup的好心好意白費了。(2)“缺省的拷貝構造函數”和“缺省的賦值函數”均采用“位拷貝”而非“值拷貝”的方式來實現,倘若類中含有指針變量,這兩個函數注定將出錯。對于那些沒有吃夠苦頭的C++程序員,假如他說編寫構造函數、析構函數與賦值函數很輕易,可以不用動腦筋,表明他的熟悉還比較膚淺,水平有待于提高。本章以類String的設計與實現為例,深入闡述被很多教科書忽視了的道理。String的結構如下:class String{public:String(const char *str = NULL); // 普通構造函數String(const String &other); // 拷貝構造函數~ String(void); // 析構函數String & operate =(const String &other); // 賦值函數PRivate:char *m_data; // 用于保存字符串}; 9.1 構造函數與析構函數的起源
作為比C更先進的語言,C++提供了更好的機制來增強程序的安全性。C++編譯器具有嚴格的類型安全檢查功能,它幾乎能找出程序中所有的語法問題,這的確幫了程序員的大忙。但是程序通過了編譯檢查并不表示錯誤已經不存在了,在“錯誤”的大家庭里,“語法錯誤”的地位只能算是小弟弟。級別高的錯誤通常隱藏得很深,就象狡猾的罪犯,想逮住他可不輕易。根據經驗,不少難以察覺的程序錯誤是由于變量沒有被正確初始化或清除造成的,而初始化和清除工作很輕易被人遺忘。Stroustrup在設計C++語言時充分考慮了這個問題并很好地予以解決:把對象的初始化工作放在構造函數中,把清除工作放在析構函數中。當對象被創建時,構造函數被自動執行。當對象消亡時,析構函數被自動執行。這下就不用擔心忘了對象的初始化和清除工作。構造函數與析構函數的名字不能隨便起,必須讓編譯器認得出才可以被自動執行。Stroustrup的命名方法既簡單又合理:讓構造函數、析構函數與類同名,由于析構函數的目的與構造函數的相反,就加前綴‘~’以示區別。除了名字外,構造函數與析構函數的另一個非凡之處是沒有返回值類型,這與返回值類型為void的函數不同。構造函數與析構函數的使命非常明確,就象出生與死亡,光溜溜地來光溜溜地去。假如它們有返回值類型,那么編譯器將不知所措。為了防止節外生枝,干脆規定沒有返回值類型。(以上典故參考了文獻[Eekel, p55-p56])9.2 構造函數的初始化表
構造函數有個非凡的初始化方式叫“初始化表達式表”(簡稱初始化表)。初始化表位于函數參數表之后,卻在函數體 {} 之前。這說明該表里的初始化工作發生在函數體內的任何代碼被執行之前。構造函數初始化表的使用規則:u 假如類存在繼續關系,派生類必須在其初始化表里調用基類的構造函數。例如class A{…A(int x); // A的構造函數}; class B : public A{…B(int x, int y);// B的構造函數};B::B(int x, int y): A(x) // 在初始化表里調用A的構造函數{…} u 類的const常量只能在初始化表里被初始化,因為它不能在函數體內用賦值的方式來初始化(參見5.4節)。u 類的數據成員的初始化可以采用初始化表或函數體內賦值兩種方式,這兩種方式的效率不完全相同。非內部數據類型的成員對象應當采用第一種方式初始化,以獲取更高的效率。例如class A{…A(void); // 無參數構造函數A(const A &other); // 拷貝構造函數A & operate =( const A &other); // 賦值函數};class B{public:B(const A &a); // B的構造函數private: A m_a; // 成員對象};示例9-2(a)中,類B的構造函數在其初始化表里調用了類A的拷貝構造函數,從而將成員對象m_a初始化。示例9-2 (b)中,類B的構造函數在函數體內用賦值的方式將成員對象m_a初始化。我們看到的只是一條賦值語句,但實際上B的構造函數干了兩件事:先暗地里創建m_a對象(調用了A的無參數構造函數),再調用類A的賦值函數,將參數a賦給m_a。B::B(const A &a): m_a(a) { … }
B::B(const A &a){m_a = a;…
}
示例9-2(a) 成員對象在初始化表中被初始化 示例9-2(b) 成員對象在函數體內被初始化對于內部數據類型的數據成員而言,兩種初始化方式的效率幾乎沒有區別,但后者的程序版式似乎更清楚些。若類F的聲明如下:class F{public:F(int x, int y); // 構造函數private:int m_x, m_y;int m_i, m_j;}示例9-2(c)中F的構造函數采用了第一種初始化方式,示例9-2(d)中F的構造函數采用了第二種初始化方式。F::F(int x, int y): m_x(x), m_y(y) { m_i = 0; m_j = 0;}
F::F(int x, int y){ m_x = x;m_y = y;m_i = 0; m_j = 0;}
示例9-2(c) 數據成員在初始化表中被初始化 示例9-2(d) 數據成員在函數體內被初始化9.3 構造和析構的次序
構造從類層次的最根處開始,在每一層中,首先調用基類的構造函數,然后調用成員對象的構造函數。析構則嚴格按照與構造相反的次序執行,該次序是唯一的,否則編譯器將無法自動執行析構過程。一個有趣的現象是,成員對象初始化的次序完全不受它們在初始化表中次序的影響,只由成員對象在類中聲明的次序決定。這是因為類的聲明是唯一的,而類的構造函數可以有多個,因此會有多個不同次序的初始化表。假如成員對象按照初始化表的次序進行構造,這將導致析構函數無法得到唯一的逆序。[Eckel, p260-261]9.4 示例:類String的構造函數與析構函數
// String的普通構造函數String::String(const char *str){if(str==NULL){m_data = new char[1];*m_data = ‘/0’;} else{int length = strlen(str);m_data = new char[length+1];strcpy(m_data, str);}} // String的析構函數String::~String(void){delete [] m_data; // 由于m_data是內部數據類型,也可以寫成 delete m_data;}9.5 不要輕視拷貝構造函數與賦值函數
由于并非所有的對象都會使用拷貝構造函數和賦值函數,程序員可能對這兩個函數有些輕視。請先記住以下的警告,在閱讀正文時就會多心:u 本章開頭講過,假如不主動編寫拷貝構造函數和賦值函數,編譯器將以“位拷貝”的方式自動生成缺省的函數。倘若類中含有指針變量,那么這兩個缺省的函數就隱含了錯誤。以類String的兩個對象a,b為例,假設a.m_data的內容為“hello”,b.m_data的內容為“world”?,F將a賦給b,缺省賦值函數的“位拷貝”意味著執行b.m_data = a.m_data。這將造成三個錯誤:一是b.m_data原有的內存沒被釋放,造成內存泄露;二是b.m_data和a.m_data指向同一塊內存,a或b任何一方變動都會影響另一方;三是在對象被析構時,m_data被釋放了兩次。u 拷貝構造函數和賦值函數非常輕易混淆,常導致錯寫、錯用??截悩嬙旌瘮凳窃趯ο蟊粍摻〞r調用的,而賦值函數只能被已經存在了的對象調用。以下程序中,第三個語句和第四個語句很相似,你分得清楚哪個調用了拷貝構造函數,哪個調用了賦值函數嗎?String a(“hello”);String b(“world”);String c = a; // 調用了拷貝構造函數,最好寫成 c(a);c = b; // 調用了賦值函數本例中第三個語句的風格較差,宜改寫成String c(a) 以區別于第四個語句。9.6 示例:類String的拷貝構造函數與賦值函數
// 拷貝構造函數String::String(const String &other){ // 答應操作other的私有成員m_dataint length = strlen(other.m_data); m_data = new char[length+1];strcpy(m_data, other.m_data);}// 賦值函數String & String::operate =(const String &other){ // (1) 檢查自賦值if(this == &other)return *this;// (2) 釋放原有的內存資源delete [] m_data;// (3)分配新的內存資源,并復制內容int length = strlen(other.m_data); m_data = new char[length+1];strcpy(m_data, other.m_data);// (4)返回本對象的引用return *this;} 類String拷貝構造函數與普通構造函數(參見9.4節)的區別是:在函數入口處無需與NULL進行比較,這是因為“引用”不可能是NULL,而“指針”可以為NULL。類String的賦值函數比構造函數復雜得多,分四步實現:(1)第一步,檢查自賦值。你可能會認為多此一舉,難道有人會愚蠢到寫出 a = a 這樣的自賦值語句!的確不會。但是間接的自賦值仍有可能出現,例如
// 內容自賦值b = a;…c = b;…a = c;
// 地址自賦值b = &a;…a = *b;
也許有人會說:“即使出現自賦值,我也可以不理睬,大不了化點時間讓對象復制自己而已,反正不會出錯!”他真的說錯了??纯吹诙降膁elete,自殺后還能復制自己嗎?所以,假如發現自賦值,應該馬上終止函數。注重不要將檢查自賦值的if語句if(this == &other)錯寫成為if( *this == other)(2)第二步,用delete釋放原有的內存資源。假如現在不釋放,以后就沒機會了,將造成內存泄露。(3)第三步,分配新的內存資源,并復制字符串。注重函數strlen返回的是有效字符串長度,不包含結束符‘/0’。函數strcpy則連‘/0’一起復制。(4)第四步,返回本對象的引用,目的是為了實現象 a = b = c 這樣的鏈式表達。注重不要將 return *this 錯寫成 return this 。那么能否寫成return other 呢?效果不是一樣嗎?不可以!因為我們不知道參數other的生命期。有可能other是個臨時對象,在賦值結束后它馬上消失,那么return other返回的將是垃圾。9.7 偷懶的辦法處理拷貝構造函數與賦值函數
假如我們實在不想編寫拷貝構造函數和賦值函數,又不答應別人使用編譯器生成的缺省函數,怎么辦?偷懶的辦法是:只需將拷貝構造函數和賦值函數聲明為私有函數,不用編寫代碼。例如:class A{ …private:A(const A &a); // 私有的拷貝構造函數A & operate =(const A &a); // 私有的賦值函數};假如有人試圖編寫如下程序:A b(a); // 調用了私有的拷貝構造函數b = a; // 調用了私有的賦值函數編譯器將指出錯誤,因為外界不可以操作A的私有函數。9.8 如何在派生類中實現類的基本函數
基類的構造函數、析構函數、賦值函數都不能被派生類繼續。假如類之間存在繼續關系,在編寫上述基本函數時應注重以下事項:u 派生類的構造函數應在其初始化表里調用基類的構造函數。u 基類與派生類的析構函數應該為虛(即加virtual<span style='font-family:宋體;mso-ascii-font-family: "Times New Roman";mso-hansi-font-family:"Times New Ro


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产69久久精品成人看| 日韩欧美一区二区三区久久| 欧美成人精品三级在线观看| 亚洲国产精品成人va在线观看| 91av免费观看91av精品在线| 日韩欧美精品中文字幕| 国产日韩欧美自拍| 国产97在线观看| 亚洲精品久久久久中文字幕欢迎你| 红桃av永久久久| 伊人久久久久久久久久久久久| 91免费看视频.| 亚洲国产精品一区二区久| 亚洲深夜福利在线| 日韩有码在线电影| 国产精品私拍pans大尺度在线| 亚洲欧洲日产国产网站| 欧美影院在线播放| 日韩不卡中文字幕| 国产精品露脸av在线| 韩国视频理论视频久久| 日韩久久精品电影| 久久精品青青大伊人av| 在线观看日韩专区| 欧美极品欧美精品欧美视频| 91精品国产99久久久久久| 国产亚洲免费的视频看| 亚洲最新视频在线| 超碰精品一区二区三区乱码| 日韩国产高清污视频在线观看| 国产日韩换脸av一区在线观看| 91av视频在线播放| 国产成人久久精品| 国产成人精品综合久久久| 日韩亚洲国产中文字幕| 国产精品精品国产| 高清欧美性猛交| 亚洲自拍小视频| 欧美成人午夜剧场免费观看| 欧美电影《睫毛膏》| 亚洲第一页中文字幕| 欧美一级片免费在线| 欧美在线视频观看免费网站| 精品久久久久久国产91| 欧美日韩成人在线播放| 亚洲精品中文字幕有码专区| 91精品国产色综合久久不卡98口| 中国人与牲禽动交精品| 欧美精品在线免费观看| 疯狂蹂躏欧美一区二区精品| 国产日本欧美一区二区三区在线| 国产精品欧美日韩一区二区| 亚洲高清久久网| 中文字幕亚洲天堂| 亚洲欧洲日产国产网站| 亚洲国产欧美一区二区三区久久| 97久久精品人搡人人玩| 日韩精品免费视频| 国产日韩欧美91| 精品爽片免费看久久| 欧美日韩亚洲系列| 中文字幕欧美精品在线| 成人写真福利网| 一区二区三区回区在观看免费视频| 久久久久久网站| 国产欧美精品日韩| 欧美精品一区在线播放| 国产在线视频欧美| 国产精品久久77777| 日韩欧美极品在线观看| 自拍偷拍亚洲区| 欧美巨大黑人极品精男| 欧美福利视频在线| 91精品国产色综合久久不卡98口| 亚洲精品美女在线观看播放| 亚洲国产小视频在线观看| 日韩精品极品在线观看| 色综合久久久久久中文网| 孩xxxx性bbbb欧美| 伦伦影院午夜日韩欧美限制| 亚洲新声在线观看| 国内精品久久久久久久久| 91在线|亚洲| 欧美高清无遮挡| 国产精品久久9| 欧美大片免费观看在线观看网站推荐| 午夜精品福利在线观看| 国产热re99久久6国产精品| 欧美电影在线免费观看网站| 国产成人精品一区二区在线| 欧美老女人在线视频| 在线日韩精品视频| 在线观看国产精品91| 欧美区在线播放| 亚洲人成电影网站色| 日本中文字幕久久看| 成人精品aaaa网站| 欧美成在线视频| 欧美日韩中文字幕在线视频| 超薄丝袜一区二区| 国产91亚洲精品| 亚洲国产精品人人爽夜夜爽| 在线日韩第一页| 国产精品吹潮在线观看| 欧美激情网站在线观看| 亚洲成年网站在线观看| 中文字幕精品久久久久| 欧美日韩中文字幕| 91精品中文在线| 亚洲精品久久久久中文字幕欢迎你| 日韩高清免费观看| 欧美日韩一区二区在线| 国产成人av在线播放| 欧美视频在线视频| 欧美日韩aaaa| 欧美午夜www高清视频| 国产一区二区三区丝袜| 日韩国产一区三区| 精品日韩美女的视频高清| 国产精品入口福利| 欧美激情在线有限公司| 91天堂在线观看| 亚洲成人网在线观看| 欧美国产第二页| 欧美另类在线播放| 亚洲欧美一区二区三区在线| 欧美极品少妇全裸体| 日韩大陆毛片av| 日本久久久久久久久久久| 欧美成人黑人xx视频免费观看| 久久中文字幕一区| 国产午夜精品美女视频明星a级| 欧美精品在线免费播放| 中国人与牲禽动交精品| 色综合色综合久久综合频道88| 日韩在线观看免费网站| 亚洲欧美国产精品专区久久| 欧美激情第6页| 久久在线精品视频| 精品综合久久久久久97| 欧美高跟鞋交xxxxxhd| 日韩在线观看av| 日韩成人av一区| 久久精品国产成人精品| 色综合久久88| 亚洲精品中文字幕av| 国产激情久久久| 岛国av一区二区在线在线观看| 亚洲 日韩 国产第一| 亚洲人成电影网站色| 亚洲国产精品资源| 亚洲性线免费观看视频成熟| 国产手机视频精品| 久久久久久久久久久免费| 国产一区二区三区毛片| 日韩精品久久久久久久玫瑰园| 精品久久久久久久久久久| 欧美日韩在线另类| 精品小视频在线| 日韩欧美亚洲范冰冰与中字| 国产香蕉一区二区三区在线视频| 91成人精品网站| 欧美刺激性大交免费视频| 亚洲免费精彩视频|