您的当前位置:首页正文

C++之重载运算与类型转换

2024-11-08 来源:个人技术集锦

基本概念

重载的运算符是具有特殊名字的函数:它们的名字由关键字operator和其后要定义的运算符号共同组成。和其他函数一样,重载的运算符也包含返回类型、参数列表以及函数体。

对于一个运算符函数来说,它或者是类的成员,或者至少含有一个类类型的参数

当一个重载的运算符是成员函数时,this绑定到左侧运算对象。成员运算符函数的(显式)参数数量比与运算对象的数量少一个

当我们定义重载的运算符时,必须首先决定是将其声明为类的成员函数还是声明一个普通的非成员函数。在某些时候我们别无选择,因为有的运算符作为普通函数比作为成员更好,有下面原则来选择:

  • 赋值(=)、下标([ ])、 调用(( ))和成员访问箭头(->)运算符必须是成员。
  • 复合赋值运算符一般来说应该是成员,但并非必须,这一 点与赋值运算符略有不同。
  • 改变对象状态的运算符或者与给定类型密切相关的运算符,如递增、递减和解引用
    运算符,通常应该是成员。
  • 具有对称性的运算符可能转换任意一端的运算对象, 例如算术、相等性、关系和位
    运算符等,因此它们通常应该是普通的非成员函数。

当我们把运算符定义成成员函数时,它的左侧运算对象必须是运算符所属类的一个对象,例如:

string s = "world";
string t = s + "!";  //正确
string u = "hi" + s; //错误

使用下面声明的类来解决各种运算符重载

class Test {
public:
	friend ostream& operator<<(ostream&, const Test&);
	friend istream& operator>>(istream&, Test&);
	friend Test operator+(const Test&, const Test&);
	Test& operator+=(const Test&);
	friend bool operator==(const Test&, const Test&);
	friend bool operator!=(const Test&, const Test&);
	int& operator[](int n) { return vec[n]; }
	const int& operator[](int n) const { return vec[n]; }
	Test(){}
	Test(int _data1,int _data2) : data1(_data1),data2(_data2) {}
	~Test() {}
private:
	vector<int> vec;
	int data1;
	double data2;
};

输入和输出运算符

一般声明为类的友元函数,便于访问类的私有函数

重载输出运算符

ostream& operator<<(ostream& os, const Test& test) {
	os << test.data1 << " " << test.data2;
	return os;
}

第一个参数是ostream的非常量引用,非常量是因为向流写入会改变状态,引用是IO对象不允许拷贝,第二个参数是要输出类的对象的常引用。

重载输入运算符

istream& operator>>(istream& is,  Test& test) {
	is >> test.data1  >> test.data2;
	if (is) {
		//...
	}
	else {
		test = Test();
	}
	return is;
}

重载输入运算符必须处理输入可能失败的情况,而输出运算符不需要

输入时的错误:

  • 当流含有错误类型的数据时读取操作可能失败。例如在读取完bookNo后,输入运
    算符假定接下来读入的是两个数字数据,一旦输入的不是数字数据,则读取操作及
    后续对流的其他使用都将失败。
  • 当读取操作到达文件末尾或者遇到输入流的其他错误时也会失败。

赋值运算符

Test& Test::operator+=(const Test& test) {
	data1 += test.data1;
	data2 += test.data2;
	return *this;
}

算术和关系运算符

通常情况下,我们把算术和关系运算符定义成非成员函数以允许对左侧或右侧的运算对象进行转换,因为这些运算符一般不需要改变运算对象的状态,所以形参都是常量的引用。

算数运算符

Test operator+(const Test& test1, const Test& test2) {
	Test newTest = test1;
	newTest += test2;
	return newTest;
}

相等运算符

bool operator==(const Test& test1, const Test& test2) {
	return test1.data1 == test2.data1 && test1.data1 == test2.data2;
}
bool operator!=(const Test& test1, const Test& test2) {
	return !(test1 == test2);
}

下标运算符

	int& operator[](int n) { return vec[n]; }
	const int& operator[](int n) const { return vec[n]; }

递增和递减运算符

//前置
Test& Test::operator++() {
	++data1;
	++data2;
	return *this;
}
Test& Test::operator--() {
	--data1;
	--data2;
	return *this;
}

//后置
Test Test::operator++(int) {
	Test test = *this;
	++*this;
	return test;
}
Test Test::operator--(int) {
	Test test = *this;
	--* this;
	return test;
}

重载、类型转换与运算符

类型转换运算符

operator type() const;
举个例子:

class SmallInt {
public:
	SmallInt(int i = 0): val (i){
	if(i<011i>255)
		throw std: :out_ of_ range ("Bad SmallInt value") ;
	}
	operator int() const { return val; }
private :
	std: :size_ t val;
};

int main(){
	SmallInt si;
	int tmp = si+3;  //首先将si隐式转换成int,然后执行整数的加法
}

在实践中,类很少提供类型转换运算符。在大多数情况下,如果类型转换自动发生,用户可能会感觉比较以外,而不是感觉受到了帮助。

所以C++新标准引入了显式的类型转换运算符

class SmallInt{
public:
	explicit operator int() const {return val;}
}

int main(){
	SmallInt si;
	int tmp = static_cast<int>si+3;
}

该规定存在一个例外,即如果表达式被用作条件,则编译器会将显式的类型转换自动应用于它。换句话说,当表达式出现在下列位置时,显式的类型转换将被隐式地执行:

  • if、while及do语句的条件部分
  • for语句头的条件表达式
  • 逻辑非运算符(!)、 逻辑或运算符(11)、 逻辑与运算符(&&) 的运算对象
  • 条件运算符(? :) 的条件表达式。
显示全文