5

我有以下课程

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


    private:
        CRegistry &_Registry;
}

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

4

9 回答 9

9

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

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

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++ 实现(编译器和标准库)的标准保留的

于 2009-06-09T13:34:22.390 回答
5

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

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

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

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

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

于 2009-06-09T13:16:41.147 回答
2

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

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

于 2009-06-09T13:51:35.443 回答
1

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

CItem();

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

于 2009-06-09T13:18:17.587 回答
1
  1. 使用继承:创建一个基类CItem并从中派生CRegisteredItem
  2. 使用指针(和重载的构造函数)
于 2009-06-09T13:18:36.790 回答
1

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

于 2009-06-09T13:19:33.793 回答
1

使 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 的构造函数。

于 2009-06-09T18:39:48.473 回答
0

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

于 2009-06-09T13:20:37.240 回答
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;
 }
于 2009-06-09T13:27:50.500 回答