最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++類中隱藏的幾個默認(rèn)函數(shù)你知道嗎

 更新時間:2022年03月08日 10:17:28   作者:我叫RT  
這篇文章主要為大家詳細(xì)介紹了C++類中隱藏的幾個默認(rèn)函數(shù),使用數(shù)據(jù)庫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

Test類中隱藏的六個默認(rèn)的函數(shù)

class Test
{
public:
	//默認(rèn)的構(gòu)造函數(shù)
	Test();
	//析構(gòu)函數(shù) 
	~Test();
	//拷貝構(gòu)造函數(shù) 
	Test(const Test &t);
	//賦值函數(shù) 
	Test& operator=(const Test &x);
	//一般對象取地址函數(shù) 
	Test* operator&();
	//常對象取地址函數(shù) 
	const Test* operator&()const;
private:
		int data;
		//int *data;	// 注意:如果成員中含有指針類型,需重載拷貝函數(shù)與賦值函數(shù)
						// 否則會造成淺拷貝
						// 另外,需要注意在析構(gòu)函數(shù)中,釋放類中使用的額外資源(堆區(qū)申請的資源)
};

1.構(gòu)造函數(shù)

作用:對象所在的內(nèi)存空間做初始化 、給對象賦資源

特點(diǎn)

1.可以重載 :可以根據(jù)實際需要進(jìn)行缺省的、多參重載

2.不依賴對象:對象無法調(diào)用構(gòu)造函數(shù),只能在對象定義點(diǎn)被調(diào)用

//成員函數(shù)類外實現(xiàn),需在函數(shù)名前指定作用域,否則編譯器會認(rèn)為在定義一個普通的函數(shù)
Test::Test()	//類中默認(rèn)的構(gòu)造函數(shù)
{
}
//此外,構(gòu)造函數(shù)可以支持重載,我們可以根據(jù)需要自己寫一些構(gòu)造函數(shù)
//需要注意的是,如果我們自己寫了構(gòu)造函數(shù),那么編譯器就不會提供默認(rèn)的構(gòu)造函數(shù)了
Test::Test(int d = 0 )	//缺省的構(gòu)造函數(shù)
{
	data = d;
}
Test::Test(int d = 0 ):data(d)	//缺省的構(gòu)造函數(shù),用初始化列表的方式初始化
{
}

兩者初始化的區(qū)別在于,初始化列表是真正意義上的初始化,它告訴編譯器在實例化對象的時候以何種方式對成員賦值,而在前者的賦值規(guī)則寫在了構(gòu)造函數(shù)內(nèi)部,是在已經(jīng)生成了成員變量之后再進(jìn)行的賦值操作。

初始化列表示例

Tips: 注意區(qū)分列表參數(shù)初始化列表初始化的區(qū)別 。列表參數(shù)初始化即在函數(shù)的形參列表后通過 fun(int ia) :mval(ia) 冒號+括號的這種方式初始化,而列表初始化一般是指如 std::vector<int> vec{ 1,2,3,4,5 }; vec{1,2,3,4,5}; 這種,在定義時通過 { } 括起來的列表初始化“數(shù)組”的行為。 事實上,在C++11標(biāo)準(zhǔn)中還有一種就地初始化的概念,這里先不做討論。

對于 初始化列表 有幾點(diǎn)特性需要注意:

比如以下操作,成員變量有引用類型和const類型,在C++中規(guī)定const類型為一個常量,定義時必須初始化,而引用我們認(rèn)為是一個變量的別名也需要在定義時就初始化。所以以下操作只能使用初始化列表的方式初始化。

class Test
{
public:
	/* error 常量、引用在定義時就初始化
	Test(int a, int b,int c)
	{
		ma = a;
		mb = a;	// error
		mc = a; // error
	}
	*/
	Test(int a, int b, int c):ma(a),mb(b),mc(c)
	{
	}
private:
	int ma;
	int& mb;
	const int mc;
};

此外,如果有多個成員變量需要使用初始化列表的方式初始化,需要注意一點(diǎn)細(xì)節(jié),初始化的順序只與成員變量的定義順序相關(guān)。

如以下程序,可以寫成Test(int a):ma(mb), mb(a){}Test(int a):mb(a),ma(mb){}因為成員變量的定義順序為int mb; int ma;,也就是說賦值順序與初始化列表無關(guān),只與成員變量被定義的順序有關(guān)。

