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

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

初始化C++類成員和在你的MFC應(yīng)用中加入位置欄

2019-11-17 05:46:18
字體:
供稿:網(wǎng)友
       
問題

我的問題是關(guān)于初始化C++類成員的。我見過許多這樣的代碼(包括在你的欄目中也見到過):

CSomeClass::CSomeClass()

{

x=0;

y=1;

}

而在別的什么地方則寫成下面的樣子:

CSomeClass::CSomeClass() : x(0), y(1)

{

}

我的一些程序員朋友說第二種方法比較好,但他們都不知道為什么是這樣。你能告訴我這兩種類成員初始化方法的區(qū)別嗎?

回答

從技術(shù)上說,你的程序員朋友是對的,但是在大多數(shù)情況下,兩者實(shí)際上沒有區(qū)別。有兩個原因使得我們選擇第二種語法,它被稱為成員初始化列表:一個原因是必須的,另一個只是出于效率考慮。

讓我們先看一下第一個原因——必要性。設(shè)想你有一個類成員,它本身是一個類或者結(jié)構(gòu),而且只有一個帶一個參數(shù)的構(gòu)造函數(shù)。

class CMember {

public:

CMember(int x) { ... }

};

因?yàn)镃member有一個顯式聲明的構(gòu)造函數(shù),編譯器不產(chǎn)生一個缺省構(gòu)造函數(shù)(不帶參數(shù)),所以沒有一個整數(shù)就無法創(chuàng)建Cmember的一個實(shí)例。

CMember* pm = new CMember; // Error!!

CMember* pm = new CMember(2); // OK

假如Cmember是另一個類的成員,你怎樣初始化它呢?你必須使用成員初始化列表。

class CMyClass {

CMember m_member;

public:

CMyClass();

};

//必須使用成員初始化列表

CMyClass::CMyClass() : m_member(2)

{

???

}

沒有其它辦法將參數(shù)傳遞給m_member,假如成員是一個常量對象或者引用也是一樣。根據(jù)C++的規(guī)則,常量對象和引用不能被賦值,它們只能被初始化。

第二個原因是出于效率考慮,當(dāng)成員類具有一個缺省的構(gòu)造函數(shù)和一個賦值操作符時。MFC的Cstring提供了一個完美的例子。假定你有一個類CmyClass具有一個Cstring類型的成員m_str,你想把它初始化為"yada yada."。你有兩種選擇:

CMyClass::CMyClass() {

// 使用賦值操作符

// CString::Operator=(LPCTSTR);

m_str = _T("yada yada");

}

//使用類成員列表

// and constrUCtor CString::CString(LPCTSTR)

CMyClass::CMyClass() : m_str(_T("yada yada"))

{

}

在它們之間有什么不同嗎?是的。編譯器總是確保所有成員對象在構(gòu)造函數(shù)體執(zhí)行之前初始化,因此在第一個例子中編譯的代碼將調(diào)用CString::Cstring來初始化m_str,這在控制到達(dá)賦值語句前完成。在第二個例子中編譯器產(chǎn)生一個對CString:: CString(LPCTSTR)的調(diào)用并將"yada yada"傳遞給這個函數(shù)。結(jié)果是在第一個例子中調(diào)用了兩個Cstring函數(shù)(構(gòu)造函數(shù)和賦值操作符),而在第二個例子中只調(diào)用了一個函數(shù)。在Cstring的例子里這是無所謂的,因?yàn)槿笔?gòu)造函數(shù)是內(nèi)聯(lián)的,Cstring只是在需要時為字符串分配內(nèi)存(即,當(dāng)你實(shí)際賦值時)。但是,一般而言,重復(fù)的函數(shù)調(diào)用是浪費(fèi)資源的,尤其是當(dāng)構(gòu)造函數(shù)和賦值操作符分配內(nèi)存的時候。在一些大的類里面,你可能擁有一個構(gòu)造函數(shù)和一個賦值操作符都要調(diào)用同一個負(fù)責(zé)分配大量內(nèi)存空間的Init函數(shù)。在這種情況下,你必須使用初始化列表,以避免不要的分配兩次內(nèi)存。在內(nèi)部類型如ints或者longs或者其它沒有構(gòu)造函數(shù)的類型下,在初始化列表和在構(gòu)造函數(shù)體內(nèi)賦值這兩種方法沒有性能上的差別。不管用那一種方法,都只會有一次賦值發(fā)生。有些程序員說你應(yīng)該總是用初始化列表以保持良好習(xí)慣,但我從沒有發(fā)現(xiàn)根據(jù)需要在這兩種方法之間轉(zhuǎn)換有什么困難。在編程風(fēng)格上,我傾向于在主體中使用賦值,因?yàn)橛懈嗟目臻g用來格式化和添加注釋,你可以寫出這樣的語句:x=y=z=0;

