【问题标题】:C++ Composition with abstract class具有抽象类的 C++ 组合
【发布时间】:2015-08-28 17:17:39
【问题描述】:

假设我有一个创建和复制成本很高的抽象类:

class AbstractBase {
public:
    AbstractBase() {
        for (int i = 0; i < 50000000; ++i) {
            values.push_back(i);
        }
    }

    virtual void doThing() = 0;

private:
    vector<int> values;
};

它有两个子类FirstDerived

class FirstDerived : public AbstractBase {
public:
    void doThing() {
        std::cout << "I did the thing in FirstDerived!\n";
    }

};

SecondDerived:

class SecondDerived : public AbstractBase {
public:
    void doThing() {
        std::cout << "I did the thing in SecondDerived!\n";
    }
};

此外,我想使用组合(而不是聚合)创建一个利用 FirstDerivedSecondDerived 的类。这意味着我希望ComposedOfAbstractBase 拥有传入的任何临时对象。如果我不使用此类中的抽象类,则如下所示:(在 C++11 中)

class ComposedOfWhicheverDerived {
public:
    ComposedOfWhicheverDerived(AbstractBase abstract_base) : abstract_base(std::move(abstract_base)) {;}
private:
    AbstractBase abstract_base;
};

但是,这不适用于抽象类,因为我永远无法创建 AbstractBase 的实例,即使我小心不要传入临时的 AbstractBase,如下所示:

ComposedOfWhicheverDerived a(FirstDerived());

对于编译器来说,这和以下一样糟糕:

ComposedOfWhicheverDerived b(AbstractBase());

因为我在类声明中还有一个AbstractBase 的实例。

我想出的下一个解决方案是:

class ComposedOfAbstractBase {
public:
    ComposedOfAbstractBase(AbstractBase&& abstract_base) : some_derived_instance(abstract_base) {;}

private:
    AbstractBase& some_derived_instance;
};

这很好用(即使我不完全理解)!这两个实例均有效且按预期工作:

ComposedOfAbstractBase a(FirstDerived());
ComposedOfAbstractBase b(SecondDerived());

它不会创建任何 AbstractBase 临时传入的副本,并且允许存储对 AbstractBase 的引用。尽管对右值引用的引用充其量似乎不清楚:它没有传达ComposedOfAbstractBase 拥有 传入的任何临时值。除此之外,事实证明这个解决方案似乎是子最佳。为了展示这一点,我创建了这个类:

class ComposedOfFirstDerived {
public:
    ComposedOfFirstDerived(FirstDerived first_derived) : first_derived(std::move(first_derived)) {;}

private:
    FirstDerived first_derived;
};

它只能接受FirstDerived,因此我们可以应用std::move 来卸载临时的所有权。我可以像这样创建一个实例:

ComposedOfFirstDerived c(FirstDerived()); 

有趣的是,创建此类的速度始终比 ComposedOfAbstractClass 快 10%。

有人知道这里发生了什么吗?为什么ComposedOfFirstDerived 的创建速度比ComposedOfAbstractBase 快得多?有没有更好的方法来使用抽象类进行组合,或者我是否坚持使用次优解决方案?

对不起,如果这是一个满嘴的问题。我感谢任何花时间阅读并给出真实答案的人,因为我非常难过!

【问题讨论】:

  • 次优解决方案是完全错误的。它不拥有临时人员。
  • 那么,什么拥有临时工?它是否相当于创建一个新指针然后从不删除它?
  • 不,相当于持有一个指向无效内存区域的指针。

标签: c++ oop c++11 optimization composition


【解决方案1】:

ComposedOfAbstractBase 不是解决方案。你拿着一个悬空的参考。

因为AbstractBase,顾名思义,是抽象的——你不能按值持有一个。您只能通过引用或指针保存一个。由于引用不能拥有该对象,因此您可以使用指针。而拥有指针的现代方式是使用unique_ptr

class ComposedOfAbstractBasePtr {
public:
    ComposedOfAbstractBasePtr(std::unique_ptr<AbstractBase> p)
    : some_derived_instance(std::move(p))
    { }

private:
    std::unique_ptr<AbstractBase> some_derived_instance;
};

请注意,您的 AbstractBase 没有虚拟析构函数。你应该解决这个问题。

【讨论】:

  • 那我该如何初始化呢?我不能再做ComposedOfAbstractBasePtr(FirstDerived())。我是否必须为对象创建 unique_ptr 然后将其传入?由于粗心大意,是什么阻止我稍后删除该 ptr?
  • @donutmonger 你会做make_unique&lt;FirstDerived&gt;()。或者你可以早点做一个unique_ptr,然后把move放进去。只要你专门处理unique_ptrs,就不能随便删除。您必须明确输入 delete p.get(); 这应该是一个危险信号...
  • @donutmonger: ComposedOfAbstractBasePtr(std::make_unique&lt;FirstDerived&gt;()) 你不能将delete 应用到unique_ptr,即使你可以,你还是传递了一个未命名的临时文件。这不会因为粗心而出错。
  • 非常感谢,现在更有意义了!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-10
  • 1970-01-01
  • 1970-01-01
  • 2016-05-16
  • 1970-01-01
  • 1970-01-01
  • 2014-08-16
相关资源
最近更新 更多