【问题标题】:Optional reference member - is it possible?可选参考成员 - 有可能吗?
【发布时间】:2009-06-09 13:10:12
【问题描述】:

我有以下课程

class CItem
{
    public:
        CItem(CRegistry &Registry) _Registry(Registry) {Registry.Register();}
        ~CItem() {_Registry.Unregister()};


    private:
        CRegistry &_Registry;
}

一段时间后,事实证明并非所有 CItem 对象都需要注册,所以我需要一个 CItem 版本,它不需要在构造函数中注册(当然还有注册代码)。我该如何实施? 我在这里可以看到的唯一解决方案是获取注册表并将其保留为指针。有没有更优雅的解决方案,比如使用模板等(我不喜欢从引用切换到指针)?

【问题讨论】:

  • 顺便说一句,标准保留带前导下划线的名称。
  • 特别是以下划线和大写字母开头的名字!
  • 详细说明:下划线后跟大写字母的名称在任何地方都保留。其他以下划线开头的名称仅保留在全局名称空间中,因此如果您坚持可以用于成员变量(并且不介意它们隐藏某些特定于实现的符号的可能性很小)。 17.4.3.1.2.

标签: c++ class constructor-overloading


【解决方案1】:

如果你想保留一个类,只需将属性更改为原始指针并允许它为空。正如尼尔指出的那样,针对不完全正当的原始指针存在广泛的不正当圣战。使用原始指针并清楚地记录(注释)该对象不拥有指向内存的所有权,这样以后就不会有人想在您的析构函数中添加delete

所有其他解决方案都比在内部使用指针更糟糕。这是一个实现细节。还要考虑是否有意义。您的代码将不再能够假定指针是有效的,这将使您的类内部的逻辑复杂化。

class CItem
{
public:
   CItem(CRegistry &Registry) : _Registry(&Registry) {Registry->Register();}
   CItem() : _Registry(0) {}
   ~CItem() { if ( _Registry ) _Registry->Unregister(); }

private:
   CRegistry *_Registry; // Pointer is not owned. Do not delete!
};

最后一点:不要在属性前加上一个下划线,因为它们是为 C++ 实现(编译器和标准库)标准保留的

【讨论】:

  • “不要在属性前加上一个下划线”,如果不明显,也不要使用多个下划线。任何地方都包含双下划线的名称保留用于所有用途。
  • 为了更明确地了解可选语义,您可以使用 boost::optional 而不是原始指针。但除此之外,我同意,对于“内置”可选支持,指针可以很好地解决问题。
【解决方案2】:

唯一的另一种选择是创建一个 CItem 基类,然后从中派生 ItemWithRef(它有一个引用)和 ItemWithoutRef。但是 ir 使用指针更容易、更清晰。

从关于引用作为成员的问题数量来看,似乎有人在某个地方传播了这样一种观点,即引用是好的,指针是坏的。情况并非如此,尤其是在涉及数据成员时。

只是为了澄清下划线的事情:

  • 以下划线和小写字母开头的名称在出现在命名空间(即类之外)范围内时保留给 C++ 编译/库编写者

  • 以下划线和大写字母开头或包含两个连续下划线的名称无条件保留给编译器/库编写者 - 您不得在自己的代码中使用它们

【讨论】:

    【解决方案3】:

    通过引用成员,您清楚地表明每个CItem 都需要一个注册表(因为引用必须绑定到有效对象,并且每个CItem 都有一个引用成员)。实现可选CRegistry 的直接方法是使用boost::optional(这比NULL 指针习语更安全、更清晰)。或者,Null Object Pattern 让您拥有一个 CNullRegistry 类,该类实现注册、注销和其他功能,作为无操作。然后让构造函数参数默认为一个 CNullRegistry 对象。

    但是,您可能想考虑一种更高级别的方法,该方法清楚地将已注册的CItems 与未注册的 CItems 区分开来。正如其他答案所建议的那样,继承和模板专业化都为此提供了机制。优点是您的设计现在可以依赖于“我已注册”不变量。

    【讨论】:

      【解决方案4】:
      1. 使用继承:创建一个基类CItem 并从中派生CRegisteredItem
      2. 使用指针(和重载的构造函数)

      【讨论】:

        【解决方案5】:

        指针的一个明显替代方案是private: static CRegistry selfRegistered;。 然后你可以写CItem::CItem() : Registry(selfRegistered) { }

        【讨论】:

          【解决方案6】:

          您能否创建另一个不以 CRegistry 作为参数的构造函数:

          CItem();
          

          将 _Registry 初始化为静态“僵尸”值?可能不如使用指针或子类优雅?

          【讨论】:

          • 为此,您必须能够创建一个“空”注册表对象,这可能需要更改注册表类。这是一个非常糟糕的解决方案,恕我直言。
          • 是的。不是我个人会实施的。我会使用私人指针。
          【解决方案7】:

          使 CRegistry 成为一个抽象类:

          C类注册表 { 上市: 虚拟无效寄存器(常量 CItem& 项目)= 0; 虚拟 void Unregister(const CItem& Item) = 0; };

          然后导出两个实现

          类 CNoopRegistry : 公共 CRegistry { 上市: virtual void Register(const CItem& Item) {} virtual void Unregister(const CItem& Item) {} }; CWorkingRegistry 类:公共 CRegistry { 上市: virtual void Register(const CItem& Item) { /* 做一些有用的事情 */ } virtual void Unregister(const CItem& Item) { /* 做一些有用的事情 */ } };

          并将您需要的任何实例传递给 CItem 的构造函数。

          【讨论】:

            【解决方案8】:

            您可以使CRegistry 成为一个单例(或只是一个独立的类)并在CItem 构造函数中决定是否要注册该特定实例。这将项目与注册表分离,恕我直言,将来更改内容变得更容易。

            【讨论】:

              【解决方案9】:

              您可能可以像这样使用模板专业化:

              class CRegistry
              {
              public:
                  void Register(){}
                  void Unregister(){}
              };
              
              template <class RegistryType>
              class CItem
              {
                      public:
                              CItem(){}
                              ~CItem() {}
              
              
              };
              
              
              template<>
              class CItem<CRegistry>
              {
                  public:
                   CItem(CRegistry &Registry_in):  Registry(Registry_in) {Registry.Register();}
                  ~CItem() {Registry.Unregister();}
              
              
                      private:
                              CRegistry& Registry;
              };
              
              int main()
              {
              
                  CRegistry c1;
                  CItem<CRegistry> it(c1);
                  CItem<int> it2;
                      return 0;
               }
              

              【讨论】:

                猜你喜欢
                • 2013-06-05
                • 2011-06-05
                • 2013-04-25
                • 1970-01-01
                • 2014-12-19
                • 2018-08-04
                • 2017-10-16
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多