类型双关:能否省略placement new与析构函数调用?
对象数组场景下的严格别名规则与类型双关实践问题
目前关于严格别名规则(strict aliasing rule)和类型双关(type-punning)的资料不少,但针对对象数组场景的易懂解释却很难找到。我想要实现一个非模板内存池类,用于存储对象数组——只有在访问时才需要明确实际类型,相当于一个迭代器为模板的非模板vector。
我的核心疑问如下:
- 除算术类型外,是否可以省略代码中第45、55行的placement new调用,同时省略Deallocate()中的析构函数循环?
- 对于算术类型,直接用
reinterpret_cast<T*>(p)替代placement new,并且移除析构循环是否安全?还有哪些类型也可以采用这种做法?
注:我使用C++14,同时也关注新版本中的实现方式。
代码示例
#include <cassert> #include <iostream> #include <type_traits> // 支持从单个double值初始化的类型 using test_t = float; // 示例函数:p指向至少3个test_t的连续序列 void load(test_t* p) { std::cout << "starting load\n"; p[0] = static_cast<test_t>(3.14); p[1] = static_cast<test_t>(31.4); p[2] = static_cast<test_t>(314.); std::cout << "ending load\n"; } // 类型双关缓冲区 // 持有无类型缓冲区(实际是char*),可根据需求存储任意类型 struct Buffer { // 缓冲区地址 char* p = nullptr; // 已存储元素数量 size_t n = 0; // 缓冲区字节大小 size_t s = 0; // 分配足够容纳N个T类型对象的char缓冲区,并默认构造它们 // 若未先调用Deallocate就对已分配缓冲区调用此函数,属于未定义行为 template <typename T> T* DefaultAllocate(const size_t N) { size_t RequiredSize = sizeof(std::aligned_storage_t<sizeof(T), alignof(T)>) * N; n = N; T* tmp; if (s < RequiredSize) { if (p) { delete[] p; } s = RequiredSize; std::cout << "Requiring " << RequiredSize << " bytes of storage\n"; p = new char[s]; // 数组版placement new默认构造 tmp = new (p) T[N]; // T* tmp = reinterpret_cast<T*>(p); // // 对于算术类型和无需关心默认值的平凡可析构类型可选 // for (size_t i = 0; i < n; ++i) { // new (tmp + i) T(); // } } else { // 数组版placement new默认构造 tmp = new (p) T[N]; // T* tmp = reinterpret_cast<T*>(p); // // 对于算术类型和无需关心默认值的平凡可析构类型可选 // for (size_t i = 0; i < n; ++i) { // new (tmp + i) T(); // } } return tmp; } // 销毁缓冲区中的对象但不释放缓冲区本身 template <typename T> void Deallocate() { T* tmp = reinterpret_cast<T*>(p); // 按创建的逆序销毁元素 // 对于平凡可析构类型可选 for (size_t i = 0; i < n; ++i) { tmp[n - 1 - i].~T(); } n = 0; } ~Buffer() { if (p) { delete[] p; } } }; int main() { constexpr std::size_t N = 3; Buffer B; test_t* fb = B.DefaultAllocate<test_t>(N); load(fb); std::cout << fb[0] << '\n'; std::cout << fb[1] << '\n'; std::cout << fb[2] << '\n'; std::cout << alignof(test_t) << '\t' << sizeof(test_t) << '\n'; B.Deallocate<test_t>(); return 0; }
编辑补充
原C++14代码存在对齐不当的问题,现已参考相关回答完成版本改进。
内容的提问来源于Stack Exchange,提问作者Oersted
相关产品推荐
相关产品推荐

