C++11運算子過載詳解與向量類過載例項(<<,>>,+,-,*等)

進擊的汪sir發表於2021-07-15

1. C++運算子過載介紹

 C ++ 中預定義的運算子的操作物件只能是基本資料型別。但實際上,對於許多使用者自定義型別(例如類),也需要類似的運算操作。這時就必須在C ++ 中重新定義這些運算子,賦予已有運算子新的功能,使它能夠用於特定型別執行特定的操作。運算子過載的實質是函式過載,它提供了C ++ 的可擴充套件性,也是C ++ 最吸引人的特性之一。

運算子過載時要遵循以下規則:

( 1 ) 除了類屬關係運算子 " . " 、成員指標運算子 " .* " 、作用域運算子 " :: " 、sizeof運算子和三目運算子 " ?: " 以外,C ++ 中的所有運算子都可以過載。

( 2 ) 過載運算子限制在C ++ 語言中已有的運算子範圍內的允許過載的運算子之中,不能建立新的運算子。

( 3 ) 運算子過載實質上是函式過載,因此編譯程式對運算子過載的選擇,遵循函式過載的選擇原則。

( 4 ) 過載之後的運算子不能改變運算子的優先順序和結合性,也不能改變運算子運算元的個數及語法結構。

( 5 ) 運算子過載不能改變該運算子用於內部型別物件的含義。它只能和使用者自定義型別的物件一起使用,或者用於使用者自定義型別的物件和內部型別的物件混合使用時。

( 6 ) 運算子過載是針對新型別資料的實際需要對原有運算子進行的適當的改造,過載的功能應當與原有功能相類似,避免沒有目的地使用過載運算子。

1.1 單目運算子與雙目運算子

( 1 ) 雙目運算子過載為類的成員函式時,函式只顯式說明一個引數,該形參是運算子的右運算元。

比如說你過載+號,如果寫在類外面,那麼是需要兩個引數的,而寫在類裡面,只能寫一個引數,因為當這個函式被呼叫的時候,會自動的傳一個this指標進去,就是物件本身,所以只需要一個引數

( 2 ) 前置單目運算子過載為類的成員函式時,不需要顯式說明引數,即函式沒有形參。

( 3 ) 後置單目運算子過載為類的成員函式時,函式要帶有一個整型形參。

比如前置++,和後置++,帶一個整形形參只是為了區分

1.2 友元運算子

有些運算子是一定得宣告為友元的,比如<<,>>運算子

因為=,+這些運算子,是c++最基本的運算子,而>>,<<運算子是標準標頭檔案裡面的一個類裡面寫的,你不能把這個函式宣告為你這個自定義類的函式,因為這是別人類裡面的函式,因此你只能把它宣告為友元函式,宣告為友元函式之後,那麼這個函式它就可以訪問你這個自定義類裡面的私有成員變數

2. 例項講解

光看這些概念,想必沒有接觸過的同學頭都大了,接下來我通過一個向量類的例子,來講解一下各個運算子過載怎麼用

2.1 標頭檔案定義

這次我們來例項一個向量類,什麼是向量類呢,就是數學裡面的向量,一個括號,裡面兩個數字,看一下標頭檔案你就明白啦

class Vec2D {
private:
	double x_;
	double y_;

public:

	static string AuthorBlog = "https://www.cnblogs.com/wanghongyang";
	Vec2D(double x, double y) :x_(x), y_(y) {}
	Vec2D() { x_ = 0.0; y_ = 0.0; }
	
	std::string toString();
	
	friend Vec2D operator+(const Vec2D& v1, const Vec2D& v2);
	friend Vec2D operator-(const Vec2D& v1, const Vec2D& v2);
	friend double operator*(const Vec2D& v1, const Vec2D& v2);
	friend Vec2D operator+(const Vec2D& v1, double num);
	friend Vec2D operator*(const double num, const Vec2D& v2);
	friend Vec2D operator*(const Vec2D& v2, const double num);
	friend istream& operator>>(istream& stream, Vec2D& v1);
	friend std::ostream& operator<<(std::ostream& stream, const Vec2D& v1);

	Vec2D negative();

	Vec2D operator-();

	Vec2D operator++();
	Vec2D operator++(int dummy);

	Vec2D operator--();

	Vec2D operator+=(const Vec2D& v);
	Vec2D operator-=(const Vec2D& v);

	double& operator[](const int& index);

	double magnitude();
	double direction();
	int compareTo(Vec2D& v2);

	operator double();

