使用普通函数重载运算符

在上一课中,我们将 operator+ 实现为友元函数:

#include <iostream>

class Cents
{
private:
  int m_cents{};

public:
  Cents(int cents)
    : m_cents{ cents }
  {}

  // 使用友元函数实现 Cents + Cents
  friend Cents operator+(const Cents& c1, const Cents& c2);

  int getCents() const { return m_cents; }
};

// 注意:此函数并非成员函数!
Cents operator+(const Cents& c1, const Cents& c2)
{
  // 利用 Cents 构造函数以及内建的 operator+(int, int)
  // 由于是友元函数,可直接访问 m_cents
  return { c1.m_cents + c2.m_cents };
}

int main()
{
  Cents cents1{ 6 };
  Cents cents2{ 8 };
  Cents centsSum{ cents1 + cents2 };
  std::cout << "I have " << centsSum.getCents() << " cents.\n";

  return 0;
}

使用友元函数重载运算符十分便利,因为它能直接访问参与运算的类的内部成员。在上面的示例中,友元函数 operator+ 直接使用了 m_cents

然而,如果无需直接访问私有成员,就可以将重载运算符写成普通函数。上述 Cents 类已提供了访问函数 getCents(),使得我们可以在不访问私有成员的情况下完成运算。因此,可以把 operator+ 写成非友元:

#include <iostream>

class Cents
{
private:
  int m_cents{};

public:
  Cents(int cents)
    : m_cents{ cents }
  {}

  int getCents() const { return m_cents; }
};

// 注意:此函数既非成员函数,也非友元函数!
Cents operator+(const Cents& c1, const Cents& c2)
{
  // 利用 Cents 构造函数以及内建的 operator+(int, int)
  // 无需直接访问私有成员
  return Cents{ c1.getCents() + c2.getCents() };
}

int main()
{
  Cents cents1{ 6 };
  Cents cents2{ 8 };
  Cents centsSum{ cents1 + cents2 };
  std::cout << "I have " << centsSum.getCents() << " cents.\n";

  return 0;
}

由于普通函数与友元函数在行为上几乎相同(区别仅在于对私有成员的访问权限),通常我们不会刻意区分二者。唯一需要注意的是:友元函数在类内的声明同时充当了原型;而普通函数版本则必须在别处显式提供原型。

示例文件结构如下:

Cents.h

#ifndef CENTS_H
#define CENTS_H

class Cents
{
private:
  int m_cents{};

public:
  Cents(int cents)
    : m_cents{ cents }
  {}

  int getCents() const { return m_cents; }
};

// 需要显式提供 operator+ 的原型,以便其他文件识别该重载
Cents operator+(const Cents& c1, const Cents& c2);

#endif

Cents.cpp

#include "Cents.h"

// 注意:此函数既非成员函数,也非友元函数!
Cents operator+(const Cents& c1, const Cents& c2)
{
  // 利用 Cents 构造函数以及内建的 operator+(int, int)
  // 无需直接访问私有成员
  return { c1.getCents() + c2.getCents() };
}

main.cpp

#include "Cents.h"
#include <iostream>

int main()
{
  Cents cents1{ 6 };
  Cents cents2{ 8 };
  Cents centsSum{ cents1 + cents2 }; // 若无 Cents.h 中的原型,此处将编译失败
  std::cout << "I have " << centsSum.getCents() << " cents.\n";

  return 0;
}

最佳实践

若现有成员函数已能提供足够信息,应优先使用普通函数而非友元函数来重载运算符(越少函数触及类内部越好)。但不要为了把运算符重载为普通函数而额外增加访问函数!

最佳实践
如果无需额外函数即可用普通函数完成运算符重载,则优先采用普通函数而非友元函数。

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"