欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++特殊類設(shè)計(jì)及類型轉(zhuǎn)換舉例詳解

 更新時(shí)間:2025年05月02日 09:49:16   作者:孞?¥  
這篇文章主要介紹了C++中如何設(shè)計(jì)不能被拷貝、只能在堆上或棧上創(chuàng)建對(duì)象的類、不能被繼承的類以及單例模式的實(shí)現(xiàn)方法,還討論了C++中的類型轉(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)文章

最新評(píng)論