C++特殊類設(shè)計與類型轉(zhuǎn)換詳細(xì)代碼示例
常見的特殊類的設(shè)計
1.請設(shè)計一個類,不能被拷貝
拷貝只會發(fā)生在兩個場景中:拷貝構(gòu)造函數(shù)以及賦值運(yùn)算符重載,因此想要讓一個類禁止拷貝,只需要讓該類不能調(diào)用拷貝構(gòu)造函數(shù)以及賦值運(yùn)算符重載即可。
C++98寫法:
將拷貝構(gòu)造函數(shù)與賦值運(yùn)算符重載只聲明不定義,并且將其訪問權(quán)限設(shè)置為私有即可。
class CopyBan
{
//...
private:
CopyBan(const CopyBan& c);
CopyBan& operator=(const CopyBan& c);
//...
};原因:1.設(shè)置成私有:如果只聲明沒有設(shè)置成private,用戶自己如果在類外定義了,就不能禁止拷貝了。
2.只聲明不定義:不定義是因為該函數(shù)根本不會調(diào)用,定義了其實也沒有什么意義,不寫反而還簡單,而且如果定義了就不會防止成員函數(shù)內(nèi)部拷貝了。
C++11寫法:
C++11擴(kuò)展delete的用法,delete除了釋放new申請的資源外,如果在默認(rèn)成員函數(shù)后面跟上=delete,表示讓編譯器刪除掉該默認(rèn)成員函數(shù)。
class CopyBan
{
//...
CopyBan(const CopyBan& c)=delete;
CopyBan& operator=(const CopyBan& c)=delete;
//...
};2.請設(shè)計一個類,只能在堆上創(chuàng)建對象
實現(xiàn)方式:
1.將類的構(gòu)造函數(shù)設(shè)置為私有,拷貝構(gòu)造聲明成私有。防止別人調(diào)用拷貝在棧上生成對象。
2.提供一個靜態(tài)的成員函數(shù),在該靜態(tài)成員函數(shù)中完成堆對象的創(chuàng)建。
class HeapOnly
{
public:
static HeapOnly* CreateHeapObj()
{
return new HeapOnly;
}
private:
HeapOnly()
{ }
//C++98方式
//1.只聲明,不實現(xiàn)。因為實現(xiàn)可能會很麻煩,而你本身并不需要
//2.聲明成私有
HeapOnly& operator=(const HeapOnly&);
//C++11方式
HeapOnly& operator=(const HeapOnly&) = delete;
};3.請設(shè)計一個類,只能在棧上創(chuàng)建對象
方法:同上述將構(gòu)造函數(shù)私有化,然后設(shè)計靜態(tài)方法創(chuàng)建對象返回即可。
class StackOnly
{
public:
static StackOnly CreateStackObj()
{
return StackOnly();
}
//禁掉operator new可以把下面用new 調(diào)用拷貝構(gòu)造申請對象給禁掉
//StackOnly obj = StackOnly::CreateStackObj();
//StackOnly* ptr = new StackOnly(obj);
void* operator new(size_t size) = delete;
void operator delete(void* p) = delete;
private:
StackOnly()
:_a(0)
{ }
int _a;
};4. 請設(shè)計一個類,不能被繼承
C++98方式:把類的構(gòu)造函數(shù)私有化,派生類調(diào)不到基類的構(gòu)造函數(shù)就無法繼承。
C++98中構(gòu)造函數(shù)私有化,派生類中調(diào)不到基類的構(gòu)造函數(shù),則無法繼承
class NonInherit
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
private:
NonInherit()
{ }
};C++11方法:final關(guān)鍵字,final修飾類,表示該類不能被繼承。ps(final還能修飾虛函數(shù),表示該虛函數(shù)不能被重寫。)
final 關(guān)鍵字:final修飾類,表示該類不能被繼承。
class A final
{
//..
};5.請設(shè)計一個類,只能創(chuàng)建一個對象(單例模式)
設(shè)計模式:
設(shè)計模式(Design Pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類的、代碼設(shè)計經(jīng)驗的總結(jié)。為什么會產(chǎn)生設(shè)計模式這樣的東西呢?就像人類歷史發(fā)展會產(chǎn)生兵法。最開始部落之間打仗時都是人拼人的對砍。后來春秋戰(zhàn)國時期,七國之間經(jīng)常打仗,就發(fā)現(xiàn)打仗也是有套路的,后來孫子就總結(jié)出了《孫子兵法》。孫子兵法也是類似。
使用設(shè)計模式的目的:為了代碼可重用性、讓代碼更容易被他人理解、保證代碼可靠性。設(shè)計模式使代碼編寫真正工程化;設(shè)計模式是軟件工程的基石脈絡(luò),如同大廈的結(jié)構(gòu)一樣。
單例模式:
一個類只能創(chuàng)建一個對象,即單例模式。該模式可以保證系統(tǒng)中該類只有一個實例,并提供一個訪問它的全局訪問點(diǎn),該實例被所有程序模塊共享。比如:在某個服務(wù)器程序中,該服務(wù)器的配置信息存放在一個文件中,這些配置數(shù)據(jù)由一個單例對象統(tǒng)一讀取,然后服務(wù)進(jìn)程中的其他對象再通過這個單例對象獲取這些配置信息,這種方式簡化了在復(fù)雜環(huán)境下的配置管理。
單例模式有兩種實現(xiàn)模式:
1.餓漢模式:
(一開始(main函數(shù)之前)就創(chuàng)建單例對象)
就是說不管你將來用還是不用,程序啟動時就會創(chuàng)建一個唯一的實例對象。
餓漢模式
優(yōu)點(diǎn):簡單
缺點(diǎn):可能會導(dǎo)致進(jìn)程啟動慢,且如果有多個單例類對象實例啟動順序不確定。
class Singleton
{
public:
static Singleton* GetInstance()
{
return &m_instance;
}
private:
//構(gòu)造函數(shù)私有化
Singleton(){}
//C++98防止拷貝
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
//or
//C++11
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton m_instance;
};
Singleton Singleton::m_instance;//在程序入口之前就完成單例對象的初始化但是如果這個單例對象再多線程高并發(fā)環(huán)境下頻繁使用,性能要求比較高,那么顯然使用餓漢模式來避免資源競爭,提高響應(yīng)速度更好。
2.懶漢模式:
如果單例對象構(gòu)造十分耗時或者占用很多資源,比如加載插件啦,初始化網(wǎng)絡(luò)連接啦,讀取文件啦等等,而有可能該對象程序運(yùn)行時不會用到,那么也要在程序一開始就進(jìn)行初始化,就會導(dǎo)致程序啟動時非常的緩慢。所以這種情況就使用懶漢模式(延遲加載)更好。
懶漢模式也會有線程安全問題,在第一次初始化的時候要加鎖。
class Singleton
{
public:
static Singleton* GetInstance()
{
注意這里一定要使用Double-Check的方式加鎖,才能保證效率和線程安全
if (nullptr == m_pInstance)
{
m_mtx.lock();//or--- unique_lock<mutex> lock(m_mtx);
if (nullptr == m_pInstance)
{
m_pInstance = new Singleton();
}
m_mtx.unlock();
}
}
//實現(xiàn)一個內(nèi)嵌的垃圾回收類
class CGarbo
{
public:
~CGarbo()
{
if (Singleton::m_pInstance)
delete Singleton::m_pInstance;
}
};
//定義一個靜態(tài)成員變量,程序結(jié)束時,系統(tǒng)自動調(diào)用它的析構(gòu)函數(shù)從而釋放單例對象
static CGarbo Garbo;
main函數(shù)結(jié)束,調(diào)用它的析構(gòu),從而釋放單例對象
private:
//構(gòu)造函數(shù)私有化
Singleton(){}
//防止拷貝
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* m_pInstance;//單例對象指針
static std::mutex m_mtx; //互斥鎖
};
Singleton* Singleton::m_pInstance = nullptr;
Singleton::CGarbo Garbo;
std::mutex Singleton::m_mtx;C++11中創(chuàng)建單例(懶漢模式)最簡單的方法
class Singleton
{
public:
局部的靜態(tài)對象,是在第一次調(diào)用時初始化
static Singleton& GetInstance()
{
static Singleton inst;
return inst;
}
private:
Singleton()
{
std::cout << "Singleton" << std::endl;
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};C++11之前,這個代碼是不安全的,編譯器不支持。
C++11之后,可以保證局部靜態(tài)對象的初始化是線程安全的,只初始化一次。(C++11之后這個操作變成原子的了。)
C++中的類型轉(zhuǎn)換
1.C語言中的類型轉(zhuǎn)換
在C語言中,如果賦值運(yùn)算符左右兩側(cè)類型不同,或者形參與實參類型不匹配,或者返回值類型與接收返回值類型不一致時,就需要發(fā)生類型轉(zhuǎn)換,C語言中總共有兩種形式的類型轉(zhuǎn)換:隱式類型轉(zhuǎn)換和顯式類型轉(zhuǎn)換。
1.隱式類型轉(zhuǎn)換:編譯器在編譯階段自動進(jìn)行,能轉(zhuǎn)就轉(zhuǎn),不能轉(zhuǎn)就編譯失敗。
2.顯式類型轉(zhuǎn)換:需要用戶自己處理。
void Test()
{
int i = 1;
//隱式類型轉(zhuǎn)換
double d = i;
printf("%d,%.2lf\n", i, d);
int* p = &i;
//顯式類型轉(zhuǎn)換
int address = (int)p;
printf("%x,%d\n", p, address);
}缺陷:轉(zhuǎn)換的可視性比較差,所有的轉(zhuǎn)換形式都是以一種相同形式書寫,難以跟蹤錯誤的轉(zhuǎn)換。
2.為什么C++需要四種類型轉(zhuǎn)換
C風(fēng)格的轉(zhuǎn)換形式很簡單,但是有不少缺點(diǎn):
1.隱式類型轉(zhuǎn)換有些情況下可能會出問題:比如說數(shù)據(jù)精度的丟失。
2.顯式類型轉(zhuǎn)換將所有情況混合在一起,代碼不夠清晰。
因此C++提出了自己的類型轉(zhuǎn)換的風(fēng)格,注意C++要兼容C語言,所以在C++中還是可以使用C語言那一套的類型轉(zhuǎn)換。
3.C++強(qiáng)制類型轉(zhuǎn)換
標(biāo)準(zhǔn)C++為了加強(qiáng)類型轉(zhuǎn)換的可視性,引入了四種命名的強(qiáng)制類型轉(zhuǎn)換操作符:
static_cast , reinterpret_cast , const_cast , dynamic_cast
3.1 stacit_cast
static_cast用于非多態(tài)類型的轉(zhuǎn)換(靜態(tài)轉(zhuǎn)換),編譯器隱式執(zhí)行的任何類型轉(zhuǎn)換都可用static_cast,但它不能用于兩個不相關(guān)的類型進(jìn)行轉(zhuǎn)換。(用于兩個相關(guān)類型/相近類型之間,C里的隱式類型轉(zhuǎn)換都可用這個static_cast強(qiáng)轉(zhuǎn)。)
int main()
{
double d = 12.34;
int a = static_cast<int>(d);
std::cout << a << std::endl;
return 0;
}3.2 reinterpret_cast
reinterpret_cast操作符通常為操作數(shù)的位模式提供較低層次的重新解釋,用于將一種類型轉(zhuǎn)換為另一種不同的類型。(可用于兩個不相關(guān)類型之間進(jìn)行強(qiáng)轉(zhuǎn)。)
int main()
{
double d = 12.34;
int a = static_cast<int>(d);
std::cout << a << std::endl;
這里使用static_cast會報錯,應(yīng)該使用reinterpret
//int* p = static_cast<int*>(a);
int* p = reinterpret_cast<int*>(a);
return 0;
}3.3 const_cast
const_cast最常用的用途就是刪除變量的const屬性,方便賦值。(去掉const屬性。)
int main()
{
const int a = 2;
int* p = const_cast<int*>(&a);
*p = 3;
std::cout << a << std::endl;
return 0;
}3.4 dynamic_cast
dynamic_cast用于將一個父類對象的指針/引用轉(zhuǎn)換為子類對象的指針或引用(動態(tài)轉(zhuǎn)換)。
向上轉(zhuǎn)型:子類對象指針/引用 ->父類指針/引用(不需要轉(zhuǎn)換,賦值兼容規(guī)則)。
向下轉(zhuǎn)型:父類對象指針/引用->子類指針/引用(用dynamic_cast轉(zhuǎn)型是安全的)。
注意:
1.dynamic_cast只能用于父類含有虛函數(shù)的類。
2.dynamic_cast會先檢查是否能轉(zhuǎn)換成功,能成功則轉(zhuǎn)換,不能則返回0。
Base* b = new Derived(); b實際上指向Derived對象 情況1:成功轉(zhuǎn)換 Derived* d1 = dynamic_cast<Derived*>(b); 成功:b實際指向Derived對象,d1是有效的Derived指針 情況2:轉(zhuǎn)換失敗 Base* b2 = new Base(); // b2指向純Base對象 Derived* d2 = dynamic_cast<Derived*>(b2); 失?。篵2不是Derived對象,d2 == nullptr
class A
{
public:
virtual void f(){}
};
class B :public A
{};
void fun(A* pa)
{
//dynamic_cast會先檢查是否能成功轉(zhuǎn)換成功,能成功則轉(zhuǎn)換,不能則返回
B* pb1 = static_cast<B*>(pa);
B* pb2 = dynamic_cast<B*>(pa);
std::cout << "pb1:" << pb1 << std::endl;
std::cout << "pb2:" << pb2 << std::endl;
}
int main()
{
A a;
B b;
fun(&a);
fun(&b);
return 0;
}注意:
強(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ā)生錯誤的機(jī)會。強(qiáng)烈建議:避免使用強(qiáng)制類型轉(zhuǎn)換。
4.RTTI(了解)
RTTI:Run-time Type identification的簡稱,即:運(yùn)行時類型識別。
C++通過一下方式來支持RTTI:
1.typeid運(yùn)算符。
2.dynamic_cast運(yùn)算符。
3.decltype。
5.常見面試題
1.C++中的四種類型轉(zhuǎn)換分別是:static_cast,reinterpret_cast,const_cast,dynamic_cast。
2.說說四種類型轉(zhuǎn)換的應(yīng)用場景。

static_cast適用于兩種類型相近的強(qiáng)制轉(zhuǎn)化,dynamic_cast適用于多態(tài)類型的向下轉(zhuǎn)換(安全檢查)(向上轉(zhuǎn)換本來就是安全的)。cosnt_cast適用于刪除變量的const屬性。reinterpret_cast適用于任意類型之間的轉(zhuǎn)換(危險)。
總結(jié)
到此這篇關(guān)于C++特殊類設(shè)計與類型轉(zhuǎn)換的文章就介紹到這了,更多相關(guān)C++特殊類設(shè)計與類型轉(zhuǎn)換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++數(shù)據(jù)結(jié)構(gòu)之AVL樹的實現(xiàn)
AVL樹是高度平衡的而二叉樹,它的特點(diǎn)是AVL樹中任何節(jié)點(diǎn)的兩個子樹的高度最大差別為1,本文主要給大家介紹了C++如何實現(xiàn)AVL樹,需要的朋友可以參考下2022-06-06
Cocos2d-x學(xué)習(xí)筆記之Hello World源碼分析
這篇文章主要介紹了Cocos2d-x學(xué)習(xí)筆記之Hello World源碼分析,接上一篇內(nèi)容,本文著重分析源碼文件,需要的朋友可以參考下2014-09-09
帶你用C語言實現(xiàn)strtok和字符串分割函數(shù)
下面小編就為大家?guī)硪黄猚語言中字符串分割函數(shù)及實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2021-09-09
淺析C++中dynamic_cast和static_cast實例語法詳解
這篇文章主要介紹了淺析C++中dynamic_cast和static_cast實例演示,包括static_cast語法知識和static_cast的作用講解,namic_cast 語法詳解,需要的朋友可以參考下2021-07-07

