C++特殊類設(shè)計(jì)與類型轉(zhuǎn)換詳細(xì)代碼示例
常見的特殊類的設(shè)計(jì)
1.請?jiān)O(shè)計(jì)一個(gè)類,不能被拷貝
拷貝只會發(fā)生在兩個(gè)場景中:拷貝構(gòu)造函數(shù)以及賦值運(yùn)算符重載,因此想要讓一個(gè)類禁止拷貝,只需要讓該類不能調(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.只聲明不定義:不定義是因?yàn)樵摵瘮?shù)根本不會調(diào)用,定義了其實(shí)也沒有什么意義,不寫反而還簡單,而且如果定義了就不會防止成員函數(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.請?jiān)O(shè)計(jì)一個(gè)類,只能在堆上創(chuàng)建對象
實(shí)現(xiàn)方式:
1.將類的構(gòu)造函數(shù)設(shè)置為私有,拷貝構(gòu)造聲明成私有。防止別人調(diào)用拷貝在棧上生成對象。
2.提供一個(gè)靜態(tài)的成員函數(shù),在該靜態(tài)成員函數(shù)中完成堆對象的創(chuàng)建。
class HeapOnly
{
public:
static HeapOnly* CreateHeapObj()
{
return new HeapOnly;
}
private:
HeapOnly()
{ }
//C++98方式
//1.只聲明,不實(shí)現(xiàn)。因?yàn)閷?shí)現(xiàn)可能會很麻煩,而你本身并不需要
//2.聲明成私有
HeapOnly& operator=(const HeapOnly&);
//C++11方式
HeapOnly& operator=(const HeapOnly&) = delete;
};3.請?jiān)O(shè)計(jì)一個(gè)類,只能在棧上創(chuàng)建對象
方法:同上述將構(gòu)造函數(shù)私有化,然后設(shè)計(jì)靜態(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. 請?jiān)O(shè)計(jì)一個(gè)類,不能被繼承
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.請?jiān)O(shè)計(jì)一個(gè)類,只能創(chuàng)建一個(gè)對象(單例模式)
設(shè)計(jì)模式:
設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。為什么會產(chǎn)生設(shè)計(jì)模式這樣的東西呢?就像人類歷史發(fā)展會產(chǎn)生兵法。最開始部落之間打仗時(shí)都是人拼人的對砍。后來春秋戰(zhàn)國時(shí)期,七國之間經(jīng)常打仗,就發(fā)現(xiàn)打仗也是有套路的,后來孫子就總結(jié)出了《孫子兵法》。孫子兵法也是類似。
使用設(shè)計(jì)模式的目的:為了代碼可重用性、讓代碼更容易被他人理解、保證代碼可靠性。設(shè)計(jì)模式使代碼編寫真正工程化;設(shè)計(jì)模式是軟件工程的基石脈絡(luò),如同大廈的結(jié)構(gòu)一樣。
單例模式:
一個(gè)類只能創(chuàng)建一個(gè)對象,即單例模式。該模式可以保證系統(tǒng)中該類只有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn),該實(shí)例被所有程序模塊共享。比如:在某個(gè)服務(wù)器程序中,該服務(wù)器的配置信息存放在一個(gè)文件中,這些配置數(shù)據(jù)由一個(gè)單例對象統(tǒng)一讀取,然后服務(wù)進(jìn)程中的其他對象再通過這個(gè)單例對象獲取這些配置信息,這種方式簡化了在復(fù)雜環(huán)境下的配置管理。
單例模式有兩種實(shí)現(xiàn)模式:
1.餓漢模式:
(一開始(main函數(shù)之前)就創(chuàng)建單例對象)
就是說不管你將來用還是不用,程序啟動時(shí)就會創(chuàng)建一個(gè)唯一的實(shí)例對象。
餓漢模式
優(yōu)點(diǎn):簡單
缺點(diǎn):可能會導(dǎo)致進(jìn)程啟動慢,且如果有多個(gè)單例類對象實(shí)例啟動順序不確定。
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;//在程序入口之前就完成單例對象的初始化但是如果這個(gè)單例對象再多線程高并發(fā)環(huán)境下頻繁使用,性能要求比較高,那么顯然使用餓漢模式來避免資源競爭,提高響應(yīng)速度更好。
2.懶漢模式:
如果單例對象構(gòu)造十分耗時(shí)或者占用很多資源,比如加載插件啦,初始化網(wǎng)絡(luò)連接啦,讀取文件啦等等,而有可能該對象程序運(yùn)行時(shí)不會用到,那么也要在程序一開始就進(jìn)行初始化,就會導(dǎo)致程序啟動時(shí)非常的緩慢。所以這種情況就使用懶漢模式(延遲加載)更好。
懶漢模式也會有線程安全問題,在第一次初始化的時(shí)候要加鎖。
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();
}
}
//實(shí)現(xiàn)一個(gè)內(nèi)嵌的垃圾回收類
class CGarbo
{
public:
~CGarbo()
{
if (Singleton::m_pInstance)
delete Singleton::m_pInstance;
}
};
//定義一個(gè)靜態(tài)成員變量,程序結(jié)束時(shí),系統(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)用時(shí)初始化
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之前,這個(gè)代碼是不安全的,編譯器不支持。
C++11之后,可以保證局部靜態(tài)對象的初始化是線程安全的,只初始化一次。(C++11之后這個(gè)操作變成原子的了。)
C++中的類型轉(zhuǎn)換
1.C語言中的類型轉(zhuǎn)換
在C語言中,如果賦值運(yùn)算符左右兩側(cè)類型不同,或者形參與實(shí)參類型不匹配,或者返回值類型與接收返回值類型不一致時(shí),就需要發(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)換形式都是以一種相同形式書寫,難以跟蹤錯(cuò)誤的轉(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,但它不能用于兩個(gè)不相關(guān)的類型進(jìn)行轉(zhuǎn)換。(用于兩個(gè)相關(guān)類型/相近類型之間,C里的隱式類型轉(zhuǎn)換都可用這個(gè)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)換為另一種不同的類型。(可用于兩個(gè)不相關(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會報(bào)錯(cuò),應(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用于將一個(gè)父類對象的指針/引用轉(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實(shí)際上指向Derived對象 情況1:成功轉(zhuǎn)換 Derived* d1 = dynamic_cast<Derived*>(b); 成功:b實(shí)際指向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ā)生錯(cuò)誤的機(jī)會。強(qiáng)烈建議:避免使用強(qiáng)制類型轉(zhuǎn)換。
4.RTTI(了解)
RTTI:Run-time Type identification的簡稱,即:運(yùn)行時(shí)類型識別。
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)換(危險(xiǎn))。
總結(jié)
到此這篇關(guān)于C++特殊類設(shè)計(jì)與類型轉(zhuǎn)換的文章就介紹到這了,更多相關(guān)C++特殊類設(shè)計(jì)與類型轉(zhuǎn)換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++ 將字符串值賦給CHAR數(shù)組的實(shí)現(xiàn)
這篇文章主要介紹了C++ 將字符串值賦給CHAR數(shù)組的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01
C++使用循環(huán)計(jì)算標(biāo)準(zhǔn)差的代碼實(shí)現(xiàn)
在C++中,計(jì)算標(biāo)準(zhǔn)差可以使用循環(huán)來實(shí)現(xiàn),本文給大家介紹了一個(gè)示例代碼,演示了如何使用循環(huán)計(jì)算標(biāo)準(zhǔn)差,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2023-12-12
利用rapidjson實(shí)現(xiàn)解析嵌套的json的方法示例
今天小編就為大家分享一篇關(guān)于利用rapidjson實(shí)現(xiàn)解析嵌套的json的方法示例,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-04-04
一起來學(xué)習(xí)C++的構(gòu)造和析構(gòu)
這篇文章主要為大家詳細(xì)介紹了C++構(gòu)造和析構(gòu),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03

