注:答案是按照特定的顺序给出的,但由于许多用户根据投票而不是给出答案的时间对答案进行排序,以下是答案的索引,以最有意义的顺序排列:

C中运算符重载的一般语法++C语言中运算符重载的三个基本规则++成员与非成员之间的决定常见操作员过载赋值运算符输入和输出运算符函数调用运算符比较运算符算术运算符阵列订阅指针类类型的运算符转换运算符重载新建和删除

(注意:这是Stack Overflow的C++FAQ的一个条目。如果你想批评以这种形式提供FAQ的想法,那么在meta上发布这一切的帖子就是这样做的地方。该问题的答案在C++聊天室中进行监控,FAQ想法最初就在那里开始,所以你的答案很可能会被提出该想法的人阅读。)


当前回答

转换运算符(也称为用户定义转换)

在C++中,您可以创建转换运算符,这些运算符允许编译器在您的类型和其他定义的类型之间进行转换。转换运算符有两种类型,隐式和显式。

隐式转换运算符(C++98/C++03和C++11)

隐式转换运算符允许编译器将用户定义类型的值隐式转换(如int和long之间的转换)为其他类型。

下面是一个带有隐式转换运算符的简单类:

class my_string {
public:
  operator const char*() const {return data_;} // This is the conversion operator
private:
  const char* data_;
};

隐式转换运算符和单参数构造函数一样,都是用户定义的转换。当试图匹配对重载函数的调用时,编译器将授予一个用户定义的转换。

void f(const char*);

my_string str;
f(str); // same as f( str.operator const char*() )

一开始,这似乎非常有用,但问题是,隐式转换甚至在不需要时开始。在以下代码中,将调用void f(const char*),因为my_string()不是左值,因此第一个不匹配:

void f(my_string&);
void f(const char*);

f(my_string());

初学者很容易误解这一点,即使是有经验的C++程序员有时也会感到惊讶,因为编译器选择了他们没有怀疑的重载。这些问题可以通过显式转换运算符来缓解。

显式转换运算符(C++11)

与隐式转换运算符不同,显式转换运算符不会在您不期望的时候出现。以下是一个带有显式转换操作符的简单类:

class my_string {
public:
  explicit operator const char*() const {return data_;}
private:
  const char* data_;
};

请注意显式。现在,当您尝试从隐式转换运算符执行意外代码时,会出现编译器错误:

prog.cpp: In function ‘int main()’:
prog.cpp:15:18: error: no matching function for call to ‘f(my_string)’
prog.cpp:15:18: note: candidates are:
prog.cpp:11:10: note: void f(my_string&)
prog.cpp:11:10: note:   no known conversion for argument 1 from ‘my_string’ to ‘my_string&’
prog.cpp:12:10: note: void f(const char*)
prog.cpp:12:10: note:   no known conversion for argument 1 from ‘my_string’ to ‘const char*’

要调用显式转换运算符,必须使用static_cast、C样式转换或构造函数样式转换(即T(value))。

然而,有一个例外:允许编译器隐式转换为bool。此外,编译器在转换为bool后不允许再进行一次隐式转换(编译器一次允许进行2次隐式转化,但最多只能进行1次用户定义的转化)。

因为编译器不会抛出“过去”布尔值,所以显式转换运算符现在不再需要安全布尔值习惯用法。例如,C++11之前的智能指针使用安全布尔习惯用法来防止转换为整型。在C++11中,智能指针改用显式运算符,因为编译器在将类型显式转换为bool后,不允许隐式转换为整型。

继续重载新建和删除。

其他回答

C语言中运算符重载的三个基本规则++

当谈到C++中的运算符重载时,您应该遵循三个基本规则。与所有这些规则一样,确实有例外。有时,人们偏离了这些准则,其结果并不坏,但这种积极的偏离很少。至少,我所看到的100个这种偏差中有99个是不合理的。然而,它也可能是千分之999。所以你最好遵守以下规则。

每当运算符的含义不明显且无可争议时,它就不应过载。相反,请提供一个具有精心选择的名称的函数。基本上,重载运算符的首要原则是:不要这样做。这可能看起来很奇怪,因为关于运算符重载有很多事情要知道,所以很多文章、书籍章节和其他文本都涉及到这一切。但是,尽管有这些看似明显的证据,但只有极少数情况下运算符重载是合适的。原因是,实际上很难理解运算符应用程序背后的语义,除非在应用程序域中使用运算符是众所周知且无可争议的。与人们普遍的看法相反,事实并非如此。始终遵循运算符众所周知的语义。C++对重载运算符的语义没有任何限制。编译器将乐于接受实现二进制+运算符的代码,以从其右操作数中减去。然而,这样的运算符的用户永远不会怀疑表达式a+b会从b中减去a。当然,这假设应用程序域中运算符的语义是无可争议的。始终提供一组相关操作中的所有操作。操作员相互关联,并与其他操作相关。如果您的类型支持a+b,用户也可以调用a+=b。如果它支持前缀increment++a,那么他们也希望a++也能工作。如果他们能检查a是否<b,他们肯定也会检查a是否>b。如果他们能复制构造你的类型,他们希望赋值也能起作用。


继续执行成员与非成员之间的决定。

为什么用于将对象流到std::cout或文件的运算符<<函数不能作为成员函数?

假设你有:

struct Foo
{
   int a;
   double b;

   std::ostream& operator<<(std::ostream& out) const
   {
      return out << a << " " << b;
   }
};

鉴于此,您不能使用:

Foo f = {10, 20.0};
std::cout << f;

由于运算符<<被重载为Foo的成员函数,因此运算符的LHS必须是Foo对象。这意味着,您需要使用:

