可选参考成员 - 是否可能?

时间:2009-06-09 13:10:12

标签: c++ class constructor-overloading

我有以下课程

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


    private:
        CRegistry &_Registry;
}

一段时间后发现并非所有CItem对象都需要注册,因此我需要一个CItem版本,它不需要构造函数中的Registry(当然还有注册代码)。我该如何实现呢? 我在这里看到的唯一解决方案是获取并保持Registry作为指针。是否有更优雅的解决方案,如使用模板等(我不喜欢从引用切换到指针)?

9 个答案:

答案 0 :(得分:8)

如果要保留单个类,只需将属性更改为原始指针并允许它为null。正如尼尔所指出的那样,对于原始指针存在广泛的不合理的圣战,这种说法并不完全合理。使用原始指针并清楚地记录(注释)该对象不拥有指向内存的所有权,这样就不会有人想在以后的析构函数中添加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 ++实现标准(编译器和标准库)保留的

答案 1 :(得分:5)

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

从关于作为成员的引用的问题的数量,似乎在某个地方,有人正在传播引用是好的指针和指针是坏的。事实并非如此,特别是涉及数据成员时。

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

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

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

答案 2 :(得分:2)

通过拥有引用成员,您明确表示每个CItem都需要注册表 (因为引用必须绑定到有效对象,每个{{1}有参考成员)。实现可选CItem的直接方法是使用boost::optional(这比NULL指针习惯用法更安全,更清晰)。或者,Null Object Pattern允许您拥有一个实现注册,注销和其他功能的CNullRegistry类,作为no-ops。然后将构造函数参数默认为CNullRegistry对象。

但是,您可能希望考虑一种更高级别的方法,该方法清楚地描述未注册的CRegistry已注册的CItem。正如其他答案所暗示的那样,继承和模板专业化都为此提供了机制。优点是您的设计现在可以依赖于“我已注册”的不变量。

答案 3 :(得分:1)

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

CItem();

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

答案 4 :(得分:1)

  1. 使用继承:创建基类CItem并从中派生CRegisteredItem
  2. 使用指针(和重载的构造函数)

答案 5 :(得分:1)

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

你可以使用这样的模板专业化:

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

答案 8 :(得分:0)

让CRegistry成为一个抽象类:

class CRegistry
{
public:
  virtual void Register(const CItem& Item) = 0;
  virtual void Unregister(const CItem& Item) = 0;
};

然后派生出两个实现

class CNoopRegistry : public CRegistry
{
public:
  virtual void Register(const CItem& Item) {}
  virtual void Unregister(const CItem& Item) {}
};

class CWorkingRegistry : public CRegistry
{
public:
  virtual void Register(const CItem& Item) { /* do something useful */ }
  virtual void Unregister(const CItem& Item) { /* do something useful */ }
};

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