存储可以使用不同派生类型初始化的基类型成员变量时,请避免使用new

Avoid new when storing base-typed member variable that could be initialized with different derived types

本文关键字:类型 new 变量 基类 可以使 派生 初始化 存储 成员      更新时间:2023-10-16

我的代码结构如下,其中多个类实现Interface。在Example类中,我将一个指向Interface的指针和new()适当地存储在构造函数中(取决于此处未显示的构造函数参数)。我正在寻找避免在这种情况下使用new()的方法,但还没有找到解决方案。这样的事情最好的做法是什么?

class Interface
{
   virtual void Foo() = 0;
};
class A : public Interface
{
   void Foo() { ... }
};
class B : public Interface
{
   void Foo() { ... }
};
class Example
{
private:
     Interface* m_bar; 
public:
     Example()
     {
         m_bar = new A(); // deleted in destructor
     }
};

通常有两种方法,每种方法都有自己的优点。

如果A是在编译时真正定义的,那么处理这一问题的典型方法就是简单地使用模板类型:

template <typename T>
class TemplateExample
{
    T m_bar;
public:
    TemplateExample() : m_bar() {};
}

这有一些缺点。TemplateExample<A>变得与TemplateExample<B>无关,当T没有遵循正确的接口时,错误消息非常迟钝,等等。好处是可以使用鸭子类型而不是接口类型,m_bar就是一个具体的实例。

另一种(有争议的更常见的)方法是进行以下

class UniquePtrExample
{
   std::unique_ptr<Interface> m_bar;
public:
   UniquePtrExample() : m_bar(new A()){}
};

这有一个好处,如果你遵循一个可复制的模式,可以在运行时进行配置:

class Interface
{
public:
   virtual void Foo() = 0;
   virtual Interface* clone() const = 0;
};
template <typename T>
class CloneHelper : public Interface
{
public:
     virtual Interface* clone() const { return new T(static_cast<const T&>(*this));}
};
class A : public CloneHelper<A>
{
   virtual void Foo() { std::cout << 'A' << std::endl; }
};
class B : public CloneHelper<B>
{
   virtual void Foo() { std::cout << 'B' << std::endl; }
};
class UniquePtrExample
{
   std::unique_ptr<Interface> m_bar;
public:
   UniquePtrExample() : m_bar(new A()){}
   UniquePtrExample(const Interface& i) : m_bar(i.clone());
};

请注意,您可以进一步扩展上述功能,使其具有克隆功能的移动变体。