C++特殊類設(shè)計(jì)及類型轉(zhuǎn)換舉例詳解
一、設(shè)計(jì)一個(gè)不能被拷貝的類
拷貝只會(huì)發(fā)生在兩個(gè)場(chǎng)景中:拷貝構(gòu)造函數(shù)以及賦值運(yùn)算符重載,因此想要讓一個(gè)類禁止拷貝, 只需讓該類不能調(diào)用拷貝構(gòu)造函數(shù)以及賦值運(yùn)算符重載即可。
C++98:
將拷貝構(gòu)造函數(shù)與賦值運(yùn)算符重載只聲明不定義,并且將其訪問(wèn)權(quán)限設(shè)置為私有即可。
原因:
- 設(shè)置成私有:如果只聲明沒(méi)有設(shè)置成private,用戶自己如果在類外定義了,就可以不被禁止拷貝了
- 只聲明不定義:不定義是因?yàn)樵摵瘮?shù)根本不會(huì)調(diào)用,定義了其實(shí)也沒(méi)有什么意義,不寫反而還簡(jiǎn)單,而且如果定義了就不會(huì)防止成員函數(shù)內(nèi)部拷貝了。
示例代碼:
class CopyBan { private: CopyBan(const CopyBan&); CopyBan& operator=(const CopyBan&); //... };
C++11:
C++11擴(kuò)展delete的用法,delete除了釋放new申請(qǐng)的資源外,如果在默認(rèn)成員函數(shù)后跟上
=delete,表示讓編譯器刪除掉該默認(rèn)成員函數(shù)。
示例代碼:
class CopyBan { // ... CopyBan(const CopyBan&) = delete; CopyBan& operator=(const CopyBan&) = delete; //... };
二、設(shè)計(jì)一個(gè)只能在堆上創(chuàng)建對(duì)象的類
實(shí)現(xiàn)方式:
1. 將類的構(gòu)造函數(shù)私有,拷貝構(gòu)造聲明成私有。防止別人調(diào)用拷貝在棧上生成對(duì)象。
2. 提供一個(gè)靜態(tài)的成員函數(shù),在該靜態(tài)成員函數(shù)中完成堆對(duì)象的創(chuàng)建
方法一:
class HeapOnly { public: //提供在堆上創(chuàng)建對(duì)象的方法 static HeapOnly* CreateObj() { return new HeapOnly; } //將拷貝構(gòu)造和賦值重載也禁止 HeapOnly(const HeapOnly&) = delete; HeapOnly& operator=(const HeapOnly&) = delete; private: //將構(gòu)造私有,防止外界直接創(chuàng)建對(duì)象 HeapOnly() {} }; int main() { //靜態(tài)區(qū)上創(chuàng)建對(duì)象 //static HeapOnly hp0; // 棧上創(chuàng)建對(duì)象 //HeapOnly hp1; //堆上創(chuàng)建對(duì)象 //HeapOnly* hp2 = new HeapOnly; HeapOnly* hp3 = HeapOnly::CreateObj(); //要防止別人通過(guò)這種方式在棧上創(chuàng)建對(duì)象 //通過(guò)禁止拷貝構(gòu)造來(lái)防止這種方式 //HeapOnly hp4(*hp3); //手動(dòng)釋放堆上的資源 delete hp3; return 0; }
解釋:上面代碼是通過(guò)私有構(gòu)造函數(shù)的方式來(lái)阻止外界自己創(chuàng)建對(duì)象,并提供一個(gè)在堆上創(chuàng)建對(duì)象的方法,使得外界只能在堆上創(chuàng)建對(duì)象,將拷貝構(gòu)造和賦值重載禁止是防止別人像圖中那樣通過(guò)這兩個(gè)方法在棧上創(chuàng)建對(duì)象。
方法二:
class HeapOnly { public: void Destroy() { delete this; } private: //析構(gòu)函數(shù)私有化 ~HeapOnly() {} }; int main() { //static HeapOnly hp0; //HeapOnly hp1; HeapOnly* hp2 = new HeapOnly; //delete hp2; hp2->Destroy(); return 0; }
解釋:該方法是通過(guò)私有析構(gòu)函數(shù)的方式使外界無(wú)法自動(dòng)調(diào)用析構(gòu)函數(shù),進(jìn)而無(wú)法創(chuàng)建對(duì)象,只能在堆上創(chuàng)建對(duì)象,因?yàn)樵诙焉仙暾?qǐng)的空間需要自己主動(dòng)釋放,不會(huì)自動(dòng)調(diào)用析構(gòu)。這種實(shí)現(xiàn)的方法無(wú)需禁止拷貝構(gòu)造和賦值重載,因?yàn)橥ㄟ^(guò)這兩種方式創(chuàng)建出來(lái)的棧上的對(duì)象仍會(huì)因?yàn)闊o(wú)法調(diào)用析構(gòu)而無(wú)法創(chuàng)建。
三、設(shè)計(jì)一個(gè)只能在棧上創(chuàng)建對(duì)象的類
方法一:同上將構(gòu)造函數(shù)私有化,然后設(shè)計(jì)靜態(tài)方法創(chuàng)建對(duì)象返回,并禁止掉重載的new和delete。
class StackOnly { public: static StackOnly CreateObj() { return StackOnly(); } //StackOnly(const StackOnly& s) = delete; void* operator new(size_t size) = delete; void operator delete(void* p) = delete; private: StackOnly() :_a(0) {} private: int _a; }; int main() { //static StackOnly s1; //StackOnly s2; //StackOnly* s3 = new StackOnly; StackOnly s4 = StackOnly::CreateObj(); //StackOnly* s5 = new StackOnly(s4); static StackOnly s6(s4); return 0; }
解釋:私有構(gòu)造函數(shù),并提供創(chuàng)建對(duì)象的方法,這樣外界無(wú)法自己創(chuàng)建對(duì)象,只能使用提供的方法在棧上創(chuàng)建對(duì)象。但如果只是這樣外界可以通過(guò)拷貝構(gòu)造在堆上或在靜態(tài)區(qū)創(chuàng)建對(duì)象,可我們不能禁止掉拷貝構(gòu)造,因?yàn)樵跅I蟿?chuàng)建對(duì)象并返回,會(huì)用到拷貝構(gòu)造,如上述代碼中s4接收返回的棧上的對(duì)象就是將棧上的對(duì)象拷貝給s4的,所以我們重載new和delete,C++中如果我們重載了這兩個(gè)方法,那么我們調(diào)用這兩個(gè)方法時(shí)會(huì)優(yōu)先調(diào)用我們自己的而不是庫(kù)的,我們?cè)賹⑦@兩個(gè)方法禁止,這樣就阻止別人在堆上創(chuàng)建對(duì)象了。但是在靜態(tài)區(qū)禁止不了。
方法二:同上將構(gòu)造函數(shù)私有化,然后設(shè)計(jì)靜態(tài)方法創(chuàng)建對(duì)象返回,并禁止掉拷貝構(gòu)造,但提供移動(dòng)構(gòu)造。
class StackOnly { public: static StackOnly CreateObj() { return StackOnly(); } StackOnly(const StackOnly&& s) { //...... } StackOnly(const StackOnly& s) = delete; private: StackOnly() :_a(0) {} private: int _a; }; int main() { StackOnly s4 = StackOnly::CreateObj(); //StackOnly* s5 = new StackOnly(s4); //static StackOnly s6(s4); //這種方式禁止不掉 StackOnly* s5 = new StackOnly(move(s4)); static StackOnly s6(move(s4)); return 0; }
解釋:提供的在棧上創(chuàng)建對(duì)象的方法返回的是匿名對(duì)象,是右值,可以通過(guò)移動(dòng)構(gòu)造賦值出去,這樣外界用事先創(chuàng)建好的對(duì)象再通過(guò)拷貝的方式在堆上或者在靜態(tài)區(qū)創(chuàng)建對(duì)象就創(chuàng)建不了了,但如果有人將左值move成右值再去拷貝,那就阻止不了了。
四、設(shè)計(jì)一個(gè)不能被繼承的類
C++98方式:構(gòu)造函數(shù)私有化,派生類中調(diào)不到基類的構(gòu)造函數(shù)。則無(wú)法繼承
示例代碼:
// C++98中構(gòu)造函數(shù)私有化,派生類中調(diào)不到基類的構(gòu)造函數(shù)。則無(wú)法繼承 class NonInherit { public: static NonInherit GetInstance() { return NonInherit(); } private: NonInherit() {} };
C++11方法:?nal關(guān)鍵字,?nal修飾類,表示該類不能被繼承。
示例代碼:
class A final { // .... };
五、設(shè)計(jì)一個(gè)只能創(chuàng)建一個(gè)對(duì)象的類(單例模式)
設(shè)計(jì)模式:
設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過(guò)分類的、代碼設(shè)計(jì)經(jīng)驗(yàn)的 總結(jié)。為什么會(huì)產(chǎn)生設(shè)計(jì)模式這樣的東西呢?就像人類歷史發(fā)展會(huì)產(chǎn)生兵法。最開(kāi)始部落之間打 仗時(shí)都是人拼人的對(duì)砍。后來(lái)春秋戰(zhàn)國(guó)時(shí)期,七國(guó)之間經(jīng)常打仗,就發(fā)現(xiàn)打仗也是有套路的,后 來(lái)孫子就總結(jié)出了《孫子兵法》。孫子兵法也是類似。
使用設(shè)計(jì)模式的目的:為了代碼可重用性、讓代碼更容易被他人理解、保證代碼可靠性。 設(shè)計(jì)模 式使代碼編寫真正工程化;設(shè)計(jì)模式是軟件工程的基石脈絡(luò),如同大廈的結(jié)構(gòu)一樣。
單例模式:
一個(gè)類只能創(chuàng)建一個(gè)對(duì)象,即單例模式,該模式可以保證系統(tǒng)中該類只有一個(gè)實(shí)例,并提供一個(gè) 訪問(wèn)它的全局訪問(wèn)點(diǎn),該實(shí)例被所有程序模塊共享。比如在某個(gè)服務(wù)器程序中,該服務(wù)器的配置 信息存放在一個(gè)文件中,這些配置數(shù)據(jù)由一個(gè)單例對(duì)象統(tǒng)一讀取,然后服務(wù)進(jìn)程中的其他對(duì)象再 通過(guò)這個(gè)單例對(duì)象獲取這些配置信息,這種方式簡(jiǎn)化了在復(fù)雜環(huán)境下的配置管理。
單例模式有兩種實(shí)現(xiàn)模式:
- 餓漢模式
就是說(shuō)不管你將來(lái)用不用,程序啟動(dòng)時(shí)就創(chuàng)建一個(gè)唯一的實(shí)例對(duì)象。
// 餓漢模式 // 1、多個(gè)餓漢模式的單例,某個(gè)對(duì)象初始化內(nèi)容較多(讀文件),會(huì)導(dǎo)致程序啟動(dòng)慢 // 2、A和B兩個(gè)餓漢,對(duì)象初始化存在依賴關(guān)系,要求A先初始化,B再初始化,餓漢無(wú)法保證 class InfoMgr { public: static InfoMgr& GetInstance() { return _ins; } void Print() { cout << _ip << endl; cout << _port << endl; cout << _buffSize << endl; } private: InfoMgr(const InfoMgr&) = delete; InfoMgr& operator=(const InfoMgr&) = delete; InfoMgr() { cout << "InfoMgr()" << endl; } private: string _ip = "127.0.0.1"; int _port = 80; size_t _buffSize = 1024 * 1024; //... static InfoMgr _ins; }; InfoMgr InfoMgr::_ins; int main() { InfoMgr::GetInstance().Print(); //InfoMgr copy(InfoMgr::GetInstance()); return 0; }
解釋:首先單例模式只允許一個(gè)類創(chuàng)建一個(gè)對(duì)象,所以還是將構(gòu)造函數(shù),拷貝構(gòu)造,賦值重載全部禁掉,防止外界自己創(chuàng)建對(duì)象,然后再類里面添加一個(gè)私有的靜態(tài)的類的對(duì)象,這個(gè)對(duì)象因?yàn)槭庆o態(tài)的,不會(huì)存在類中,存在靜態(tài)區(qū),所以不會(huì)造成類里面有類對(duì)象,類對(duì)象里面又有類對(duì)象這種無(wú)窮套娃的問(wèn)題,這里將該類的對(duì)象放到類中是為了讓它受到類域的限制,不讓外界隨意訪問(wèn),其實(shí)就相當(dāng)于靜態(tài)的全局變量(但被類域限制著),當(dāng)程序一啟動(dòng),這個(gè)對(duì)象就會(huì)被創(chuàng)建,我們?cè)偬峁┮粋€(gè)方法供外界獲取這個(gè)對(duì)象即可。
注意點(diǎn):首先,多個(gè)餓漢模式的單例,某個(gè)對(duì)象初始化內(nèi)容較多(如需要讀文件),會(huì)導(dǎo)致程序啟動(dòng)慢。其次,A和B兩個(gè)餓漢,對(duì)象初始化存在依賴關(guān)系,要求A先初始化,B再初始化,餓漢無(wú)法保證,因?yàn)槎际侨肿兞?,誰(shuí)先初始化無(wú)法保證。
如果這個(gè)單例對(duì)象在多線程高并發(fā)環(huán)境下頻繁使用,性能要求較高,那么顯然使用餓漢模式來(lái)避 免資源競(jìng)爭(zhēng),提高響應(yīng)速度更好。
- 懶漢模式
如果單例對(duì)象構(gòu)造十分耗時(shí)或者占用很多資源,比如加載插件啊, 初始化網(wǎng)絡(luò)連接啊,讀取文件啊等等,而有可能該對(duì)象程序運(yùn)行時(shí)不會(huì)用到,那么也要在程序一開(kāi)始就進(jìn)行初始化, 就會(huì)導(dǎo)致程序啟動(dòng)時(shí)非常的緩慢。 所以這種情況使用懶漢模式(延遲加載)更好。
// 懶漢模式 class InfoMgr { public: static InfoMgr& GetInstance() { // 第一次調(diào)用時(shí)創(chuàng)建單例對(duì)象 // 有線程安全的風(fēng)險(xiǎn) if (_pins == nullptr) { _pins = new InfoMgr; } return *_pins; } void Print() { cout << _ip << endl; cout << _port << endl; cout << _buffSize << endl; } static void DelInstance() { delete _pins; _pins = nullptr; } private: InfoMgr(const InfoMgr&) = delete; InfoMgr& operator=(const InfoMgr&) = delete; InfoMgr() { cout << "InfoMgr()" << endl; } private: string _ip = "127.0.0.1"; int _port = 80; size_t _buffSize = 1024 * 1024; //... static InfoMgr* _pins; }; InfoMgr* InfoMgr::_pins = nullptr; int main() { InfoMgr::GetInstance().Print(); InfoMgr::GetInstance().Print(); return 0; }
解釋:和餓漢思路類似,只不過(guò)懶漢不能一開(kāi)始就創(chuàng)建好這個(gè)唯一的類對(duì)象,只有當(dāng)需要的時(shí)候才會(huì)創(chuàng)建,不過(guò)這里懶漢的對(duì)象是在堆上創(chuàng)建的,可以對(duì)外提供一個(gè)釋放資源的方法。
注意點(diǎn):懶漢模式創(chuàng)建對(duì)象時(shí)有線程風(fēng)險(xiǎn)問(wèn)題,這里只是演示一下基本思路,所以代碼并沒(méi)有做的非常嚴(yán)謹(jǐn),如果想解決這個(gè)問(wèn)題可以加鎖。
方法二:(此方法適用C++11之后)
class InfoMgr { public: static InfoMgr& GetInstance() { // 第一次調(diào)用時(shí)創(chuàng)建單例對(duì)象 // C++11之后 static InfoMgr ins; return ins; } void Print() { cout << _ip << endl; cout << _port << endl; cout << _buffSize << endl; } private: InfoMgr(const InfoMgr&) = delete; InfoMgr& operator=(const InfoMgr&) = delete; InfoMgr() { cout << "InfoMgr()" << endl; } private: string _ip = "127.0.0.1"; int _port = 80; size_t _buffSize = 1024 * 1024; //... }; int main() { InfoMgr::GetInstance().Print(); InfoMgr::GetInstance().Print(); cout << &InfoMgr::GetInstance() << endl; cout << &InfoMgr::GetInstance() << endl; return 0; }
解釋:這里提供一個(gè)局部的靜態(tài)變量,而不是全局的,這樣只有第一次需要的時(shí)候才會(huì)創(chuàng)建,后面因?yàn)槭庆o態(tài)變量,再去申請(qǐng)對(duì)象時(shí)使用的是前面創(chuàng)建好的靜態(tài)對(duì)象。
六、C語(yǔ)言中的類型轉(zhuǎn)換
在C語(yǔ)言中,如果賦值運(yùn)算符左右兩側(cè)類型不同,或者形參與實(shí)參類型不匹配,或者返回值類型與 接收返回值類型不一致時(shí),就需要發(fā)生類型轉(zhuǎn)化,C語(yǔ)言中總共有兩種形式的類型轉(zhuǎn)換:隱式類型 轉(zhuǎn)換和顯式類型轉(zhuǎn)換。
- 隱式類型轉(zhuǎn)化:編譯器在編譯階段自動(dòng)進(jìn)行,能轉(zhuǎn)就轉(zhuǎn),不能轉(zhuǎn)就編譯失敗
- 顯式類型轉(zhuǎn)化:需要用戶自己處理
缺陷:轉(zhuǎn)換的可視性比較差,所有的轉(zhuǎn)換形式都是以一種相同形式書寫,難以跟蹤錯(cuò)誤的轉(zhuǎn)換。
七、C++中的三類類型轉(zhuǎn)換
內(nèi)置類型之間:
- 隱式類型轉(zhuǎn)換 整形之間/整形和浮點(diǎn)數(shù)之間
- 顯示類型的轉(zhuǎn)換 指針和整形、指針之間
示例代碼:
// a、內(nèi)置類型之間 // 1、隱式類型轉(zhuǎn)換 整形之間/整形和浮點(diǎn)數(shù)之間 // 2、顯示類型的轉(zhuǎn)換 指針和整形、指針之間 int main() { int i = 1; // 隱式類型轉(zhuǎn)換 double d = i; printf("%d, %.2f\n", i, d); int* p = &i; // 顯示的強(qiáng)制類型轉(zhuǎn)換 int address = (int)p; printf("%p, %d\n", p, address); return 0; }
內(nèi)置類型和自定義類型之間:
- 自定義類型 = 內(nèi)置類型 ->構(gòu)造函數(shù)支持
- 內(nèi)置類型 = 自定義類型 ->operator重載支持
示例代碼:
// b、內(nèi)置類型和自定義類型之間 // 1、自定義類型 = 內(nèi)置類型 ->構(gòu)造函數(shù)支持 // 2、內(nèi)置類型 = 自定義類型 class A { public: //explicit A(int a) //explicit關(guān)鍵字可以禁止隱式轉(zhuǎn)換,必須顯示轉(zhuǎn)換 A(int a) :_a1(a) ,_a2(a) {} A(int a1, int a2) :_a1(a1) , _a2(a2) {} //自定義類型 -> 內(nèi)置類型 // ()被仿函數(shù)占用了,不能用 // operator 類型實(shí)現(xiàn),有返回值,無(wú)返回類型 // 默認(rèn)返回類型就是要轉(zhuǎn)換的類型 //explicit operator int() operator int() { return _a1 + _a2; } private: int _a1 = 1; int _a2 = 1; }; int main() { //內(nèi)置類型轉(zhuǎn)換自定義類型 string s1 = "1111111"; A aa1 = 1; //A aa1 = (A)1; A aa2 = { 2,2 }; const A& aa3 = { 2,2 }; //自定義類型轉(zhuǎn)換內(nèi)置類型 int z = aa1.operator int(); //int x = (int)aa1; int x = aa1; int y = aa2; cout << x << endl; cout << y << endl; //庫(kù)里的shared_ptr提供了轉(zhuǎn)換為bool類型的重載函數(shù) std::shared_ptr<int> foo; std::shared_ptr<int> bar(new int(34)); //這里本質(zhì)是轉(zhuǎn)換為了bool類型 //if (foo.operator bool()) if (foo) std::cout << "foo points to " << *foo << '\n'; else std::cout << "foo is null\n"; if (bar) std::cout << "bar points to " << *bar << '\n'; else std::cout << "bar is null\n"; return 0; }
自定義類型和自定義類型之間:
- 對(duì)應(yīng)的構(gòu)造函數(shù)支持
示例代碼:
// c、自定義類型和自定義類型之間 -- 對(duì)應(yīng)的構(gòu)造函數(shù)支持 class A { public: A(int a) :_a1(a) , _a2(a) {} A(int a1, int a2) :_a1(a1) , _a2(a2) {} int get() const { return _a1 + _a2; } private: int _a1 = 1; int _a2 = 1; }; class B { public: B(int b) :_b1(b) {} B(const A& aa) :_b1(aa.get()) {} private: int _b1 = 1; }; #include"List.h" #include<list> int main() { A aa1(1); B bb1(2); bb1 = aa1; B& ref1= bb1; const B& ref2 = aa1; bit::list<int> lt = { 1,2,3,4 }; // 權(quán)限的縮小?權(quán)限縮小和放大,僅限于const的指針和引用 // 不是權(quán)限縮小,這里類型轉(zhuǎn)換 bit::list<int>::const_iterator cit = lt.begin(); while (cit != lt.end()) { cout << *cit << " "; ++cit; } cout << endl; return 0; }
解釋:上面代碼中涉及到一個(gè)從普通迭代器到const迭代器的轉(zhuǎn)換問(wèn)題,這不屬于權(quán)限縮小,權(quán)限問(wèn)題只有在指針和引用中存在,這里無(wú)法轉(zhuǎn)換是因?yàn)榈鞯膶?shí)現(xiàn)使用了模版,模版實(shí)例化后普通迭代器和const迭代器本就是不同的類型,所以這里是類型不同導(dǎo)致相互之間無(wú)法賦值,解決辦法如下圖。
解釋:我們需要再迭代器中增加一個(gè)方法,這個(gè)方法的形參必須是普通迭代器類型,當(dāng)該迭代器模版實(shí)例化為普通迭代器后,這個(gè)函數(shù)在普通迭代器中就是拷貝構(gòu)造,當(dāng)該迭代器模板實(shí)例化為const迭代器后,這個(gè)函數(shù)在const迭代器中就能夠?qū)魅氲钠胀ǖ鬓D(zhuǎn)換為const迭代器。
八、C++強(qiáng)制類型轉(zhuǎn)換
8.1、為什么C++需要四種類型轉(zhuǎn)換
C風(fēng)格的轉(zhuǎn)換格式很簡(jiǎn)單,但是是有不少缺點(diǎn)的:
- 隱式類型轉(zhuǎn)化有些情況下可能會(huì)出問(wèn)題:比如數(shù)據(jù)精度丟失
- 顯式類型轉(zhuǎn)換將所有情況混合在一起,代碼不夠清晰
因此C++提出了自己的類型轉(zhuǎn)化風(fēng)格,注意因?yàn)镃++要兼容C語(yǔ)言,所以C++中還可以使用C語(yǔ)言的 轉(zhuǎn)化風(fēng)格。標(biāo)準(zhǔn)C++為了加強(qiáng)類型轉(zhuǎn)換的可視性,引入了四種命名的強(qiáng)制類型轉(zhuǎn)換操作符:
- static_cast
- reinterpret_cast
- const_cast
- dynamic_cast
8.2、static_cast
static_cast用于非多態(tài)類型的轉(zhuǎn)換(靜態(tài)轉(zhuǎn)換),編譯器隱式執(zhí)行的任何類型轉(zhuǎn)換都可用。但它不能用于兩個(gè)不相關(guān)的類型進(jìn)行轉(zhuǎn)換。(即static_cast對(duì)應(yīng)隱式類型轉(zhuǎn)換)
示例代碼:
int main() { // 對(duì)應(yīng)隱式類型轉(zhuǎn)換 -- 數(shù)據(jù)的意義沒(méi)有改變 double d = 12.34; int a = static_cast<int>(d); cout << a << endl; return 0; }
8.3、reinterpret_cast
reinterpret_cast操作符通常為操作數(shù)的位模式提供較低層次的重新解釋,用于將一種類型轉(zhuǎn)換為另一種不同的類型。(即reinterpret_cast對(duì)應(yīng)強(qiáng)制類型轉(zhuǎn)換)
示例代碼:
int main() { // 對(duì)應(yīng)隱式類型轉(zhuǎn)換 -- 數(shù)據(jù)的意義沒(méi)有改變 double d = 12.34; int a = static_cast<int>(d); cout << a << endl; // 對(duì)應(yīng)強(qiáng)制類型轉(zhuǎn)換 -- 數(shù)據(jù)的意義已經(jīng)發(fā)生改變 int* p1 = reinterpret_cast<int*>(a);; return 0; }
8.4、const_cast
const_cast最常用的用途就是刪除變量的const屬性,方便賦值。
示例代碼:
int main() { // 對(duì)應(yīng)強(qiáng)制類型轉(zhuǎn)換中有風(fēng)險(xiǎn)的去掉const屬性 //volatile const int b = 2; const int b = 2; int* p2 = const_cast<int*>(&b); *p2 = 3; cout << b << endl; cout << *p2 << endl; return 0; }
解釋:上面代碼中存在一個(gè)問(wèn)題這里我們確實(shí)將b的值改變了,但是如果我們直接打印b的值會(huì)發(fā)現(xiàn)打印出來(lái)的值還是變化前的,這是因?yàn)榫幾g器可能直接將b當(dāng)做一個(gè)常量(2)輸出出來(lái)了,或者從寄存器中直接獲取的b的值,而沒(méi)有重新上內(nèi)存中獲取新的值,導(dǎo)致使用時(shí)還是舊值。我們可以通過(guò)關(guān)鍵字volatile解決這個(gè)問(wèn)題,被該關(guān)鍵字修飾后每次都會(huì)上內(nèi)存中去取值,確保拿到更新后的新值。
8.5、dynamic_cast
dynamic_cast用于將一個(gè)父類對(duì)象的指針/引用轉(zhuǎn)換為子類對(duì)象的指針或引用(動(dòng)態(tài)轉(zhuǎn)換)
- 向上轉(zhuǎn)型:子類對(duì)象指針/引用->父類指針/引用(不需要轉(zhuǎn)換,賦值兼容規(guī)則)
- 向下轉(zhuǎn)型:父類對(duì)象指針/引用->子類指針/引用(用dynamic_cast轉(zhuǎn)型是安全的)
注意:
- dynamic_cast只能用于父類含有虛函數(shù)的類。
- dynamic_cast會(huì)先檢查是否能轉(zhuǎn)換成功,能成功則轉(zhuǎn)換,不能則返回0(即空指針)。
示例代碼:
class A { public: virtual void f() {} int _a = 1; }; class B : public A { public: int _b = 2; }; void fun(A* pa) { // dynamic_cast會(huì)先檢查是否能轉(zhuǎn)換成功(指向子類對(duì)象),能成功則轉(zhuǎn)換, // (指向父類對(duì)象)不能則返回NULL // 指向父類轉(zhuǎn)換時(shí)有風(fēng)險(xiǎn)的,后續(xù)訪問(wèn)存在越界訪問(wèn)的風(fēng)險(xiǎn) // 指向子類轉(zhuǎn)換時(shí)安全 B* pb1 = dynamic_cast<B*>(pa); if (pb1) { cout << "pb1:" << pb1 << endl; cout << pb1->_a << endl; cout << pb1->_b << endl; pb1->_a++; pb1->_b++; cout << pb1->_a << endl; cout << pb1->_b << endl; } else { cout << "轉(zhuǎn)換失敗" << endl; } } int main() { A a; B b; fun(&a); fun(&b); return 0; }
解釋:子類轉(zhuǎn)父類沒(méi)有問(wèn)題,主要是父類轉(zhuǎn)子類,當(dāng)父類指針指向子類對(duì)象時(shí)可以轉(zhuǎn)換成功,當(dāng)父類指針指向父類對(duì)象時(shí)會(huì)轉(zhuǎn)換失敗。
注意:強(qiáng)制類型轉(zhuǎn)換關(guān)閉或掛起了正常的類型檢查,每次使用強(qiáng)制類型轉(zhuǎn)換前,程序員應(yīng)該仔細(xì)考慮是否還有其他不同的方法達(dá)到同一目的,如果非強(qiáng)制類型轉(zhuǎn)換不可,則應(yīng)限制強(qiáng)制轉(zhuǎn)換值的作用域,以減少發(fā)生錯(cuò)誤的機(jī)會(huì)。強(qiáng)烈建議:避免使用強(qiáng)制類型轉(zhuǎn)換
九、RTTI
RTTI:Run-time Type identi?cation的簡(jiǎn)稱,即:運(yùn)行時(shí)類型識(shí)別。
C++通過(guò)以下方式來(lái)支持RTTI:
- typeid運(yùn)算符
- dynamic_cast運(yùn)算符
- decltype
到此這篇關(guān)于C++特殊類設(shè)計(jì)及類型轉(zhuǎn)換的文章就介紹到這了,更多相關(guān)C++特殊類設(shè)計(jì)及類型轉(zhuǎn)換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實(shí)現(xiàn)分?jǐn)?shù)計(jì)算器
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)分?jǐn)?shù)計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06C語(yǔ)言位段(位域)機(jī)制結(jié)構(gòu)體的特殊實(shí)現(xiàn)及解析
這篇文章主要為大家介紹了C語(yǔ)言位段位域機(jī)制結(jié)構(gòu)體的特殊實(shí)現(xiàn)講解有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-02-02詳解用C語(yǔ)言實(shí)現(xiàn)三子棋游戲流程
三子棋是一種民間傳統(tǒng)游戲,又叫九宮棋、圈圈叉叉、一條龍、井字棋等。將正方形對(duì)角線連起來(lái),相對(duì)兩邊依次擺上三個(gè)雙方棋子,只要將自己的三個(gè)棋子走成一條線,對(duì)方就算輸了2021-11-11select函數(shù)實(shí)現(xiàn)高性能IO多路訪問(wèn)的關(guān)鍵示例深入解析
這篇文章主要為大家介紹了select函數(shù)實(shí)現(xiàn)高性能IO多路訪問(wèn)的關(guān)鍵示例深入解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09matlab遺傳算法求解車間調(diào)度問(wèn)題分析及實(shí)現(xiàn)源碼
這篇文章主要為大家介紹了matlab遺傳算法求解車間調(diào)度問(wèn)題解析,文中附含詳細(xì)實(shí)現(xiàn)源碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-02-02