跳转到内容

C++ 编程/类/良好类

来自维基教科书,开放世界中的开放书籍

什么是“良好”(容器安全) 类?

[编辑 | 编辑源代码]

一个“良好”的类考虑了以下函数的使用

1. 复制构造函数。
2. 赋值运算符。
3. 相等运算符。
4. 不相等运算符。

类声明

[编辑 | 编辑源代码]
class Nice
{
public:
    Nice(const Nice &Copy);
    Nice &operator= (const Nice &Copy);
    bool operator== (const Nice &param) const;
    bool operator!= (const Nice &param) const;
};

一个“良好”的类也可以称为容器安全类。许多容器,例如我们将在后面看到的标准模板库(STL) 中的那些容器,在与你的类的对象交互时使用复制构造和赋值运算符。如果默认行为(按成员进行逐个复制,而不是按二进制复制)不可取或不足以正确地复制/构造你的对象,则只需声明和定义赋值运算符和复制构造函数。

一个普遍的经验法则是,如果默认的按成员进行逐个复制操作不适用于你的对象,那么你应该定义一个合适的复制构造函数和赋值运算符。如果两者中任何一个被定义,则它们都需要。

复制构造函数

[编辑 | 编辑源代码]

复制构造函数的目的是允许程序员执行与赋值运算符相同的指令,特殊情况是知道调用者正在初始化/构造而不是复制。

在使用复制构造函数时使用 explicit 关键字也是一个好习惯,以防止意外的隐式类型转换。

示例

class Nice
{
public:
  explicit Nice(int _a) : a(_a)
  {
    return;
  }
private:
  int a;
};

class NotNice
{
public:
  NotNice(int _a) :  a(_a)
  {
    return;
  }
private:
  int a;
};

int main()
{
  Nice proper = Nice(10); //this is ok
  Nice notproper = 10; //this will result in an error
  NotNice eg = 10; //this WILL compile, you may not have intended this conversion
  return 0;
}

相等运算符

[编辑 | 编辑源代码]

相等运算符表示“这个对象是否等于那个对象?”。 什么构成相等由程序员决定。 这是如果你想用你类中的对象使用相等运算符的必要条件。

但是,在大多数应用(例如数学)中,通常情况下,对不相等进行编码比对相等进行编码更容易。在这种情况下,以下代码可以为相等编写。

inline bool Nice::operator== (const Nice& param) const
{
    return !(*this != param);
}

不相等运算符

[编辑 | 编辑源代码]

不相等运算符表示“这个对象是否不等于那个对象?”。 什么构成不相等由程序员决定。 这是如果你想用你类中的对象使用不相等运算符的必要条件。

但是,在某些应用中,对相等进行编码比对不相等进行编码更容易。在这种情况下,以下代码可以为不相等编写。

inline bool Nice::operator!= (const Nice& param) const
{
    return !(*this == param);
}

如果关于(不)相等运算符具有不同效率(无论哪种)的陈述对你来说完全是胡说八道,请考虑通常,所有对象属性必须匹配,两个对象才能被认为是相等的。
通常,只有当两个对象要被认为是不相等时,一个对象属性必须不同。对于相等和不相等运算符,这并不意味着一个是比另一个更快的。

但是,请注意,使用上面定义的相等和不相等函数会导致无限递归循环,因此必须小心,只使用其中一个。此外,还有一些情况是两者都不适用,因此上面两种方法都无法使用。

给定两个对象 A 和 B(具有类属性 x 和 y),相等运算符可以写成

  if (A.x != B.x) return false;
  if (A.y != B.y) return false;
  return true;

而不相等运算符可以写成

  if (A.x != B.x) return true;
  if (A.y != B.y) return true;
  return false;

所以,是的,相等运算符当然可以写成...!(a!=b)...,但它并没有更快。事实上,这里还增加了方法调用和否定操作的开销。

因此问题变成了,少量执行开销是否值得更小的代码和更好的可维护性?对此没有简单的答案,它完全取决于程序员如何使用它们。如果你的类包含,比如,一个包含 10 亿个元素的数组,那么开销是微不足道的。

华夏公益教科书