如何正确实现对象列表的创建(和分配)仅由C++中的抽象基知道
How to properly implement creation (and allocation) of a list of objects known only by abstract base in C++?
我应该更喜欢堆栈分配而不是堆分配。最好按值传递(特别是当你正在创建新对象时 - 但同时,如果你按基类返回,你的对象将被切片),或者至少通过引用传递指针(尽管你不能创建引用向量)。
我仔细阅读了所有这些,现在我觉得我知道的比以前知道的少了。我对如何编写一个 IMO 应该微不足道的代码一无所知,同时尊重所有这些精心编写和深思熟虑的答案中提到的最佳实践。
这就是我想要实现的内容。(我不假装它是正确的C++,但我只是想传达这个想法)。
// This thing is purely virtual!
class BaseStuff { }
// Has important behaviour and data members that shouldn't be sliced
class SomeStuff : public BaseStuff { }
// Don't slice me plz
class OtherStuff : public BaseStuff { }
BaseStuff CreateStuff()
{
// falls a set of rules to create SomeStuff or OtherStuff instance based on phase of the moon
}
std::vector<BaseStuff> CreateListOfStuff()
{
// calls CreateStuff a lot
}
public static void main()
{
List<BaseStuff> allTheStuff = CreateListOfStuff();
// to things with stuff
}
如果你想要多态性,你必须使用指针。
BaseStuff* CreateStuff()
{
...
}
List<BaseStuff*> allTheStuff = CreateListOfStuff();
实际上,如果可能,您应该更喜欢堆栈分配而不是堆分配。但这并不总是可能的。
当然,您可以考虑使用智能指针 - unique_ptr、shared_ptr等,而不是本质上不安全的原始指针,但智能指针仍然
List<shared_ptr<BaseStuff>> = CreateListOfStuff
您将存储指向基类的多态指针,该基类实际上指向派生类型(注意我在这里使用了std::unique_ptr
s):
#include <iostream>
#include <memory>
#include <vector>
class Base
{
public:
virtual void shout() = 0;
};
class Child : public Base
{
public:
void shout() { std::cout << "Child!n"; }
};
class Orphan : public Base
{
public:
void shout() { std::cout << "Orphan!n"; }
};
int main()
{
std::vector<std::unique_ptr<Base>> the_list;
the_list.reserve(3);
the_list.emplace_back(std::make_unique<Child>());
the_list.emplace_back(std::make_unique<Orphan>());
the_list.emplace_back(std::make_unique<Child>());
for(const auto& item : the_list)
{
item->shout(); // remember, items are (smart) pointers!
}
}
活生生的例子。在标准C++容器中只能存储一个类型,因此必须在此处存储指向 Base 的(智能)指针。
在这种情况下,您经常(通常?)做的一件事是在基类中定义一个成员函数(传统上称为 clone
)以创建对象的副本:
class BaseStuff {
public:
virtual BaseStuff *clone() const = 0;
};
class SomeStuff : public BaseStuff {
virtual void SomeStuff *clone() const {
return new SomeStuff(*this); // needs a correctly functioning copy ctor
}
};
class OtherStuff : public BaseStuff {
public:
virtual OtherStuff *clone() const {
return new OtherStuff(*this);
}
};
这使我们能够通过指向基的指针复制项目,而无需切片。这样,如果您希望(例如)您的ListOfStuff
存储您放入其中的项目的副本(而不仅仅是指向原始项目的指针,这些项目可能不复存在,留下悬空的指针),您可以添加如下项目:
class ListOfStuff {
class Node {
// Note: this is extremely incomplete. Almost certainly needs a dtor,
// and you want to either implement or delete the copy ctor to either
// do copying correctly, or assure it can't happen at all.
Node *next;
BaseStuff *data;
public:
Node(BaseStuff *data, Node *next = nullptr) : data(data), next(next) {}
};
Node *begin, *end;
public:
Add(BaseStuff *item) {
BaseStuff *new_item = item->clone();
end->next = new node(new_item);
end = end->next;
}
};
通过一个小的修改,我同意Armen Tsirunyan的基本观点:当你处理多态层次结构时,你通常必须通过指针或(修改)引用来做到这一点。
在许多情况下,您可以使用智能指针而不是指针 - 但并非在所有情况下,并且为特定情况选择正确的智能指针本身可能并非易事。不过,您通常确实希望在您的指针周围使用某种包装器 - 上面的代码避免使用它们,主要是因为我对您在做什么没有足够的了解来选择正确的指针。
- 仅使用绝对值对数组进行排序,并在C++中显示实际值
- 设计一个只能由特定类实例化的类(如果可能的话,通过make_unique)
- 如何仅为一个函数添加延迟
- 如果 x.h 仅由函数声明组成,为什么有必要在 x 中包含 x.h.cpp
- 是否很好地定义了强制转换为仅由 char[] 组成的结构并从该数组读取?
- 一个班级可以接受仅由外部环境使用其属性
- 是由BOOST ::返回的函子,仅使用绑定的参数绑定,等效于不采用参数的函数
- 对静态 constexpr 成员的未定义引用仅由值使用
- 仅限 Xcode 9 - 如何解决由类模板中的静态变量引起的 [-Wundefined-var-template] 警告
- QopenGLWidget paintGL仅由resizeGL触发
- 如何确保这部分代码仅由一个线程运行(在 openmp 中)
- 如何正确实现对象列表的创建(和分配)仅由C++中的抽象基知道
- 如果由另一个 OpenMP 程序调用,则外部调用的 OpenMP 程序仅使用一个线程运行
- 为什么BoostSpirit正确地将标识符解析为std::字符串,而不是解析为仅由std::string组成的自适应结构
- 找出字符串是否仅由给定字符集组成的最佳方法/算法
- 仅由表C++定义的求值函数
- 如何重载其参数仅由gcc矢量扩展vector_size属性不同的函数
- 仅由非常大的随机输入引起的基于出列前的pqueue堆中转储的分段故障核
- 初始化仅由一组对象共享的静态变量
- LNK2005仅由非模板函数调用