运算符重载 C++ 中的友元函数与成员函数
friend vs member functions in Operator Overloading C++
之前,我了解了如何将 C++ 中的运算符重载为 class 的成员函数和友元函数。虽然,我知道如何使用这两种技术在 C++ 中重载运算符。但我仍然很困惑**哪个更好**?重载运算符的成员函数或友元函数,我应该使用哪个,为什么?
请指导我!非常感谢您的回复。我将很高兴并感谢您的回答。
这可能没有固定的规则,所以我只是给出我的意见:尽可能使用成员函数进行运算符重载。这样做的主要原因是我将运算符视为 class 的组成部分,并且 class 的逻辑更加本地化。
选择自由函数进行运算符重载的原因是为了保持 class 尽可能精简(仅在需要时使用 friend)。
在某些情况下,运营商不能成为成员,因此在这种情况下不进行讨论。主要成员函数将 class 实例作为第一个参数,这并不总是可能的(例如 operator<< for i/o)。
会员功能和好友不是两个对立的,只能选一个。它们都可以使用,例如通过实现一个并让另一个调用它来避免重复代码。
struct A
{
A operator+=(A const & second);
};
A operator+(A const &first, A const &second)
{
A temp(first);
temp += second;
return temp;
}
但是如果你想为 one 函数选择是否需要成员函数,这里是我遵循的规则:实现非成员函数(friend
或不)当我有一个 二元运算符 来尊重我们期望的对称性时。示例:使用 A
和将 int
隐式转换为 A
的可能性(使用带有 int
的构造函数),如果我有一个成员函数,将会有什么结果。
A a1, a2;
a1 + a2; // OK
a1 + 42; // OK
42 + a2; // KO
有了自由函数,就有了结果。
A a1, a2;
a1 + a2; // Ok
a1 + 42; // Ok
42 + a2; // Ok
使用这种可能性的具体 C++ class 是 std::string
。
#include <iostream>
#include <string>
int main()
{
std::string s {"World"};
// Works with absolutely no problem.
std::string chaine = "Hello " + s;
std::cout << chaine << std::endl;
}
总而言之,这里有一个 good link 可以帮助你。
选择的不是 "member or friend",而是 "member or non-member"。
(友谊经常被过度使用,而且通常在学校过早地教授。)
这是因为您总是可以添加一个 public 自由函数可以调用的成员函数。
例如:
class A
{
public:
explicit A(int y) : x(y) {}
A plus(const A& y) const { return A{x + y.x}; }
private:
int x;
};
A operator+(const A& lhs, const A& rhs) { return lhs.plus(rhs); }
至于如何选择:如果运算符不将class的实例作为其左手操作数,则必须是一个自由函数,否则这在很大程度上取决于个人品味(或者编码标准,如果你不是一个人的话)。
示例:
// Can't be a member because the int is on the left.
A operator+ (int x, const A& a) { return A{x} + a; }
对于具有相应变异运算符的运算符(如 +
和 +=
),通常将变异运算符作为成员,将另一个作为非成员:
class B
{
public:
explicit B(int y) : x(y) {}
B& operator+= (const B& y) { x += y.x; return *this; }
private:
int x;
};
B operator+(B lhs, const B& rhs) { return lhs += rhs; }
当然你也可以拼写出来:
class C
{
public:
explicit C(int y) : x(y) {}
C& add(const C& y) { x += y.x; return *this; }
private:
int x;
};
C& operator+=(C& lhs, const C& rhs) { return lhs.add(rhs); }
C operator+(C lhs, const C& rhs) { return lhs += rhs; }
不是品味问题,但某些用例显然需要两种方式之一。
当运算符仅将您正在设计的 class 的成员作为参数时,封装投票使用成员函数。示例:添加两个对象:
class A {
...
A operator + (const class A& other); // naturally a member function
...
};
相反,当你写的class的成员是运算符的第二个参数时,你只能使用友元函数
std::outstream& operator << (std::outstream& out, const class A& a);
class A {
...
friend std::outstream& operator << (std::outstream& out, const class A& a); // must be friend here
};
它最多可以是 std::outstream
的成员,但是当 std::outstream
class 在标准 C++ 库中创建时 A
class 不存在...
之前,我了解了如何将 C++ 中的运算符重载为 class 的成员函数和友元函数。虽然,我知道如何使用这两种技术在 C++ 中重载运算符。但我仍然很困惑**哪个更好**?重载运算符的成员函数或友元函数,我应该使用哪个,为什么? 请指导我!非常感谢您的回复。我将很高兴并感谢您的回答。
这可能没有固定的规则,所以我只是给出我的意见:尽可能使用成员函数进行运算符重载。这样做的主要原因是我将运算符视为 class 的组成部分,并且 class 的逻辑更加本地化。
选择自由函数进行运算符重载的原因是为了保持 class 尽可能精简(仅在需要时使用 friend)。
在某些情况下,运营商不能成为成员,因此在这种情况下不进行讨论。主要成员函数将 class 实例作为第一个参数,这并不总是可能的(例如 operator<< for i/o)。
会员功能和好友不是两个对立的,只能选一个。它们都可以使用,例如通过实现一个并让另一个调用它来避免重复代码。
struct A
{
A operator+=(A const & second);
};
A operator+(A const &first, A const &second)
{
A temp(first);
temp += second;
return temp;
}
但是如果你想为 one 函数选择是否需要成员函数,这里是我遵循的规则:实现非成员函数(friend
或不)当我有一个 二元运算符 来尊重我们期望的对称性时。示例:使用 A
和将 int
隐式转换为 A
的可能性(使用带有 int
的构造函数),如果我有一个成员函数,将会有什么结果。
A a1, a2;
a1 + a2; // OK
a1 + 42; // OK
42 + a2; // KO
有了自由函数,就有了结果。
A a1, a2;
a1 + a2; // Ok
a1 + 42; // Ok
42 + a2; // Ok
使用这种可能性的具体 C++ class 是 std::string
。
#include <iostream>
#include <string>
int main()
{
std::string s {"World"};
// Works with absolutely no problem.
std::string chaine = "Hello " + s;
std::cout << chaine << std::endl;
}
总而言之,这里有一个 good link 可以帮助你。
选择的不是 "member or friend",而是 "member or non-member"。
(友谊经常被过度使用,而且通常在学校过早地教授。)
这是因为您总是可以添加一个 public 自由函数可以调用的成员函数。
例如:
class A
{
public:
explicit A(int y) : x(y) {}
A plus(const A& y) const { return A{x + y.x}; }
private:
int x;
};
A operator+(const A& lhs, const A& rhs) { return lhs.plus(rhs); }
至于如何选择:如果运算符不将class的实例作为其左手操作数,则必须是一个自由函数,否则这在很大程度上取决于个人品味(或者编码标准,如果你不是一个人的话)。
示例:
// Can't be a member because the int is on the left.
A operator+ (int x, const A& a) { return A{x} + a; }
对于具有相应变异运算符的运算符(如 +
和 +=
),通常将变异运算符作为成员,将另一个作为非成员:
class B
{
public:
explicit B(int y) : x(y) {}
B& operator+= (const B& y) { x += y.x; return *this; }
private:
int x;
};
B operator+(B lhs, const B& rhs) { return lhs += rhs; }
当然你也可以拼写出来:
class C
{
public:
explicit C(int y) : x(y) {}
C& add(const C& y) { x += y.x; return *this; }
private:
int x;
};
C& operator+=(C& lhs, const C& rhs) { return lhs.add(rhs); }
C operator+(C lhs, const C& rhs) { return lhs += rhs; }
不是品味问题,但某些用例显然需要两种方式之一。
当运算符仅将您正在设计的 class 的成员作为参数时,封装投票使用成员函数。示例:添加两个对象:
class A {
...
A operator + (const class A& other); // naturally a member function
...
};
相反,当你写的class的成员是运算符的第二个参数时,你只能使用友元函数
std::outstream& operator << (std::outstream& out, const class A& a);
class A {
...
friend std::outstream& operator << (std::outstream& out, const class A& a); // must be friend here
};
它最多可以是 std::outstream
的成员,但是当 std::outstream
class 在标准 C++ 库中创建时 A
class 不存在...