class Test
{
public:
	Test(int a):ma(mb), mb(a)	//mb先被定義出來,先給mb賦值,再給ma賦值
	{
	}
	/* 下面錯誤的寫法: 
		解釋:
		1. mb先定義,ma后定義,兩者的使用參數(shù)列表初始化的順序是先 mb, 再 ma
		2. 在初始化之前 ma 與 mb 都是隨機(jī)值,或被填充為0xcccccccc (具體看編譯器實現(xiàn))
		3. 在初始化時, mb(ma) ,則mb被初始化為無效值(隨機(jī)值或0xcccccccc)
		                ma(a) ,  ma 被初始化為 a 的值。
						因此,如果調(diào)用Test(10), 則 mb: -858993460 ma: 10
	*/
	Test(int a) :mb(ma), ma(a)	
	{
	}
public:
	void Show()
	{
		std::cout << "ma: " << ma << std::endl;
		std::cout << "mb: " << mb << std::endl;
	}
private:
	int mb;
	int ma;
};

注:以下函數(shù)的Test類成員均為 int *ma ,表示數(shù)據(jù)成員為指針時,各成員函數(shù)的實現(xiàn)方法。

2.析構(gòu)函數(shù)

作用:釋放對象所占的其他資源。

特點(diǎn)

不可重載 : 對象銷毀時會調(diào)用析構(gòu)函數(shù),并釋放空間。依賴對象:可手動調(diào)用即this->~Test()或 Test t; t.~Test(),但是不建議,因為對象銷毀時會自動調(diào)用,如果手動調(diào)用可能會引起內(nèi)存空間的重復(fù)析構(gòu)導(dǎo)致程序崩潰

//默認(rèn)的析構(gòu)函數(shù) 
Test::~Test()
{		//沒有額外的資源,什么都不寫
}
//如果程序中有額外的空間需要釋放
class Test
{
public:
	//構(gòu)造函數(shù) 
	Test(int ia = 0) 
	{
		data = new int{ ia };		//data指向一塊堆區(qū)內(nèi)存
	}
	//析構(gòu)函數(shù)
	~Test();
private:
		int* data;
};
//析構(gòu)函數(shù) 
Test::~Test()
{
	delete data;	//把額外空間的釋放寫進(jìn)析構(gòu)函數(shù)
	data = nullptr;
}

3.拷貝構(gòu)造函數(shù)

作用:拿一個已存在的對象來生成相同類型的新對象

注意:類中提供的拷貝構(gòu)造函數(shù)為一個淺拷貝類型,即如果成員變量中含有指針類型,它在進(jìn)行拷貝構(gòu)造的時候不會進(jìn)行額外空間的開辟,最終會造成函數(shù)析構(gòu)時的錯誤。

class Test
{
public:
	//構(gòu)造函數(shù) 
	Test(int ia = 0) 
	{
		data = new int{ ia };		//data指向一塊堆區(qū)內(nèi)存
	}
	//拷貝構(gòu)造函數(shù) 
	Test(const Test &t);	//一定要傳引用,否則在開辟形參的過程中會遞歸的調(diào)用拷貝構(gòu)造函數(shù)來構(gòu)造形參,而函數(shù)始終無法執(zhí)行
private:
		int* data;
};
//默認(rèn)的拷貝構(gòu)造函數(shù) 
Test::Test(const Test &t)
{
	data = t.data;		//淺拷貝,只把現(xiàn)有的成員變量進(jìn)行拷貝,沒有對堆區(qū)內(nèi)存進(jìn)行拷貝,使多個對象的data指向了同一片堆區(qū)空間,在對象銷毀時會造成空間的重復(fù)釋放引發(fā)程序崩潰。
}
//拷貝構(gòu)造函數(shù)
Test::Test(const Test &t)
{
	data = new int;		//如果是字符類型data = new char[strlen(t.data) + 1]; 
						// 注意strlen() 函數(shù)不能傳遞nullptr參數(shù)
	strcpy_s(data,sizeof(int), t.data);
}
// 或者使用初始化列表的方式
Test::Test(const Test& t) :data(new int{*(t.data)})
{
}

4.賦值運(yùn)算符的重載函數(shù)

作用:拿一個已存在的對象給相同類型的已存在對象賦值

實現(xiàn)步驟

1.賦值判斷

2.釋放舊資源

3.生成新資源

4.賦值

