You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过C++函数模板为超类各子类创建实例并返回基类指针数组

实现结论

完全可以实现,基于C++11及后续标准的可变参数模板特性即可满足需求,不需要额外的运行时注册逻辑。

注:你给出的调用示例中(Base*)[n] baseList不是合法的C语法,C不支持直接返回栈上的原生C风格数组,我们可以返回std::vector<Base*>存储基类指针,需要原生数组时直接取vector的底层数据指针即可;更推荐使用智能指针版本自动管理内存,避免泄漏。

具体实现

1. 基础版本(默认构造)

如果所有派生类都是默认构造,直接展开参数包批量构造即可:

#include <vector>
#include <memory>
#include <type_traits>

// 原生指针版本
template <typename Base, typename... Deriveds>
std::vector<Base*> createChildren() {
    static_assert((std::is_base_of<Base, Deriveds>::value && ...), "所有模板参数必须是Base的派生类");
    return { static_cast<Base*>(new Deriveds())... };
}

// 更安全的智能指针版本,自动管理内存
template <typename Base, typename... Deriveds>
std::vector<std::unique_ptr<Base>> createChildrenUnique() {
    static_assert((std::is_base_of<Base, Deriveds>::value && ...), "所有模板参数必须是Base的派生类");
    std::vector<std::unique_ptr<Base>> res;
    res.reserve(sizeof...(Deriveds));
    (res.emplace_back(std::make_unique<Deriveds>()), ...);
    return res;
}

这里的static_assert会在编译期校验传入的类型确实是基类的派生类,提前拦截错误类型传入。

2. 支持统一构造参数的版本

因为所有派生类的构造函数参数列表完全一致,可以实现支持完美转发构造参数的版本,调用时把公共参数传入即可:

// 原生指针带参版本
template <typename Base, typename... Deriveds, typename... Args>
std::vector<Base*> createChildren(Args&&... args) {
    static_assert((std::is_base_of<Base, Deriveds>::value && ...), "所有模板参数必须是Base的派生类");
    return { static_cast<Base*>(new Deriveds(std::forward<Args>(args)...))... };
}

// 智能指针带参版本
template <typename Base, typename... Deriveds, typename... Args>
std::vector<std::unique_ptr<Base>> createChildrenUnique(Args&&... args) {
    static_assert((std::is_base_of<Base, Deriveds>::value && ...), "所有模板参数必须是Base的派生类");
    std::vector<std::unique_ptr<Base>> res;
    res.reserve(sizeof...(Deriveds));
    (res.emplace_back(std::make_unique<Deriveds>(std::forward<Args>(args)...)), ...);
    return res;
}
使用示例
// 定义基类,注意析构函数必须是virtual
class Base {
public:
    Base() = default;
    Base(int val) : value(val) {}
    virtual ~Base() = default; // 必须声明虚析构,否则通过基类指针释放派生类对象会触发未定义行为
    virtual void print() = 0;
protected:
    int value = 0;
};

class D1 : public Base {
public:
    using Base::Base;
    void print() override { /* D1业务逻辑 */ }
};

class D2 : public Base {
public:
    using Base::Base;
    void print() override { /* D2业务逻辑 */ }
};

class D3 : public Base {
public:
    using Base::Base;
    void print() override { /* D3业务逻辑 */ }
};

int main() {
    // 默认构造调用
    auto children1 = createChildren<Base, D1, D2, D3>();
    // 带统一参数构造调用,给所有派生类传入构造参数10
    auto children2 = createChildren<Base, D1, D2, D3>(10);
    
    // 智能指针版本使用方式一致,不需要手动delete
    auto children3 = createChildrenUnique<Base, D1, D2, D3>(10);

    // 如果需要原生C风格的Base*数组,直接取vector的底层指针即可
    Base* const* c_style_arr = children1.data();
    size_t arr_size = children1.size();

    // 原生指针版本使用完后需要手动释放内存
    for (auto ptr : children1) {
        delete ptr;
    }
    return 0;
}
注意事项
  • 基类必须声明虚析构函数,否则通过基类指针delete派生类对象属于未定义行为,会造成内存泄漏、程序崩溃等问题
  • 优先使用智能指针版本,不需要手动管理内存,没有泄漏风险
  • 上述实现中用到的折叠表达式是C17特性,如果需要兼容C11/14,可以用初始化列表展开的方式替代折叠逻辑,核心实现思路一致

内容的提问来源于stack exchange,提问作者shooting

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 02:27:16