C++類與對象3(中)

目錄

1.拷貝構造函數

2.賦值運算符重載函數

本篇講述以上兩種默認函數

拷貝構造函數

我們可以創建一個對象,那麼能否再創建一個和這個對象一模一樣的的新的對象呢?這就引入了拷貝構造函數
拷貝構造函數與之前講的構造函數一樣,函數名爲類名,當用已存在的類類型的對象創建新的對象時,編譯器會自動調用拷貝構造函數。

class A1
{
public:
	A1(int a = 1,double b = 0.1)
	{
		_a = a;
		_b = b;
	}
private:
	int _a;
	double _b;
};
int main()
{
	A1 A(5,1.5)//已經存在的對象
	A1 B(A);//通過A創建一個一模一樣的的對象B
}

上面我們沒有顯式的定義出拷貝構造函數,若要顯式定義,如下:

class A1
{
public:
	A1(int a = 1,double b = 0.1)
	{
		_a = a;
		_b = b;
	}
	A1(const A1& a)//拷貝構造函數,需要通過引用傳值,否則會一直調用拷貝函數進入死遞歸
	{
		_a = a._a;
		_b = a._b;
	}
private:
	int _a;
	double _b;
};
int main()
{
	A1 A(5,1.5)//已經存在的對象
	A1 B(A);//通過A創建一個一模一樣的的對象B
}

這裏的的默認拷貝構造函數,是一種淺拷貝(以後會講到),資源是拷貝不過去的。
總結:
1.拷貝構造函數是構造函數的一個重載形式。
2.拷貝構造函數的參數只有一個且必須使用引用傳參,使用傳值方式會引發無窮遞歸調用。
3.若未顯示定義,系統生成默認的拷貝構造函數。 默認的拷貝構造函數對象按內存存儲按字節序完成拷貝,這種拷貝稱爲淺拷貝,或者值拷貝。
4.如果涉及到資源的拷貝,便需要我們自己顯式定義拷貝構造函數,完成資源的拷貝。
5.建議在引用前加上const保證可以使用臨時對象進行拷貝

運算符重載函數

如果,我們要判斷兩個對象是否相等,可以如下:

class A1
{
public:
	A1(int a = 1,double b = 0.1)
	{
		_a = a;
		_b = b;
	}
	bool isequal(const A1& a)//判斷是否相等
	{
		return _a == a._a&&_b == a._b;
	}
private:
	int _a;
	double _b;
};
int main()
{
	A1 A;
	A1 B(A);
	cout<<B.isequal(A)<<endl;
}

B.isequal(A)寫的話,代碼的可讀性並不是很高,如果寫成A==B的話,那麼便一目瞭然的知道是判斷是否相等。因此,這裏引入運算符重載函數
運算符重載函數:
C++爲了增強代碼的可讀性引入了運算符重載,運算符重載是具有特殊函數名的函數,也具有其返回值類 型,函數名字以及參數列表,其返回值類型與參數列表與普通的函數類似
函數名字爲:關鍵字operator後面接需要重載的運算符符號。
函數原型:返回值類型 operator操作符(參數列表)
例如將上述比較對象是否相等的函數寫爲運算符重載函數如下:

class A1
{
public:
	A1(int a = 1,double b = 0.1)
	{
		_a = a;
		_b = b;
	}
	bool operator==(const A1& A)
	{
			return _a == A._a&&_b == A._b;
	}
	// bool isequal(const A1& a)
	// {
	// 	return _a == a._a&&_b == a._b;
	// }
private:
	int _a;
	double _b;
};
int main()
{
	A1 A;
	A1 B(A);
	cout<<(A==B)<<endl;
	//cout << A.operator==(B) << endl;等價於上面
	//cout<<B.isequal(A)<<endl;
}

這裏的bool operator==(const A1& a)是有兩個參數的,只不過第一個參數是調用者的this指針,即左操作數是this指向的調用函數的對象
當然還有以下這種定義方式:

class A1
{
public:
	A1(int a = 1,double b = 0.1)
	{
		_a = a;
		_b = b;
	}
	bool operator==(const A1& A,const A1& B)
	{
			return B._a == A._a&&B._b == A._b;
	}
	// bool isequal(const A1& a)
	// {
	// 	return _a == a._a&&_b == a._b;
	// }
//private:
	int _a;
	double _b;
};
int main()
{
	A1 A;
	A1 B(A);
	cout<<(A==B)<<endl;
	//cout << A.operator==(B) << endl;等價於上面
	//cout<<B.isequal(A)<<endl;
}

這是定義的全局的operator== 因此,需要時成員變量是公有的,如果還想要保持封裝性的話,就需要用到我們後面學習的友元解決,
再比如我們定義一個求兩數的和:

class A1
{
public:
	A1(int a = 1,double b = 0.1)
	{
		_a = a;
		_b = b;
	}
	int operator+(const A1& a)//求兩數和
	{
		return _a + a._a;
	}
private:
	int _a;
	double _b;
};
int main()
{
	A1 A;
	A1 B(5,10.2);
	cout<<(A+B)<<endl;
	cout<<A.operator+(B)<<endl;
}

結果如圖
在這裏插入圖片描述
總結:
1.不能通過連接其他符號來創建新的操作符:比如operator@
2.重載操作符必須有一個類類型或者枚舉類型的操作數
3.用於內置類型的操作符,其含義不能改變,例如:內置的整型+,不 能改變其含義
4.作爲類成員的重載函數時,其形參看起來比操作數數目少1,成員函數的操作符有一個默認的形參this,限定爲第一個形參
5.’*’ 、’::’ 、‘sizeof’ 、’?:’ 、’.’ 注意以上5個運算符不能重載.

賦值運算符重載
之前我們用構造函數或拷貝函數給另對象賦值,這裏我們可以通過運算符來賦值,比如:

class A1
{
public:
	A1(int a = 1,double b = 0.1)
	{
		_a = a;
		_b = b;
	}
	//運算符賦值重載函數
	//默認返回的是*this
const A1& operator=(const A1 &A)
	{
		if(this!=&A)
		{
			_a = A._a;
			_b = A._b;
		}
		return *this;
	}
	void display()
	{
		cout<<_a<<" "<<_b<<endl;
	}
private:
	int _a;
	double _b;
};
int main()
{
	A1 A;
	A1 B(5,10.2);
	B.display();
	B = A;//把A賦給B
	B.display();
}

打印結果如圖:
在這裏插入圖片描述
注意:
1.=調用:如果對象都存在,調用賦值運算符重載函數, 如果左邊對象不存在,調用拷貝構造
2.返回值爲引用類型
3.默認返回*this

4.一個類如果沒有顯式定義賦值運算符重載,編譯器也會生成一個,完成對象按字節序的值拷貝
比如:

class A1
{
public:
	A1(int a = 1,double b = 0.1)
	{
		_a = a;
		_b = b;
	}
	void display()
	{
		cout<<_a<<" "<<_b<<endl;
	}
private:
	int _a;
	double _b;
};
int main()
{
	A1 A(10,2.5);
	A1 B = A;//編譯器爲我們默認生成一個賦值運算符重載函數
	B.display();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章