【问题标题】:What are the necessary special member functions that should to be declared? [duplicate]应该声明哪些必要的特殊成员函数? [复制]
【发布时间】:2017-08-26 03:47:49
【问题描述】:

我目前正在学习 C++。我了解了一些由编译器生成的特殊成员函数。

• 默认构造函数。 • 复制构造函数 • 移动构造函数 • 复制赋值运算符 • 移动赋值运算符 • 析构函数

我知道每个成员函数的功能。但我想知道哪些成员函数是必要的,应该声明(而不是由编译器生成),作为良好编码实践的一部分,同时牢记内存和效率。

【问题讨论】:

  • 理想情况下,除了常规构造函数(如果需要)之外,您不需要定义任何内容。很好的参考:en.cppreference.com/w/cpp/language/rule_of_three
  • 这完全取决于你的类(类型)做什么以及它包含什么。没有通用的答案。
  • 90% 的时间你不会定义任何这些。剩下的 10%,定义那些有意义的。如果您希望某些代码在销毁时运行,请定义析构函数。当其中的每一个都被隐式定义或没有被隐式定义时,您应该知道一组条件。

标签: c++ member-functions


【解决方案1】:

默认构造函数是其中的一个特例。如果某些成员变量需要一个不是这些成员默认值的初始状态,请编写一个默认构造函数。

只有在您知道自己需要时才应该覆盖其余的内容。

这些是您可能想要编写列出的函数的自定义版本的一些合理情况:

  1. 您的类的成员变量是指向它已分配的动态内存的指针。
  2. 您的类将数据结构作为成员,用户不应将其完整版本传递给构造函数。
  3. 您的类中有一些您不想使用默认复制模型的成员(例如,您想要一个深复制,但复制构造函数或赋值会进行浅复制)。
  4. 您的类有一个静态数据成员,在创建它的新实例时应该更改它。

每个用例的示例包括:

  1. STL 向量,动态分配其底层数组并在其析构函数中释放。
  2. STL 列表,将添加到其中的数据复制到列表节点上,其内部不会向最终用户公开。
  3. 任何 STL 容器类都是一个很好的例子。
  4. 需要访问昂贵资源的事物的数量。类似于std::shared_ptr 的设计目标。

如果一个类满足太多这些要求,您可能应该考虑更改设计以使用更多容器和标准库提供的实用程序。特别是如果一个类需要第四个属性和其他三个中的任何一个。第 1-3 点和第 4 点相关的职责应该由不同的类来处理。 重要的是要注意,拥有一个大型、创建成本高的资源和拥有许多资源是不同类型的责任。

参考资料:

【讨论】:

    【解决方案2】:

    但我想这些成员函数中的哪一个是必要的并且应该被声明(而不是由编译器生成)

    如果隐式生成的特殊成员函数按照您的意图执行,则不应显式声明它们。

    如果某些其他生成的特殊成员函数没有按照您的意愿执行,那么您必须显式声明它们,并提供您需要的实现。

    如果您对 [析构函数、复制/移动构造函数/赋值] 中的任何一个都有明确的重要定义,那么根据经验,您可能需要为它们中的每一个定义一个定义。

    【讨论】:

      【解决方案3】:

      默认构造函数有点奇怪。是否有默认构造不一定与是否编写其他构造有任何关系(尽管在实践中编写没有默认构造的移动构造可能很困难)。

      在大多数情况下,您应该尝试遵循零规则(http://en.cppreference.com/w/cpp/language/rule_of_threehttp://www.nirfriedman.com/2015/06/27/cpp-rule-of-zero/),这意味着您不会为您的类编写任何这些成员函数。相反,请选择正确表达您对各种资源所需的所有权语义的成员。成员std::vector 正确处理复制和移动您想要的方式,因此具有这样一个成员的类不一定需要任何特殊成员。但是,如果您尝试将使用 new[] 创建的原始指针用作数组,那么您将需要处理许多此类问题,并且需要编写特殊的成员函数。

      如果你需要写一个,你应该明确地考虑所有这些,但这并不意味着你必须写所有这些。有时您可能想要=default=delete 其中一些。一个简单的例子:

      template <class T>
      copy_ptr {
        copy_ptr() = default;
        copy_ptr(copy_ptr&&) = default;
        copy_ptr& operator=(copy_ptr&&) = default;
      
        copy_ptr(const copy_ptr& other)
          : m_resource(make_unique<T>(*other.m_resource)
       {}
      
       copy_ptr& operator=(const copy_ptr& other) {
         m_resource = make_unique<T>(*other.m_resource);
       }
       // define operator*, etc
      
        std::unique_ptr<T> m_resource;
      };
      

      这是一个指针,它不像unique_ptr 那样不可复制,而是对其持有的对象进行深度复制。但是,我们能够使用来自unique_ptr 的默认构造函数、移动运算符和析构函数,因此我们将它们默认为默认值。我们只重写了复制操作,做了最少的工作。

      基本上,C++ 中特殊运算符的想法是将它们“向下”推入更小的对象/类中。它们通常单独负责管理该资源。协调业务逻辑的较大类应该非常努力地将资源管理问题交给成员。

      【讨论】:

        猜你喜欢
        • 2012-07-25
        • 2012-01-27
        • 2015-06-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-24
        • 1970-01-01
        相关资源
        最近更新 更多