具有抽象 class 的 C++ 组合
C++ Composition with abstract class
假设我有一个摘要 class 创建和复制的成本很高:
class AbstractBase {
public:
AbstractBase() {
for (int i = 0; i < 50000000; ++i) {
values.push_back(i);
}
}
virtual void doThing() = 0;
private:
vector<int> values;
};
它有两个子classes 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";
}
};
此外,我想制作一个 class,它利用 FirstDerived
或 SecondDerived
使用组合(而不是聚合)。这意味着我希望 ComposedOfAbstractBase
拥有 传入的临时文件。如果我在这个 class 中不使用抽象 classes 看起来像: (在 C++11 中)
class ComposedOfWhicheverDerived {
public:
ComposedOfWhicheverDerived(AbstractBase abstract_base) : abstract_base(std::move(abstract_base)) {;}
private:
AbstractBase abstract_base;
};
但是,这不适用于抽象 classes,因为我永远无法创建 AbstractBase
的实例,即使我很小心不要传入临时 AbstractBase
,像这样:
ComposedOfWhicheverDerived a(FirstDerived());
对于编译器来说,这和以下一样糟糕:
ComposedOfWhicheverDerived b(AbstractBase());
因为我在 class 声明中还有一个 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:
class ComposedOfFirstDerived {
public:
ComposedOfFirstDerived(FirstDerived first_derived) : first_derived(std::move(first_derived)) {;}
private:
FirstDerived first_derived;
};
只能接受 FirstDerived
,所以我们可以应用 std::move
来卸载临时文件的所有权。我可以这样创建一个实例:
ComposedOfFirstDerived c(FirstDerived());
有趣的是,class 的创建速度始终比 ComposedOfAbstractClass
快 10%。
有人知道这里发生了什么吗?为什么 ComposedOfFirstDerived
的创建速度比 ComposedOfAbstractBase
快得多?有没有更好的方法来使用抽象 classes 进行组合,或者我是否坚持使用次优解决方案?
抱歉,如果这个问题有点啰嗦。我感谢任何花时间阅读它并给出真实答案的人,因为我已经被难住了!
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
没有虚拟析构函数。你应该解决这个问题。
假设我有一个摘要 class 创建和复制的成本很高:
class AbstractBase {
public:
AbstractBase() {
for (int i = 0; i < 50000000; ++i) {
values.push_back(i);
}
}
virtual void doThing() = 0;
private:
vector<int> values;
};
它有两个子classes 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";
}
};
此外,我想制作一个 class,它利用 FirstDerived
或 SecondDerived
使用组合(而不是聚合)。这意味着我希望 ComposedOfAbstractBase
拥有 传入的临时文件。如果我在这个 class 中不使用抽象 classes 看起来像: (在 C++11 中)
class ComposedOfWhicheverDerived {
public:
ComposedOfWhicheverDerived(AbstractBase abstract_base) : abstract_base(std::move(abstract_base)) {;}
private:
AbstractBase abstract_base;
};
但是,这不适用于抽象 classes,因为我永远无法创建 AbstractBase
的实例,即使我很小心不要传入临时 AbstractBase
,像这样:
ComposedOfWhicheverDerived a(FirstDerived());
对于编译器来说,这和以下一样糟糕:
ComposedOfWhicheverDerived b(AbstractBase());
因为我在 class 声明中还有一个 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:
class ComposedOfFirstDerived {
public:
ComposedOfFirstDerived(FirstDerived first_derived) : first_derived(std::move(first_derived)) {;}
private:
FirstDerived first_derived;
};
只能接受 FirstDerived
,所以我们可以应用 std::move
来卸载临时文件的所有权。我可以这样创建一个实例:
ComposedOfFirstDerived c(FirstDerived());
有趣的是,class 的创建速度始终比 ComposedOfAbstractClass
快 10%。
有人知道这里发生了什么吗?为什么 ComposedOfFirstDerived
的创建速度比 ComposedOfAbstractBase
快得多?有没有更好的方法来使用抽象 classes 进行组合,或者我是否坚持使用次优解决方案?
抱歉,如果这个问题有点啰嗦。我感谢任何花时间阅读它并给出真实答案的人,因为我已经被难住了!
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
没有虚拟析构函数。你应该解决这个问题。