class Test
{
public:
	//構(gòu)造函數(shù) 
	Test(int ia = 0) 
	{
		data = new int{ ia };		//data指向一塊堆區(qū)內(nèi)存
	}
	//賦值函數(shù)
	Test& operator=(const Test &x);	//以自身類類型的引用的方式返回
private:
		int* data;
};
//默認(rèn)的賦值函數(shù)(淺拷貝)
Test& Test::operator=(const Test &x)
{
	if(this!=&x)		//自賦值判斷
	{
		data=x.data;	//淺拷貝
	}
	return *this;		//返回自身類類型的引用
}
//賦值函數(shù)(深拷貝)
Test& Test::operator=(const Test &x)
{
	if(this!=&x)		//自賦值判斷
	{
		delete data;	//釋放原資源 
		//delete[] data; 如果申請的空間是多個,即數(shù)組形式,需要delete [] data 釋放
		data = new int;	//開辟空間
		memcpy(data, x.data, sizeof(data));	// 賦值
	}
	return *this;		//返回自身類類型的引用
}

5.一般對象取地址函數(shù)

//一般對象取地址函數(shù) 
Test::Test* operator&()
{
	return this;
}

6.常對象取地址函數(shù)

//常對象取地址函數(shù) 
const Test::Test* operator&()const
{
	return this;
}

C++11以后增加了右值引用的概念,同時增加了移動構(gòu)造函數(shù)、和移動賦值函數(shù)。

7.移動構(gòu)造函數(shù)

作用:針對某些情況構(gòu)造對象的優(yōu)化,避免重復(fù)的開辟內(nèi)存。

使用場景:比如把臨時對象的資源作為構(gòu)建新對象的資源使用,而臨時對象銷毀時,資源繼續(xù)被其他對象使用(這里就節(jié)省了一次舊對象資源的的銷毀與新對象資源申請的開銷)。

class Test
{
public:
	//構(gòu)造函數(shù) 
	Test(int ia = 0) :data(new int{ ia })
	{}
	// 拷貝構(gòu)造 ..
	// 賦值..
	// 析構(gòu)..
	// 移動構(gòu)造
	Test(Test&& rhs)
	{
		this->data = rhs.data;	// 資源的轉(zhuǎn)移
		rhs.data = nullptr;		// 資源的釋放
	}
	 /* 
	 //使用初始化列表的方式
	Test(Test&& rhs) :data(rhs.data)
	{
		rhs.data = nullptr;
	}
	*/
public:
	void print() { std::cout << (void*)data << std::endl; }
private:
	int* data;
};
int main()
{
	Test t(10); 
	t.print();
	Test t2(std::move(t));	// 把 t 的資源轉(zhuǎn)移給 t2
	t.print();
	t2.print();
	return 0;
}

8.移動賦值函數(shù)

作用:資源的轉(zhuǎn)移,針對某些情況下,節(jié)省內(nèi)存的開辟。

class Test
{
public:
	//構(gòu)造函數(shù) 
	Test(int ia = 0) :data(new int{ ia })
	{}
	// 拷貝構(gòu)造 ..
	// 賦值..
	// 析構(gòu)..
	// 移動構(gòu)造
	// 移動賦值
	Test& operator=(Test&& rhs) noexcept // 不拋出異常
	{
		if (this != &rhs)	// 防止自賦值
		{
			delete data;			// 銷毀當(dāng)前資源
			this->data = rhs.data;	// 轉(zhuǎn)移資源,即接收對方資源
			rhs.data = nullptr;		// 對方放棄資源的擁有
		}
		return *this;
	}

public:
	void print() { std::cout << (void*)data << std::endl; }
private:
	int* data;
};

int main()
{
	Test t(10), t2(20);
	t.print();
	t2.print();
	t2 = std::move(t);	// 把 t 的資源交給 t2
	t.print();	// 輸出 00000000
	t2.print();
	return 0;
}

補(bǔ)充:

另外一個關(guān)于移動構(gòu)造的話題是異常。對于移動構(gòu)造函數(shù)來說,拋出異常有時是件危險的事情。因為可能移動語義還沒完成,一個異常卻拋出來了,這就會導(dǎo)致一些指針就成為懸掛指針。因此程序員應(yīng)該盡量編寫不拋出異常的移動構(gòu)造函數(shù),通過為其添加一個noexcept關(guān)鍵字,可以保證移動構(gòu)造函數(shù)中拋出來的異常會直接調(diào)用terminate程序終止運(yùn)行,而不是造成指針懸掛的狀態(tài)。而標(biāo)準(zhǔn)庫中,我們還可以用一個std::move_if_noexcept的模板函數(shù)替代move函數(shù)。該函數(shù)在類的移動構(gòu)造函數(shù)沒有noexcept關(guān)鍵字修飾時返回一個左值引用從而使變量可以使用拷貝語義,而在類的移動構(gòu)造函數(shù)有noexcept關(guān)鍵字時,返回一個右值引用,從而使變量可以使用移動語義。

