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

如何在C++17中用std::aligned_alloc实现DefaultAllocate函数

在C++17中用std::aligned_alloc实现非模板内存池的DefaultAllocate函数

关于严格别名规则和类型双关的讨论已经很多,但针对对象数组的相关解释依然晦涩难懂。我希望实现一个非模板内存池类,用于存储对象数组,仅在访问时确定实际类型——可以将其看作是“迭代器为模板的非模板vector”。本次是系列问题的第三篇:如何在C++17中使用std::aligned_alloc(且不使用已被弃用的std::aligned_storage)实现DefaultAllocate()函数。

原实现代码

#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版本代码存在对齐问题,已有改进版本;前序问题围绕对象数组的类型双关与严格别名规则、省略placement new和析构函数的可行性展开。

内容的提问来源于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:47:01