标准库的容器类拥有它们的元素。也就是说,如果你有:
class Base {};
class Child : public Base {};
std::list < Base > myList;
std::list < Base > *pMyList;
那么myList
的元素只能作为Base
类型的对象(引用)进行访问。您可以存储Base
类型的元素(例如push_back
,emplace_back
),并获取引用/副本(例如front
或通过迭代器),例如,请参见cppreference/list。让我们来看一下push_back
:
void push_back(const value_type&);
其中value_type
是您传递给std::list
的第一个模板参数。在您的情况下,它是PartitionT < CompareJobReady, CompareJobRunning >
,或者在上面的例子中,它是Base
。
push_back
实际上会复制您传递的参数,并将该副本作为新元素。为什么呢?因为新元素可以由列表拥有。当列表本身被销毁时,列表可以销毁此元素,并将其传递给另一个列表(如果您移动/交换两个列表)。如果未复制该元素并且从外部销毁了该元素,则列表将包含已销毁的元素-这将破坏列表所提供的保证(而且这样做不好)。
另一个例子(简化,不太准确):list
通过分配器为其元素分配内存。这里的默认分配器是std::allocator<Base>
。它只为存储类型为Base
的对象所需的内存分配了足够的内存。
当你执行像
pMyList = new std::list < Child >;
这样的操作时,右侧会产生一个“指向
std::list<Child>
的指针”,而左侧的类型是“指向
std::list<Base>
的指针”。这两种类型是不相关的,因为
std::list<Base>
没有继承或定义到std::list<Child>
的转换。这有一些原因是相当好的,其中之一是通用编程需要知道它所处理的类型。多态性是一种抽象,使您无需 - 也不能 - 在编译时知道您正在处理的类型。
C++中的多态性通过指针或引用工作。因此,如果您想将一些
Child
对象排列在与此类型无关的列表中(例如,它只知道
Base
类型),则必须将它们存储为指针:
std::list < std::shared_ptr<Base> > myList2;
myList2.push_back( new Child ); // better not use, there's a caveat (1)
// approach w/o this caveat
std::shared_ptr<Base> pNewChild( new Child ); // or make_shared
myList2.push_back( pNewChild );
请注意,我在这里使用了一个
shared_ptr
,如果适合您的目的,您也可以使用
unique_ptr
,但是您不应该使用原始指针:因为
myList2
拥有其
shared_ptr
元素,并且
shared_ptr
持有共享所有权的对象(类型为Base),
myList2
间接拥有您在列表中存储指针的
Child
对象。由于原始指针不能表达所有权,例如,不清楚谁负责销毁它们。了解更多关于
"零规则"的信息。
(1)有一个警告,请参阅
boost,尽管它不影响此示例。
如果您真的想通过选择某个比较器类来进行通用编程,那么您"必须坚持在编译时": 您的列表类型(*p
的类型)不应该固定为list<Base>
,而应该是通用的(使用模板),并且您使用的所有算法也都必须是通用的。您不能(简单地*)混合通用编程和运行时类型选择,因为通用编程的全部内容都是关于在编译时创建代码。
*有一种hack方法可以实现,滥用RTTI,因此非常缓慢。
std::list
吗? - dyp