關(guān)于移動構(gòu)造函數(shù)的示例程序,引用自《深入理解C++11》一書:

#include <iostream>
using namespace std;
class HasPtrMem {
public:
	HasPtrMem() :d(new int(3)) {
		cout<<"Construct:"<<++n_cstr<<endl;
	}
	HasPtrMem(const HasPtrMem& h) :d(new int(*h.d)) {
		cout<<"Copy construct:"<<++n_cptr<<endl;
	}
	HasPtrMem(HasPtrMem&& h) :d(h.d) {//移動構(gòu)造函數(shù)
		h.d = nullptr;//將臨時值的指針成員置空
		cout<<"Move construct:"<<++n_mvtr<<endl;
	}
	~HasPtrMem() {
		delete d;
		cout<<"Destruct:"<<++n_dstr<<endl;
	}
	int* d;
	static int n_cstr;
	static int n_dstr;
	static int n_cptr;
	static int n_mvtr;
};
int HasPtrMem::n_cstr = 0;		
int HasPtrMem::n_dstr = 0;
int HasPtrMem::n_cptr = 0;
int HasPtrMem::n_mvtr = 0;
HasPtrMem GetTemp() {
	HasPtrMem h;
	cout<<"Resource from"<<__func__<<":"<<hex<<h.d<<endl;
	return h;
}
int main() {
	HasPtrMem a = GetTemp();
	cout<<"Resource from"<<__func__<<":"<<hex<<a.d<<endl;
}
//編譯選項:g++ -std=c++11 test.cpp -fno-elide-constructors

輸出:左邊是輸出結(jié)果,右邊是注釋

Construct:1						// 在GetTemp() 函數(shù)中,執(zhí)行 HasPtrMem h; 構(gòu)造對象
Resource fromGetTemp:0x1047f28	// 在GetTemp() 函數(shù)中,執(zhí)行cout << ... 
Move construct:1				// 在GetTemp() 函數(shù)中,return h; 產(chǎn)生臨時對象,此時第一次調(diào)用移動構(gòu)造
Destruct:1						// 進(jìn)入main() 函數(shù)時,GetTemp();調(diào)用結(jié)束、進(jìn)行清棧(棧幀回退),析構(gòu)掉局部對象( h )
Move construct:2				// 在main() 函數(shù)中,執(zhí)行 GetTemp(); 后產(chǎn)生的返回值是一個臨時無名對象,調(diào)用了移動構(gòu)造函數(shù),此時第二次調(diào)用移動構(gòu)造
Destruct:2						// 在main() 函數(shù)中,執(zhí)行了a = GetTemp(); 后,臨時對象生存期結(jié)束,析構(gòu)掉臨時對象(函數(shù)返回值)
Resource frommain:0x1047f28		// 在main() 函數(shù)中,執(zhí)行cout << ... 
Destruct:3						// main() 函數(shù)結(jié)束,對象 a 生存周期結(jié)束,銷毀對象 a 

需要注意的是,在編譯器中存在被稱為RVO/NRVO的優(yōu)化(RVO,Return Value Optimization,返回值優(yōu)化,或者NRVO,Named Return Value optimization)。因此在上述編譯時使用了 -fno-elide-constructors 選項在g++/clang++中關(guān)閉這個優(yōu)化,這樣可以使我們在代碼運(yùn)行的結(jié)果中較為容易地利用函數(shù)返回的臨時量右值?!?/p>

如果在編譯的時候不使用該選項的話,我們寫的很多含有移動語義的函數(shù)都被省略了。例如以下的代碼

A ReturnRvalue(){A a();return a;}
A b=ReturnRvalue();

b 變量實際就使用了ReturnRvalue函數(shù)中a的地址,任何的拷貝和移動都沒有了。通俗地說,就是b變量直接“霸占”了a變量。這是編譯器中一個效果非常好的一個優(yōu)化。不過RVO/NRVO并不是對任何情況都有效。比如有些情況下,一些構(gòu)造是無法省略的。還有一些情況,即使RVO/NRVO完成了,也不能達(dá)到最好的效果。 

總體而言,移動語義除了可以解決某些情況下編譯器無法解決的優(yōu)化問題,還在一些其他特殊的場合有著重要的用途(比如在unique_ptr中禁止構(gòu)造函數(shù),卻可以通過移動的構(gòu)造或移動賦值對unique_ptr擁有的資源進(jìn)行轉(zhuǎn)移)。

總結(jié)

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!   