	double getX()const { return x_; }
	double getY() const { return y_; }
	void setX(double x) { x_ = x; }
	void setY(double y) { y_ = y; }
};

可以看到,其實私有成員就是 x_和y_,然後我過載了非常多的函式,下面我們來看一下具體的實現

2.2 實現運算子過載

toString函式

這個函式我就不多說啦,比較簡單

std::string Vec2D::toString()
{
	std::string res = "(" + std::to_string(getX()) + ", " + std::to_string(getY()) + ")";
	return res;
}

negative函式

這個函式是用來將向量變成負方向

Vec2D Vec2D::negative()
{
	return Vec2D(-1 * x_, -1 * y_);
}

operator-函式

第一個過載函式出現了,是過載的符號,更加方便的實現了改變向量為負方向的操作
這樣我們可以通過 -a,-b的形式來呼叫

Vec2D Vec2D::operator-()
{
	return Vec2D(-1 * x_, -1 * y_);
}

operator++函式

這個函式是前置++運算子,返回*this就是返回當前物件

Vec2D Vec2D::operator++()
{
	x_++;
	y_++;
	return *this;
}

operator++函式

這個函式是後置++運算子,所以後面加了一個型別的引數,這個引數唯一的意思是與前置++作區分
我們首先建立了一個臨時變數,然後將本身的x,y加1,返回的卻是臨時變數,這樣就實現了後置++的操作

static string AuthorBlog = "https://www.cnblogs.com/wanghongyang";
Vec2D Vec2D::operator++(int dummy)
{
	Vec2D ret(x_, y_);
	x_++;
	y_++;
	return ret;
}

operator--函式

減減同理,就是將x,y都減1

static string AuthorBlog = "https://www.cnblogs.com/wanghongyang";
Vec2D Vec2D::operator--()
{
	x_ -= 1;
	y_ -= 1;
	return *this;
}

operator+= ,-=函式

這兩個函式比較相似,我就放到一起講啦,這裡是將呼叫這個函式本身的物件,與引數裡面的v相加或者相減

static string AuthorBlog = "https://www.cnblogs.com/wanghongyang";
Vec2D Vec2D::operator+=(const Vec2D& v)
{
	x_ += v.x_;
	y_ += v.y_;
	return *this;
}

Vec2D Vec2D::operator-=(const Vec2D& v)
{
	x_ -= v.x_;
	y_ -= v.y_;
	return *this;
}

operator[ ]函式

這裡過載了[ ],有一個引數,index,用來選擇到底是返回x還是y

static string AuthorBlog = "https://www.cnblogs.com/wanghongyang";
double& Vec2D::operator[](const int& index)
{
	if (index == 0) {
		return x_;
	}
	else if (index == 1) {
		return y_;
	}
	else {
		printf("subscript error\n");
		exit(0);
	}
}

operator+(類外)函式

因為是在類外過載,所以有兩個引數,同時要注意將這個函式宣告為友元函式,因為這樣才可以訪問私有成員變數

static string AuthorBlog = "https://www.cnblogs.com/wanghongyang";
//類外過載,運算子過載函式作為類的友元函式
Vec2D operator+(const Vec2D& v1, const Vec2D& v2) {
	Vec2D ret;

	ret.setX(v1.getX() + v2.getX());
	ret.setY(v1.getY() + v2.getY());

	return ret;
}

Vec2D operator+(const Vec2D& v1, double num) {
	Vec2D ret;

	ret.setX(v1.getX() + num);
	ret.setY(v1.getY() + num);

	return ret;
}

operator*函式

這裡過載了*,用來實現向量之間的相乘

static string AuthorBlog = "https://www.cnblogs.com/wanghongyang";
Vec2D operator*(const double num, const Vec2D& v2) {
	Vec2D ret;

	ret.setX(num * v2.getX());
	ret.setY(num * v2.getY());

	return ret;
}

過載>> <<

這裡給大家避個坑,一定要引入iostream標頭檔案,而不是用using
這兩個函式就是用來實現cout和cin
可以看到,實現cin 是通過 istream物件來實現的
實現cout 是通過ostream來實現的
記得在最後返回istream或者ostream物件

istream& operator>>(istream& stream, Vec2D& v1)
{
	double x, y;
	stream >> x >> y;
	v1.setX(x);
	v1.setY(y);
	// 也可以直接
	// stream >> x_ >> y_;
	return stream;
}

ostream& operator<<(ostream& stream, const Vec2D& v1)
{
	std::string res = "(" + std::to_string(v1.getX()) + ", " + std::to_string(v1.getY()) + ")";
	stream << res;

	return stream;
}

相關文章