具有多态数据的容器的常见实现是什么?
What are common implementations for containers with polymorphic data?
假设我创建了一个抽象的 class 实体,然后是它的多个子class,每个都有它的参数和功能,但由于实体共享一个公共接口。这是迄今为止面向对象编程中最常见的示例。
当我创建实体值向量并告诉它为 N 个元素保留(确保容量而不调整大小)时,内部发生了什么。如果大小可以是任何值,它如何知道大小?公共部分是否存储在向量中,然后是指向 subclass 特定部分的指针?如果是这样,该指针最终会在堆中声明在哪里?这对缓存性能非常不利。
最后,这个跟POD和非POD类型有关系吗?
如果向量包含 Entity
个值,则它包含 Entity
类型的值,而不是任何子类。由于Entity
是抽象的,所以不能实例化这样的向量。
如果您想要多态性,则必须存储指向存储在别处的对象的指针,或者如果您希望向量像管理其元素一样管理其生命周期,则必须存储智能指针。
一旦抽象 类 开始发挥作用,POD 就会立即退出 window。您正在通过指针处理所有事情。您不关心对象的大小,因为它不是对象的向量,它是指向对象的指针的向量。
如果向量 'owns' 内存(听起来像你的情况),那么代码将如下所示...
// Declaration
std::vector <Entity *> ent_vector;
// Inserting into the vector
ent_vector.push_back(new EntityImpl());
// In a destructor somewhere...
for (std::vector<Entity *>::iterator i = ent_vector.begin(); i != ent_vector.end(); ++i)
{
delete *i;
}
在 C++ 中没有明确支持这类事情。您必须(如 Mike 的回答中所述)通过指针访问多态对象以调用多态行为。这意味着您必须在容器中存储指针。如果你像往常一样希望你的容器也管理对象的生命周期,你应该使用 unique_ptr
s。
您可以按照
的方式创建您自己的小型多态容器
template<typename AbstractType>
class poly_vector
: std::vector<std::unique_ptr<AbstractType>>
{
typedef std::vector<std::unique_ptr<AbstractType>> base;
public:
using base::begin; // public access
using base::end; // to some vector
using base::size; // functionality
poly_vector() = default; // default ctor
poly_vector(poly_vector&&) = default; // move, but
poly_vector(poly_vector const&) = delete; // no copy
// add another object at the end
template<typename DerivedType, typename...Args>
typename std::enable_if<std::is_base_of<AbstractType,DerivedType>::value>::type
emplace_back(Args&&..args)
{
base::emplace_back(new DerivedType(std::forward<Args>(args)...);
}
};
然而,begin()
指向的对象又是指针(实际上是 unique_ptr
),因此访问多态对象的语义有点不寻常。
此外,emplace_back
的语义是不寻常的:您必须像 myvec.emplace_back<Dervied>(arg1,arg2);
中那样显式提供 DerivedType
作为模板参数
当然,您可以详细说明上述想法,包括避免双重解引用的迭代器类型,以及完整的分配器支持等...
假设我创建了一个抽象的 class 实体,然后是它的多个子class,每个都有它的参数和功能,但由于实体共享一个公共接口。这是迄今为止面向对象编程中最常见的示例。
当我创建实体值向量并告诉它为 N 个元素保留(确保容量而不调整大小)时,内部发生了什么。如果大小可以是任何值,它如何知道大小?公共部分是否存储在向量中,然后是指向 subclass 特定部分的指针?如果是这样,该指针最终会在堆中声明在哪里?这对缓存性能非常不利。
最后,这个跟POD和非POD类型有关系吗?
如果向量包含 Entity
个值,则它包含 Entity
类型的值,而不是任何子类。由于Entity
是抽象的,所以不能实例化这样的向量。
如果您想要多态性,则必须存储指向存储在别处的对象的指针,或者如果您希望向量像管理其元素一样管理其生命周期,则必须存储智能指针。
一旦抽象 类 开始发挥作用,POD 就会立即退出 window。您正在通过指针处理所有事情。您不关心对象的大小,因为它不是对象的向量,它是指向对象的指针的向量。
如果向量 'owns' 内存(听起来像你的情况),那么代码将如下所示...
// Declaration
std::vector <Entity *> ent_vector;
// Inserting into the vector
ent_vector.push_back(new EntityImpl());
// In a destructor somewhere...
for (std::vector<Entity *>::iterator i = ent_vector.begin(); i != ent_vector.end(); ++i)
{
delete *i;
}
在 C++ 中没有明确支持这类事情。您必须(如 Mike 的回答中所述)通过指针访问多态对象以调用多态行为。这意味着您必须在容器中存储指针。如果你像往常一样希望你的容器也管理对象的生命周期,你应该使用 unique_ptr
s。
您可以按照
的方式创建您自己的小型多态容器template<typename AbstractType>
class poly_vector
: std::vector<std::unique_ptr<AbstractType>>
{
typedef std::vector<std::unique_ptr<AbstractType>> base;
public:
using base::begin; // public access
using base::end; // to some vector
using base::size; // functionality
poly_vector() = default; // default ctor
poly_vector(poly_vector&&) = default; // move, but
poly_vector(poly_vector const&) = delete; // no copy
// add another object at the end
template<typename DerivedType, typename...Args>
typename std::enable_if<std::is_base_of<AbstractType,DerivedType>::value>::type
emplace_back(Args&&..args)
{
base::emplace_back(new DerivedType(std::forward<Args>(args)...);
}
};
然而,begin()
指向的对象又是指针(实际上是 unique_ptr
),因此访问多态对象的语义有点不寻常。
此外,emplace_back
的语义是不寻常的:您必须像 myvec.emplace_back<Dervied>(arg1,arg2);
DerivedType
作为模板参数
当然,您可以详细说明上述想法,包括避免双重解引用的迭代器类型,以及完整的分配器支持等...