或者memset(this,0,sizeof(this));

注重第二個片斷絕對是非面向?qū)ο蟮摹?br />
當(dāng)我考慮初始化列表的問題時,有一個希奇的特性我應(yīng)該警告你,它是關(guān)于C++初始化類成員的,它們是按照聲明的順序初始化的,而不是按照出現(xiàn)在初始化列表中的順序。

class CMyClass {

CMyClass(int x, int y);

int m_x;

int m_y;

};

CMyClass::CMyClass(int i) : m_y(i), m_x(m_y)

{

}

你可能以為上面的代碼將會首先做m_y=I,然后做m_x=m_y,最后它們有相同的值。但是編譯器先初始化m_x,然后是m_y,,因?yàn)樗鼈兪前催@樣的順序聲明的。結(jié)果是m_x將有一個不可猜測的值。我的例子設(shè)計來說明這一點(diǎn),然而這種bug會更加自然的出現(xiàn)。有兩種方法避免它,一個是總是按照你希望它們被初始化的順序聲明成員,第二個是,假如你決定使用初始化列表,總是按照它們聲明的順序羅列這些成員。這將有助于消除混淆。

問題

我剛剛在幾臺機(jī)器上安裝了Windows? 2000 Release Candidate 1,不知道怎樣在我的MFC應(yīng)用中得到具有新的Outlook風(fēng)格欄目的Open對話框(見圖1)。


初始化C++類成員和在你的MFC應(yīng)用中加入位置欄
Figure 1 The New Open Dialog


我能否只設(shè)置一個標(biāo)志,或者我是否需要一個新的頭文件和一個新的公共對話框的DDL?我注重到一些舊的應(yīng)用程序如Notepad似乎可以得到新的Open對話框而無須重新編譯,但它們不是MFC應(yīng)用。理想情況,我希望在Windows 9x 和Windows NT?下得到一個使用舊對話框的應(yīng)用,而在windows 2000下使用新的對話框。

Warren Stevens

回答

這個問題恐怕沒有令你興奮的答復(fù)。Windows 2000新的Open對話框是用一個新版本的commdlg.dll實(shí)現(xiàn)的,它在邊上包含“Places”欄目。顯示它的函數(shù)是GetOpenFileName,與在Windows 9x 和Windows NT?下使用的相同。然而,GetOpenFileName現(xiàn)在使用一個新版本的OPENFILENAME,這是一個在你的應(yīng)用和對話框之間傳遞信息的結(jié)構(gòu)。新的結(jié)構(gòu)有一些額外的成員:

typedef struct tagOFN {

DWord lStructSize; // 很重要!

???

// 正想你總是知道并且喜歡的那樣

#if (_WIN32_WINNT >= 0x0500)

void* pvReserved;

DWORD dwReserved;

DWORD FlagsEx;

#endif // (_WIN32_WINNT >= 0x0500)

} OPENFILENAME, *LPOPENFILENAME;

對,是這樣。Windows 2000是Windows的第5個版本,用16進(jìn)制表示是0x500。假如你用_WIN32_WINNT = 0x0500編譯程序,OPENFILENAME就會得到3個新成員。前兩個是保留的,第三個標(biāo)志域,F(xiàn)lagsEx,有一個新的OFN_EX_NOPLACESBAR欄目,它屏蔽了Places欄目。Windows——或者更準(zhǔn)確的說,commdlg.dll——使用OPENFILENAME第一個成員lStructSize來決定顯示那個對話框,假如lStructSize是76(舊的大小),Windows就運(yùn)行舊的對話框;假如是76+3′4=88(新的大小),它就運(yùn)行新的對話框——這是友好的Redmondtonians最初告訴我的。在我的研究中間,我發(fā)現(xiàn)這并不是完整的圖畫。