相關(guān)文章

  • C語言簡單實現(xiàn)掃雷小游戲

    C語言簡單實現(xiàn)掃雷小游戲

    這篇文章主要為大家詳細(xì)介紹了C語言簡單實現(xiàn)掃雷小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-09-09
  • 好用的C++ string Format“函數(shù)”介紹

    好用的C++ string Format“函數(shù)”介紹

    大家好,本篇文章主要講的是好用的C++ string Format“函數(shù)”介紹,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • C++ OpenCV生成蒙太奇圖像的示例詳解

    C++ OpenCV生成蒙太奇圖像的示例詳解

    圖片的蒙太奇效果,一般稱為馬賽克圖。由很多小圖拼接成一個大圖。這篇文章主要為大家介紹如何利用C++ OpenCV實現(xiàn)生成蒙太奇圖像,感興趣的可以了解一下
    2022-01-01
  • 獲取本地網(wǎng)卡適配器信息具體代碼

    獲取本地網(wǎng)卡適配器信息具體代碼

    這篇文章主要介紹了獲取本地網(wǎng)卡適配器信息具體代碼,有需要的朋友可以參考一下
    2013-12-12
  • 深入剖析Android中init進(jìn)程實現(xiàn)的C語言源碼

    深入剖析Android中init進(jìn)程實現(xiàn)的C語言源碼

    這篇文章主要介紹了Android中init進(jìn)程實現(xiàn)的C語言源碼,init屬性服務(wù)在安卓中屬于系統(tǒng)的底層Linux服務(wù),需要的朋友可以參考下
    2015-07-07
  • C++?超詳細(xì)示例講解list的使用

    C++?超詳細(xì)示例講解list的使用

    list是一種序列式容器。list容器完成的功能實際上和數(shù)據(jù)結(jié)構(gòu)中的雙向鏈表是極其相似的,list中的數(shù)據(jù)元素是通過鏈表指針串連成邏輯意義上的線性表,也就是list也具有鏈表的主要優(yōu)點(diǎn),即:在鏈表的任一位置進(jìn)行元素的插入、刪除操作都是快速的
    2022-07-07
  • cocos2dx實現(xiàn)橡皮擦效果以及判斷是否擦除完畢

    cocos2dx實現(xiàn)橡皮擦效果以及判斷是否擦除完畢

    這篇文章主要為大家詳細(xì)介紹了cocos2dx實現(xiàn)橡皮擦效果以及判斷是否擦除完畢,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • C語言實現(xiàn)進(jìn)程5狀態(tài)模型的狀態(tài)機(jī)

    C語言實現(xiàn)進(jìn)程5狀態(tài)模型的狀態(tài)機(jī)

    狀態(tài)機(jī)在實際工作開發(fā)中應(yīng)用非常廣泛,用這幅圖就可以很清晰的表達(dá)整個狀態(tài)的流轉(zhuǎn)。本篇通過C語言實現(xiàn)一個簡單的進(jìn)程5狀態(tài)模型的狀態(tài)機(jī),讓大家熟悉一下狀態(tài)機(jī)的魅力,需要的可以參考一下
    2022-10-10
  • 匯編語言rep movsd 的使用詳解

    匯編語言rep movsd 的使用詳解

    rep movsd 每次ecx!=0便執(zhí)行movsd ,然后ecx=ecx-1 movsd移動ds:[si] 到es:[di],在32位匯編下可以用esi代替si,edi代替di
    2013-09-09
  • C++反轉(zhuǎn)字符串中單詞的字符順序的兩種方法

    C++反轉(zhuǎn)字符串中單詞的字符順序的兩種方法

    在處理字符串相關(guān)的問題時,反轉(zhuǎn)字符串中每個單詞的字符順序是一個常見的任務(wù),同時要保證空格和單詞的初始順序不變,下面我們將詳細(xì)介紹兩種解決該問題的方法,包括其解題思路和具體實現(xiàn)細(xì)節(jié),需要的朋友可以參考下
    2025-03-03

最新評論

黄浦区| 莆田市| 且末县| 新兴县| 新兴县| 长沙县| 穆棱市| 安达市| 崇礼县| 油尖旺区| 来安县| 墨竹工卡县| 剑河县| 尉犁县| 洪江市| 封丘县| 隆回县| 宜城市| 无为县| 常德市| 方城县| 塔河县| 万盛区| 景宁| 宝山区| 剑川县| 连山| 南阳市| 巧家县| 揭阳市| 秭归县| 称多县| 上蔡县| 潞城市| 宜都市| 郸城县| 江西省| 永城市| 蓬莱市| 靖江市| 团风县|