StoneのBLOG

生活这种事情,从来都是自我陶醉

0%

C++中的运算符重载

运算符重载(operator overloading)只是一种”语法上的方便“(syntactic sugar).也就是说它只是另一种函数调用的方式。

运算符重载

运算符重载有成员运算符和非成员运算符之分,那么这个之间有什么区别呢,应该选择哪一种呢?

总的来说,如果没有什么差异,它们应该是成员运算符。这样做强调了运算符和类的结合。当左操作数是当前类的对象时,运算符会工作的很好。

但有的时候左侧操作数是别的类对象。这种情况通常出现在输入输出流重载operator<<>> 时。因为输入输出流是一个基本C++库。

这里就说到了关键了。为了应付各种数据类型的输出,输入输出流的重载就很重要了。

1
// 代码之后贴

这里我先暂停一下,说一说为什么要认识运算符重载这个问题。

举例来说,cout这个类是我学C++最早接触的对象了之一了,但是却很不是理解。其实即就是cout<<运算符进行了重载。

我自己创建了一个Record类,希望这个类能对接在<<后面的数据进行一些操作,比如说接收数据什么的。

1
2
Record << 2;
Record << "str string";

想这样把后面的数据接收保存在类内变量中。就这样我遇见了下面的代码:

1
2
3
4
5
6
7
8
// 在Record类内

template<typename T>
Record& operator<<(const T& data)
{
m_message << data;
return *this;
}

代表着混沌势力的模板登场了(对我来说)。这里就涉及到了函数模板的问题。我看到上面的代码的时候第一反应是,这段代码能直接用?

答案是肯定的,像类模板的使用那样,指定明确的模板参数类型来特化函数模板使用是可行的,也就是我觉得应该这样做的,但是让编译器从函数的参数中推断出它们的类型将会更方便。

后面的内容就更多了,在别的模板章节中展开更好。

回到上面的代码,m_message << data 的m_message是什么类型合适呢?是ostringstream。是C++标准库的东西,这样就省得自己一个一个重载数据类型了。方法是个好方法,但是最后我没用上。因为要处理宽字符的问题,涉及到宽窄字符互相转换的问题,试来试去总是出问题,我就Pass掉了,全部交给UE4里的FString类型和TEXT()来处理了。

运算符重载时的friend关键字

当我们在类(class)的定义外使用操作符(operator)的时候,操作符两边的参数都可以进行类型的隐式转换(implicit type conversions)。但是在类内定义操作符的时候,只有右手边的参数可以执行类型的隐式转换。

比如说我们声明一个这样的类:

1
2
3
4
5
6
class Message {
std::string content;
public:
Message(const std::string& str);
bool operator==(const std::string& rhs) const;
};

这种方式对运算符的重载,允许我们这样操作:

1
2
3
4
5
Message message("Test");
std::string msg("Test");
if (message == msg) { // Message类可以和std::string类型的字符串比较
// do stuff...
}

但是当我们把比较顺序换一下:

1
if (msg == message) { // this won't compile

就会编译出错,原因很明显,在类内定义操作符的时候只有右手边的参数可以执行隐式转换,上面明显没有匹配的类型。
解决方案就是增加一个匹配类型:

1
2
3
4
5
6
7
class Message {
std::string content;
public:
Message(const std::string& str);
bool operator==(const std::string& rhs) const;
friend bool operator==(const std::string& lhs, const Message& rhs);
};

或者声明一个隐式转换操作符转到我们想要的类型(嘛这里不是很熟悉,我也没完全明白):

1
2
3
4
5
6
7
class Message {
std::string content;
public:
Message(const std::string& str);
bool operator==(const std::string& rhs) const;
operator std::string() const;
};

除此之外,还有一个需要注意的是,当我们重载操作符需要参照到类的私有成员的时候也必须使用friend限定。

至于操作符重载最好是以内联函数的形式这种结论我没有依据,还是要注意一些为好。

参考资料