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

类型双关:能否省略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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 18:46:17