C++链表存储多态对象:智能指针选型、插入及多态有效性问题
我正在编写一个简单的编译器,定义了如下基类:
class Instruction { public: virtual std::string get_name() const = 0; };
并创建了一个名为Compute的指令派生类:
class Compute : public Instruction { public: std::string get_name() const override { return "Compute"; } };
需求是用智能指针创建存储Instruction指针的std::list容器,需遍历容器调用虚函数get_name()(或execute、validate等),提出以下问题:
- 应使用哪种智能指针?
- 如何将该指针插入std::list?
- 解引用该指针是否仍能触发多态?
备注:网上搜索“c++ smart pointer linked list”多是关于用智能指针链接节点的内容,此处指将智能指针作为节点的数据。
备注:“什么是智能指针及何时使用”的示例未覆盖容器中存储智能指针实现多态的场景。
目标对象的生命周期与容器或程序的生命周期一致。
问题解答
1. 应使用哪种智能指针?
推荐使用std::unique_ptr<Instruction>。因为目标对象生命周期和容器/程序一致,unique_ptr是独占式智能指针,运行开销最小,完全适配当前需求。在不需要共享对象所有权的场景下,shared_ptr的引用计数带来的额外开销没有必要。如果后续有所有权转移的需求,unique_ptr也支持移动操作。
若确实需要多个组件共享对象所有权,再考虑std::shared_ptr<Instruction>,但当前场景下unique_ptr是最优选择。
2. 如何将该指针插入std::list?
首先定义对应的容器类型:
// 使用unique_ptr的容器定义 std::list<std::unique_ptr<Instruction>> instruction_list;
插入元素的常用方式有两种:
- 直接创建并插入(C++14及以上支持
std::make_unique):
instruction_list.push_back(std::make_unique<Compute>());
- 先创建智能指针再移动插入:
auto compute_inst = std::make_unique<Compute>(); instruction_list.push_back(std::move(compute_inst)); // 注意:move操作后,compute_inst不再持有对象的所有权
如果选择shared_ptr,容器定义和插入方式如下:
std::list<std::shared_ptr<Instruction>> instruction_list; instruction_list.push_back(std::make_shared<Compute>());
3. 解引用该指针是否仍能触发多态?
是的,完全可以触发多态。智能指针的operator*()和operator->()会返回底层对象的引用或指针,行为和原生指针完全一致。只要基类中的函数是虚函数,通过智能指针解引用后调用,就会根据实际存储的派生类对象类型,调用对应的派生类实现。
示例遍历调用代码:
for (const auto& ptr : instruction_list) { // 使用operator->调用虚函数 std::cout << ptr->get_name() << std::endl; // 解引用后调用虚函数,效果等价 std::cout << (*ptr).get_name() << std::endl; }
上述代码会两次输出Compute,证明多态机制正常生效。
内容的提问来源于stack exchange,提问作者Thomas Matthews