Foo f = {10, 20.0};
f << std::cout

这是非常不直观的。

如果将其定义为非成员函数,

struct Foo
{
   int a;
   double b;
};

std::ostream& operator<<(std::ostream& out, Foo const& f)
{
   return out << f.a << " " << f.b;
}

您将能够使用:

Foo f = {10, 20.0};
std::cout << f;

这是非常直观的。

C中运算符重载的一般语法++

您不能更改C++中内置类型的运算符的含义,只能为用户定义的类型重载运算符1。也就是说,至少有一个操作数必须是用户定义的类型。与其他重载函数一样,运算符只能为某组参数重载一次。

并非所有的运算符都可以在C++中重载。不能重载的运算符包括:.::sizeof typeid.*和C++中唯一的三元运算符,?:

在C++中可以重载的运算符有:

算术运算符:+-*/%和+=-=*=/=%=(所有二进制中缀);+-(一元前缀);++--(一元前缀和后缀)位操作:&|^<<>>和&=|=^=<<>>=(所有二进制中缀);~(一元前缀)布尔代数:==!=<><>=|&&&(所有二进制中缀)!(一元前缀)内存管理:新建[]删除删除[]隐式转换运算符杂项:=[]->->*,(所有二进制中缀);*&(全一元前缀)()(函数调用,n元中缀)

然而,您可以重载所有这些并不意味着您应该这样做。请参阅运算符重载的基本规则。

在C++中,运算符以具有特殊名称的函数的形式重载。与其他函数一样,重载运算符通常可以实现为其左操作数类型的成员函数或非成员函数。您是否可以自由选择或绑定使用其中一个取决于几个条件。2应用于对象x的一元运算符@3被调用为运算符@(x)或x.operator@()。应用于对象x和y的二进制中缀运算符@被调用为操作符@(x,y)或x.运算符@(y)。4

作为非成员函数实现的运算符有时是其操作数类型的朋友。

1“用户定义”一词可能有点误导。C++区分了内置类型和用户定义类型。前者属于例如int、char和double;所有struct、class、union和enum类型都属于后者,包括标准库中的类型,即使它们本身不是由用户定义的。

2这将在本常见问题解答的后面部分介绍。

3@在C++中不是有效的运算符,这就是为什么我将它用作占位符。

C++中唯一的三元运算符不能重载,唯一的n元运算符必须始终作为成员函数实现。


继续学习C++中运算符重载的三个基本规则。

转换运算符(也称为用户定义转换)

在C++中,您可以创建转换运算符,这些运算符允许编译器在您的类型和其他定义的类型之间进行转换。转换运算符有两种类型,隐式和显式。

隐式转换运算符(C++98/C++03和C++11)

隐式转换运算符允许编译器将用户定义类型的值隐式转换(如int和long之间的转换)为其他类型。

下面是一个带有隐式转换运算符的简单类:

class my_string {
public:
  operator const char*() const {return data_;} // This is the conversion operator
private:
  const char* data_;
};

隐式转换运算符和单参数构造函数一样,都是用户定义的转换。当试图匹配对重载函数的调用时,编译器将授予一个用户定义的转换。

void f(const char*);

my_string str;
f(str); // same as f( str.operator const char*() )

一开始,这似乎非常有用,但问题是,隐式转换甚至在不需要时开始。在以下代码中,将调用void f(const char*),因为my_string()不是左值,因此第一个不匹配:

void f(my_string&);
void f(const char*);

f(my_string());

初学者很容易误解这一点,即使是有经验的C++程序员有时也会感到惊讶,因为编译器选择了他们没有怀疑的重载。这些问题可以通过显式转换运算符来缓解。

显式转换运算符(C++11)

与隐式转换运算符不同,显式转换运算符不会在您不期望的时候出现。以下是一个带有显式转换操作符的简单类:

class my_string {
public:
  explicit operator const char*() const {return data_;}
private:
  const char* data_;
};

请注意显式。现在,当您尝试从隐式转换运算符执行意外代码时,会出现编译器错误:

prog.cpp: In function ‘int main()’:
prog.cpp:15:18: error: no matching function for call to ‘f(my_string)’
prog.cpp:15:18: note: candidates are:
prog.cpp:11:10: note: void f(my_string&)
prog.cpp:11:10: note:   no known conversion for argument 1 from ‘my_string’ to ‘my_string&’
prog.cpp:12:10: note: void f(const char*)
prog.cpp:12:10: note:   no known conversion for argument 1 from ‘my_string’ to ‘const char*’

要调用显式转换运算符,必须使用static_cast、C样式转换或构造函数样式转换(即T(value))。

然而,有一个例外:允许编译器隐式转换为bool。此外,编译器在转换为bool后不允许再进行一次隐式转换(编译器一次允许进行2次隐式转化,但最多只能进行1次用户定义的转化)。

因为编译器不会抛出“过去”布尔值,所以显式转换运算符现在不再需要安全布尔值习惯用法。例如,C++11之前的智能指针使用安全布尔习惯用法来防止转换为整型。在C++11中,智能指针改用显式运算符,因为编译器在将类型显式转换为bool后,不允许隐式转换为整型。

继续重载新建和删除。

简而言之,我将提及一些要点,这些要点是我在过去一周学习Python和C++、oops和其他东西时提出的,因此如下所示:

运算符的Arity不能被进一步修改为原来的值!重载运算符只能有一个默认参数,而函数调用运算符不能使用该参数。只有内置运算符可以重载,其余的不能!

有关更多信息,您可以参考以下链接,该链接将您重定向到GeekforGeeks提供的文档。

https://www.geeksforgeeks.org/g-fact-39/