21-3 使用普通函数重载运算符

在上节课中,我们通过友元函数的方式重载了运算符+:

#include <iostream>

class Cents
{
private:
  int m_cents{};

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

  // add Cents + Cents using a friend function
  friend Cents operator+(const Cents& c1, const Cents& c2);

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

// note: this function is not a member function!
Cents operator+(const Cents& c1, const Cents& c2)
{
  // use the Cents constructor and operator+(int, int)
  // we can access m_cents directly because this is a friend function
  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;
}

image

使用友元函数重载运算符非常便捷,因为它能让你直接访问操作对象的内部成员。在上文最初的Cents示例中,我们通过友元函数版本的operator+直接访问了成员变量m_cents。

但若无需此访问权限,可将重载运算符编写为普通函数。需注意上述Cents类包含访问函数getCents(),该函数允许我们获取m_cents值而无需直接访问私有成员。因此,我们可以将重载的operator+编写为非友元函数:

#include <iostream>

class Cents
{
private:
  int m_cents{};

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

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

// note: this function is not a member function nor a friend function!
Cents operator+(const Cents& c1, const Cents& c2)
{
  // use the Cents constructor and operator+(int, int)
  // we don't need direct access to private members here
  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;
}

image

由于普通函数和友元函数的工作方式几乎完全相同(它们只是对私有成员的访问权限不同),我们通常不会区分它们。唯一的区别在于,类内部声明的友元函数同时充当函数原型。而使用普通函数版本时,则需要自行提供函数原型。

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; }
};

// Need to explicitly provide prototype for operator+ so uses of operator+ in other files know this overload exists
Cents operator+(const Cents& c1, const Cents& c2);

#endif

Cents.cpp:

#include "Cents.h"

// note: this function is not a member function nor a friend function!
Cents operator+(const Cents& c1, const Cents& c2)
{
  // use the Cents constructor and operator+(int, int)
  // we don't need direct access to private members here
  return { c1.getCents() + c2.getCents() };
}

main.cpp:

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

int main()
{
  Cents cents1{ 6 };
  Cents cents2{ 8 };
  Cents centsSum{ cents1 + cents2 }; // without the prototype in Cents.h, this would fail to compile
  std::cout << "I have " << centsSum.getCents() << " cents.\n";

  return 0;
}

image

通常情况下,如果现有成员函数能够实现相同功能,应优先使用普通函数而非友元函数(接触类内部实现的函数越少越好)。但切勿仅为将运算符重载为普通函数而非友元函数而额外添加访问函数!

最佳实践
在无需增加额外函数的情况下,应优先选择以普通函数而非友元函数的形式重载运算符。

posted @ 2026-01-22 08:02  游翔  阅读(1)  评论(0)    收藏  举报