但是在我具體說明之前,先讓我們走馬觀花的看一下MFC,討論另外一個問題。在MFC應(yīng)用中,你并不經(jīng)常直接調(diào)用GetOpenFileName,而是使用CfileDialog——或者,框架使用CfileDialog。當(dāng)用戶調(diào)用File Open,控制稀里嘩啦的一路經(jīng)過CWinApp::OnFileOpen和幾個其它的函數(shù),最終到達(dá)CDocManager::DoPRomptFileName,這個函數(shù)創(chuàng)建一個CfileDialog。CfileDialog具有一個OPENFILENAME結(jié)構(gòu)的數(shù)據(jù)成員:

class CFileDialog : public CCommonDialog {

OPENFILENAME m_ofn;

???

};

這個結(jié)構(gòu)的大小是當(dāng)友好的Redmondtonians 編譯MFC42.DLL 時OPENFILENAME的大小;換句話說,舊的大小。而且,假如你正在進(jìn)行一個靜態(tài)連接,MFC代碼在MFC42.DLL或NAFXCW.LIB里是凍結(jié)的,你不能僅僅設(shè)置m_ofn.lStructSize為新的大小,因?yàn)镃fileDialog除m_ofn外還有其它數(shù)據(jù)成員,它們將被新的OPENFILENAME的成員覆蓋。不再耽擱了,我開始使用極端的方法避開這個問題。我考慮可以做些什么,類似于MFC中使用CpropertyPage那樣。PROPSHEETPAGE和PROPSHEETHEADER的大小在從Windows 95到Windows 98的過程中的某處增加了,這是為了支持wizard風(fēng)格的頁面。為了支持新膨脹的結(jié)構(gòu),MFC提供了CpropertyPageEx和CpropertySheetEx。最初的類(不帶Ex的)仍然使用舊的結(jié)構(gòu);而新的類使用新的結(jié)構(gòu)。這是一種雜湊,尤其是因?yàn)閍fxdlgs.h具有自己的舊的結(jié)構(gòu)的定義(AFX_ OLDPROPSHEETPAGE和AFX_OLDPROPSHEETHEADER),但是這樣卻行得通。

我對CfileDialog做了同樣的事情。首先我派生一個新的CfileDialogEx類,它帶有一個新的m_ofn,包含著新的OPENFILENAMEEX結(jié)構(gòu),我模擬0x500版本加以定義。我加入這3個新的成員并且使用m_ofn.重寫了CfileDialog函數(shù)。不幸的是,因?yàn)榇蠖鄶?shù)的MFC代碼是固定的,沒有任何虛擬功能,這就意味著復(fù)制原來的整個類。但是我已經(jīng)下了決心。

在我認(rèn)為已經(jīng)找到了m_ofn出現(xiàn)的所有地方以后,我重寫了它,高興奮興的編譯了我的代碼(在Windows 98上),然后運(yùn)行——結(jié)果發(fā)現(xiàn)我得到的仍是舊風(fēng)格的對話框。而起,有一個謎團(tuán)我忘了考慮:假如Windows 2000使用lStructSize來決定運(yùn)行那個Open對話框,為什么Windows 98的應(yīng)用程序(象Notepad)在Windows 2000下運(yùn)行時得到了新的對話框呢?啊!隨Windows 98出現(xiàn)的NOTEPAD.EXE顯然在lStructSize 上有舊的OPENFILENAME的大小,因此Windows 2000必須使用lStructSize之外的某種東西來決定運(yùn)行那個對話框。

到這里,我決定回過頭去重新考慮問題。我將MFC放到一邊,嘗試直接調(diào)用GetOpenFileName。我重寫了我的應(yīng)用程序的OnFileOpen:

void CMyApp::OnFileOpen()

{

OPENFILENAME ofn; // older version

memset(&ofn, 0, sizeof(ofn));

ofn.lStructSize = sizeof(ofn);

int nResult = ::GetOpenFileName(&ofn);

}

