使用 std::aligned_alloc 对对象数组进行类型双关

问题描述 投票:0回答:1

已经有很多关于严格别名规则和类型双关的帖子,但我找不到我可以理解的关于对象数组的解释。 我的目标是拥有一个内存池非模板类,用于存储对象数组。 基本上我只需要在访问时知道实际类型:它可以被视为一个非模板向量,其迭代器将是模板。 我想到的设计提出了几个问题,所以我会尝试将它们分成几个SO问题。 我的问题(这是第三个问题,见下文)只是如何在 C++17 中使用

DefaultAllocate()
实现
std::aligned_alloc
函数(并且不使用注定要弃用的
std::align_storage
)。

#include <cassert>
#include <iostream>
#include <type_traits>

// type that support initialisation from a single double value
using test_t = float;

// just for the sake of the example: p points to at least a sequence of 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";
}

// type-punning buffer
// holds a non-typed buffer (actually a char*) that can be used to store any
// types, according to user needs
struct Buffer {
    // buffer address
    char* p = nullptr;
    // number of stored elements
    size_t n = 0;
    // buffer size in bytes
    size_t s = 0;
    // allocates a char buffer large enough for N object of type T and
    // default-construct them
    // calling it on a previously allocated buffer without adequate call to
    // Deallocate is UB
    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 array default construction
            tmp = new (p) T[N];
            // T* tmp = reinterpret_cast<T*>(p);
            // // optional for arithmetic types and also for trivially
            // destructible
            // // types when we don't care about default values
            // for (size_t i = 0; i < n; ++i) {
            //     new (tmp + i) T();
            // }
        } else {
            // placement array default construction
            tmp = new (p) T[N];
            // T* tmp = reinterpret_cast<T*>(p);
            // // optional for arithmetic types and also for trivially
            // destructible
            // // types when we don't care about default values
            // for (size_t i = 0; i < n; ++i) {
            //     new (tmp + i) T();
            // }
        }
        return tmp;
    }
    // deallocate objects in buffer but not the buffer itself
    template <typename T>
    void Deallocate() {
        T* tmp = reinterpret_cast<T*>(p);
        // Delete elements in reverse order of creation
        // optional for default destructible types
        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;
}

直播
生活更复杂

问题1链接
问题2链接

c++ arrays memory-management c++17 type-punning
1个回答
0
投票

您可以询问编译器,实际上不需要标准库特征;)

template<class T>
struct RequiredSize
{
    alignas(alignof(T)) unsigned char data[sizeof(T)];
};


struct Buffer {
    // ...

    template <typename T>
    T* DefaultAllocate(const size_t N) {
        size_t RequiredSize = sizeof(RequiredSize<T>) * N;
        // ...
    }
};
© www.soinside.com 2019 - 2024. All rights reserved.