C++中如何不使用new、不单独声明元素创建std::initializer_list<base*>并避免切片
嘿,这两个问题本质上是同一个场景的不同问法,核心都是要避免对象切片、不用动态分配(new)、不给每个派生类对象单独起名,同时保证被指向的对象拥有静态存储期。咱们一步步来看解决方案:
核心思路
要满足所有要求,关键是利用C++的static关键字来创建匿名的静态存储期派生类对象,然后直接取它们的地址放入std::initializer_list<base*>中——这样既不用单独声明变量,也不会让对象在表达式结束后销毁,还能通过指针避免切片。
具体实现示例
先定义基础的基类和派生类:
#include <initializer_list> #include <iostream> #include <string> class base { public: virtual void say_hello() const = 0; virtual ~base() = default; }; class derived1 : public base { public: void say_hello() const override { std::cout << "Hello from derived1!\n"; } }; class derived2 : public base { private: std::string msg; public: derived2(const std::string& m) : msg(m) {} void say_hello() const override { std::cout << "Hello from derived2: " << msg << "\n"; } };
1. 函数内创建std::initializer_list<base*>
如果只是在某个函数里使用这个列表,直接在初始化器中用static构造派生类对象并取地址就行:
void greet_all() { std::initializer_list<base*> objects = { &(static derived1{}), // 无参构造的静态匿名对象 &(static derived2{"Custom message"}) // 带参构造的静态匿名对象 }; for (const auto* obj : objects) { obj->say_hello(); // 虚函数调用,不会切片,正确执行派生类逻辑 } }
这里的static非常关键:它让这个临时构造的对象变成静态局部变量,存储期覆盖整个程序运行期间,第一次初始化后就一直存在,不会像普通临时对象那样在表达式结束后销毁,所以我们拿到的地址永远有效。
2. 文件作用域(全局)的指针列表
如果需要在全局/文件作用域使用这个列表,同样可以用相同的方式:
// 文件作用域,全局可用 const std::initializer_list<base*> global_objects = { &(static derived1{}), &(static derived2{"Global instance"}) };
这些被指向的派生类对象都是静态存储期,程序启动时就会完成初始化(或第一次访问时,取决于编译器的静态初始化策略),完全不需要运行时动态分配内存,也不用单独给每个对象起名(比如d1、d2)。
为什么这能解决你的问题?
- 避免对象切片:用基类指针指向派生类对象,虚函数机制会自动派发到正确的派生类实现,不会像直接存储对象那样丢失派生类信息。
- 无动态分配:全程不用
new,所有对象都是静态存储,内存在程序启动时就已分配好。 - 无需单独声明变量:所有派生类对象都是匿名的,直接在列表中构造并取地址,不用额外定义d1、d2这类变量。
内容的提问来源于stack exchange,提问作者Vegard
相关产品推荐
相关产品推荐