因?yàn)樨灤┍揪毩?xí),我使用了舊的0x400版本的SDK文件(因?yàn)槲蚁M麘?yīng)用程序既可以在Windows 2000上運(yùn)行,也可以在Windows 9x上運(yùn)行),ofn.lStructSize就有了舊的大小。當(dāng)我編譯并運(yùn)行時,我在Windows 98上得到了舊的對話框,而在Windows 2000上得到了新的對話框——就象Notepad一樣!因此可以說,實(shí)際上,Windows 2000足夠精明的為舊的應(yīng)用使用新的對話框——但不是舊的MFC應(yīng)用。它毫無意義。一個MFC應(yīng)用的不同之處在哪里呢?

一定是標(biāo)志。為了發(fā)現(xiàn)真相,我在OPENFILENAME結(jié)構(gòu)中手工添加了不同的標(biāo)志,直到我的程序產(chǎn)生了不帶Places欄目的舊風(fēng)格的窗口。你瞧,當(dāng)我為ofn.Flags加入標(biāo)志OFN_ENABLEHOOK時,我的對話框回到了從前。我將此希奇的行為報告給Redmondtonians,他們證實(shí)“這種行為是設(shè)計的”。

那么,Windows 2000判定OPENFILENAME的大小以及對話框是否使用hook過程。假如OPENFILENAME有舊的大小,Windows 2000使用OFN_ENABLEHOOK來決定運(yùn)行哪個對話框。假如OPENFILENAME使用hook過程(或者設(shè)置了ORN_ENABLETEMPLATE),Windows 2000按照舊的風(fēng)格顯示對話框;否則,顯示新的對話框。這就解釋了為什么MFC應(yīng)用顯示了舊的對話框——因?yàn)镃fileDialog,就象所有MFC的公用對話框一樣,使用hook過程。這是MFC將公用對話框嵌入它的消息映射系統(tǒng)的方式,它用同樣的方式使用AfxWndProc嵌入其它的窗口。

現(xiàn)在你看到了結(jié)果:你給迷惑了。在一個MFC應(yīng)用中得到新風(fēng)格的對話框的唯一的辦法是完全繞開CfileDialog,直接調(diào)用GetOpenFileName,并且不使用hook過程。即使你用新的SDK文件和WINVER = 0x500編譯你的應(yīng)用,你仍不能使用MFC,因?yàn)樗膸旌虳LLs有舊的大小。你可以使用WINVER = 0x500自行編譯MFC,但是誰知道那將怎么樣呢?而且假如你真的BUILD了新的MFC,你將不得不將新的DLL和你的應(yīng)用一塊發(fā)布,給它起個不同的名字,因?yàn)槟愕男碌腗FC DLL肯定不會與其它的希望使用CFileDialog 和其它結(jié)構(gòu)的舊的大小的MFC應(yīng)用兼容。或者,你可以重新生成MFC,然后靜態(tài)的連接,這將極大的增加你的可執(zhí)行文件的大小,假如你不重新實(shí)現(xiàn)Windows的話。

到截稿時間為止,我從Redmond聽說在即將發(fā)布的新的Visual C++?中,將會有一個不同名字的新版本的MFC DLL。新版本的MFC將支持Windows 2000中出現(xiàn)的新的UI和APIs。

同時,我將圖2留給你研究,它取自最新的SDK文檔,"Header File Conventions",提供了Windows版本問題的鑰匙。它向你說明,為了達(dá)到某個版本的Windows和Microsoft? Internet EXPlorer的目標(biāo)你必須使用SDK頭文件定義哪些宏。努力別跌倒在生活的快車道上。

Paul DiLascia是Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992)的作者,還是一位自由顧問和and 知名著書者。

他的聯(lián)系方法:cppqa@microsoft.com 或者 http://www.dilascia.com.




發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 南安市| 石嘴山市| 贵港市| 普兰店市| 安仁县| 天峨县| 沧源| 嘉禾县| 历史| 崇礼县| 闽侯县| 宜宾市| 舟曲县| 新津县| 太原市| 吕梁市| 固原市| 孟州市| 伊金霍洛旗| 洱源县| 安塞县| 扶余县| 永昌县| 南京市| 瓮安县| 通州区| 黑山县| 许昌县| 茂名市| 建始县| 湖口县| 绥棱县| 邵阳市| 霍邱县| 中西区| 偃师市| 佛山市| 玛沁县| 法库县| 承德市| 富川|