类的运算符重载是C++一个非常基础也很重要的语法,很多文章都有说到,但是,很多文章里面的写法其实都是不对的,或者说不完美的。
一般的运算符,可以有两种重载方式:
1、作为类的成员函数;
2、作为类的友元函数。
作为成员函数,因为有隐藏的this指针,所以二元运算符只需要一个参数即可;作为友元,二元运算符则需要两个参数。
当然,有一些运算符的重载必须用友元,例如标准输入输出>>、<<等。
请看下面一段代码:
#include <iostream>
class Test
{
public:
Test(int i) : m_iValue(i) { std::cout << "构造函数\n"; }
Test(const Test& t) { m_iValue = t.m_iValue; std::cout << "copy构造函数\n"; }
bool operator<(const Test& t)
{
return this->m_iValue < t.m_iValue;
}
friend std::ostream& operator<<(std::ostream& os, const Test& t)
{
std::cout << "value = " << t.m_iValue;
return os;
}
private:
int m_iValue{ 0 };
};
template<typename T>
T Max(T left, T right)
{
return left < right ? right : left;
}
int main()
{
using namespace std;
Test t1(20), t2(18);
cout << "----------------------------\n";
cout << Max<Test>(t1, t2) << "\n";
system("pause");
return 0;
}
执行结果如下(vs2019):
这段代码很简单,定义了一个Test类,重载了"<"运算符,用的是类的成员函数的方法;还重载了标准输出运算符<<,打印m_iValue的数字;
接着定义了一个很简单的模板函数Max,返回两个对象的较大一个。
主函数里,定义了Test的两个对象t1和t2,调用Max模板函数,打印较大的一个。
从执行结果可以看出来,Max的设计很不好,因为参数是值传递的,返回值也是一个临时对象,会调用拷贝构造函数,如果类很大,开销会很大。
Max函数其实是返回两个较大对象中的一个,说白了完全可以返回对象本身,而函数内部,并不会对参数进行任何修改,所以完全可以将Max模板函数改成如下形式,提高效率:
template<typename T>
const T& Max(const T& left, const T& right)
{
return left < right ? right : left;
}
但是!!!会发现编译不过:
从错误提示可以看出来,似乎和const有关系。问题出在哪里呢?
其实问题出在"<"的重载上:bool operator<(const Test& t);
仔细分析,“const Test& t”其实是<的右操作数,那左操作数呢?当然是“*this”,如何限定这个"*this"也是const,很简单,在重载“<”函数的后面加上const修饰,完整代码如下:
#include <iostream>
class Test
{
public:
Test(int i) : m_iValue(i) { std::cout << "构造函数\n"; }
Test(const Test& t) { m_iValue = t.m_iValue; std::cout << "copy构造函数\n"; }
bool operator<(const Test& t) const //注意,这里函数最后的const是修饰这个函数的,表示内部不会修改任何成员变量
{
return this->m_iValue < t.m_iValue;
}
friend std::ostream& operator<<(std::ostream& os, const Test& t)
{
std::cout << "value = " << t.m_iValue;
return os;
}
private:
int m_iValue{ 0 };
};
template<typename T>
const T& Max(const T& left, const T& right)
{
return left < right ? right : left;
}
int main()
{
using namespace std;
Test t1(20), t2(18);
cout << "----------------------------\n";
cout << Max<Test>(t1, t2) << "\n";
system("pause");
return 0;
}
执行结果如下:
少了三次拷贝构造。
除此之外,加上引用后,Max还能用于那些不能拷贝构造的类对象上!
如果采用类的成员函数方式重载运算符,如果运算符不会修改成员变量,一定要用const修改运算符重载函数!
否则,不仅仅是这个Max函数,很多东西都不能用。比如,set容器、map容器的key、标准库函数std::max等等,C++的类设计,不那么容易。
当然,上面的例子中,如果改成友元函数,就不会涉及这个问题:
friend bool operator<(const Test& left, const Test& right)
{
return left.m_iValue < right.m_iValue;
}
执行结果与上面相同。