c++ 容器中管理存放继承类对象的智能指针

本文介绍如何使用C++智能指针管理多种继承类对象存放在容器中的多态需求,通过示例代码展示如何利用unique_ptr和shared_ptr实现对象的自动内存管理和多态调用。

需求:

多种继承类/子类 对象共同存放于容器中, 要求能push进不同对象,pop出来后能实现多态。

实现分析:

这种情况就得容器中存放基类指针,但是存放指针就意味着得自己管理内存,主动释放。 有没有方法让c++自己去管理呢,答案是用智能指针。

示例代码: 容器中存放的是unique_ptr, pop出来后可以转成shared_ptr给外界去调用。超级方便

#include <stdio.h>
#include <stdlib.h>
#include <list>
#include <memory>
#include <iostream>

using namespace std;

class Base
{
public:
    Base();
    ~Base();

    virtual void Func();
};

Base::Base()
{
    printf( "%s\n", __func__ );
}

Base::~Base()
{
    printf( "%s\n", __func__ );
}

void Base::Func()
{
    printf( "base::%s\n", __func__ );
}

class Drived : public Base
{
public:
    Drived();
    ~Drived();

    virtual void Func();
};

Drived::Drived()
{
    printf( "%s\n", __func__ );
}

Drived::~Drived()
{
    printf( "%s\n", __func__ );
}

void Drived::Func()
{
    printf( "drived:%s\n", __func__ );
}

template<typename T, typename... Ts>
std::unique_ptr<T> make_unique(Ts&&... params)
{
    return std::unique_ptr<T>(new T(std::forward<Ts>(params)...));
}

std::list<std::unique_ptr<Base> > myList;

template<typename T>
void Push(const T &base)
{
    myList.push_back(make_unique<T> (std::move(base)));
}

void Pop()
{
    //std:unique_ptr<Base> ptr = std::move(myList.front());
    std::shared_ptr<Base> ptr = std::move(myList.front());
    ptr->Func();
    myList.pop_front();
}


    int
main( int argc, char **argv )
{
    Drived drived;
    Push(drived);
    Pop();

    return 0;
}

编译请加--std=c++11

输出:

./a.out                  
Base
Drived
drived:Func
~Base
~Drived
~Base

作者:帅得不敢出门    c++哈哈堂 31843264

<think>好的,我现在需要帮助用户解决如何在C++容器中存储不同结构的数据的问题。用户之前可能已经了解过一些相关的概念,比如联合体(union)或者模板的使用,但需要更具体的指导。首先,我要回忆C++中处理不同类型数据的常用方法。 首先想到的是使用联合体(union),联合体允许在同一内存位置存储不同的数据类型。例如引用[1]中提到,联合体可以声明不同类型的成员,但同一时间只能有一个成员有效。不过联合体在C++中有局限性,特别是不能直接存储非POD类型,比如带有构造函数或析构函数的类。这可能限制其使用场景。 接下来考虑继承和多态。通过基类指针存储派生类对象,这样容器可以保存不同类型的对象。比如定义一个基类Data,然后让不同的结构体继承它,容器存储基类指针。这种方法需要动态内存管理,可能需要智能指针来避免内存泄漏,比如使用std::unique_ptr或std::shared_ptr。 第三种方法是使用C++17引入的std::variant,它允许在类型安全的联合体中存储指定类型之一。这比传统的联合体更灵活,且支持非POD类型。同时,结合std::visit可以方便地访问存储的值。 另外,std::any也是一个选项,可以存储任意类型的实例,但使用时需要类型转换,可能不如variant安全。 还需要考虑用户的具体需求。如果用户的数据结构有共同的基类继承方法可能更合适。如果类型之间没有继承关系,variant或any可能更好。另外,性能也是一个因素,variant和继承在运行时可能会有不同的开销。 用户的问题中提到“不同结构的数据”,可能指不同的结构体,没有共同的基类。这时候使用std::variant或std::any更合适。例如,如果用户有多个结构体,如StructA、StructB等,可以定义一个variant类型,容器存储这些variant对象。 另外,模板结构体如引用[3]中的TreeNode,可能涉及链表结构,但用户的问题更关注容器存储不同类型的数据,所以可能不需要这种结构。 总结可能的解决方案: 1. 使用继承基类指针容器。 2. 使用std::variant和std::visit。 3. 使用std::any进行类型擦除。 4. 传统联合体(受限较多)。 需要比较这些方法的优缺点,并给出示例代码。例如,variant的例子更现代且类型安全,推荐使用。同时,可能需要提到C++17的支持情况,如果用户的环境支持的话。 还需要注意容器的选择,比如vector、list等,存储variant或基类指针的方式。 最后,生成相关问题,可能涉及性能比较、其他方法如工厂模式,或者特定场景的应用。</think>在C++中存储不同结构数据,可通过以下方法实现: ### 方法一:使用继承和多态 1. 定义基类 ```cpp class BaseData { public: virtual ~BaseData() = default; virtual void process() = 0; }; ``` 2. 创建派生结构体 ```cpp struct DataA : BaseData { int value; void process() override { /* 处理逻辑 */ } }; struct DataB : BaseData { string info; void process() override { /* 处理逻辑 */ } }; ``` 3. 使用智能指针容器 ```cpp vector<unique_ptr<BaseData>> container; container.push_back(make_unique<DataA>()); container.push_back(make_unique<DataB>()); ``` ### 方法二:使用std::variant(C++17) 1. 定义数据结构 ```cpp struct DataX { int id; double value; }; struct DataY { string name; vector<float> params; }; using DataVariant = variant<DataX, DataY>; ``` 2. 创建访问器 ```cpp auto visitor = [](auto&& arg) { using T = decay_t<decltype(arg)>; if constexpr (is_same_v<T, DataX>) { cout << "处理DataX: " << arg.value; } else if constexpr (is_same_v<T, DataY>) { cout << "处理DataY: " << arg.name; } }; ``` 3. 存储和访问数据 ```cpp vector<DataVariant> container; container.emplace_back(DataX{1, 3.14}); container.emplace_back(DataY{"test", {1.1, 2.2}}); for (auto& item : container) { visit(visitor, item); } ``` ### 方法三:使用std::any(类型擦除) ```cpp vector<any> container; container.push_back(DataA{5}); container.push_back(DataB{"hello"}); try { auto& data = any_cast<DataA&>(container[0]); } catch (const bad_any_cast&) { // 类型转换失败处理 } ``` ### 对比分析 | 方法 | 类型安全 | 内存管理 | 扩展性 | 性能开销 | |-----------|-------|-------|-----|-------| | 继承多态 | 高 | 需手动 | 好 | 虚函数调用 | | std::variant| 最高 | 自动 | 中等 | 编译时解析 | | std::any | 低 | 自动 | 好 | 运行时检查 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帅得不敢出门

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值