【问题标题】:Dynamic array creation without default constructor没有默认构造函数的动态数组创建
【发布时间】:2021-09-04 23:45:16
【问题描述】:

免责声明:我已经知道原始数组不是 C++ 中的一等元素,并且在许多地方,我们希望将它们替换为向量。但我还是希望换个方式……

上下文:

我正在尝试使用连续数据而不是向量向量来构建多维容器库。一个简单的类比是 2D C 数组与指针数组。多维部分作为一维数组上的代理类处理。这意味着使用代理迭代器,但我目前的问题是底层一维数组的处理。

理想情况下,一维数组可以使用静态或自动内存(使用后不应释放)或应使用的私有动态内存。到目前为止一切顺利,我目前使用布尔值 (owning) 来记住是否应该删除数组。我需要 手动管理该内存,因为我希望子数组重新使用其容器的内存,并且不能使用标准容器的复制语义。

问题:

如果底层数据类型有默认构造器,一切都很好,我可以安全地使用new[] 构建动态数组,然后使用delete[] 释放它。但我也想知道是否可以只需要一个可复制类型,并使用默认值初始化数组(std::vector 可以做什么)。

问题:

如何构建非默认可构造对象的动态数组,而不是使用默认值的复制初始化?

目前的研究:

如果 T 类有一个默认的 ctor,我可以写:

    template <typename T>
    class Holder
    {
        size_t sz;         // size of the array
        bool owning;       // true if the array should be deleted
        T* data;           // pointer to the underlying array

    public:
        // builds an array of default initialized data
        Holder(size_t sz) : sz(sz), owning(true) {
            data = new T[sz];
        }

        // uses an existing array. Do not own it by default but can steal
        // it if own is true
        Holder(T* data, size_t sz, bool own=false) : sz(sz),
            owning(own), data(data) {};

        // builds an array of data initialized from a default value
        Holder(const T&value, size_t sz) : sz(sz), owning(true) {
            // what can be done here if no default ctor is available?
        }

        ~Holder() {
            if (owning) {
                delete[] data;
            }
        }

        // Copy ctor. Will borrow the original array
        Holder(const Holder<T>& other) : sz(other.sz), owning(false), data(other.data){}

        // Move ctor
        Holder(Holder&& other) : sz(other.sz), owning(other.owning) {
            data = other.data;
            other.data = nullptr;
            other.owning = false;
        }
        // assignment operators and accessors omitted for brievety
        ...
    };

我也知道我可以用std::uninitialized_fill 安全地填充一个未初始化的数组,然后用std::destroy 销毁它的对象,但我不确定如何正确使用它以及它是否与new[] 和@987654329 兼容@。换一种说法,我可以只用delete 分配一个用operator new[] 分配并用uninitialized_fill 初始化的数组吗?

【问题讨论】:

    标签: c++ arrays dynamic-memory-allocation default-constructor


    【解决方案1】:

    我不确定如何正确使用它以及它是否与new[]delete[] 兼容。

    不兼容new T[],但兼容new char[]

    template <typename T>
    class Holder
    {
        size_t sz;         // size of the array
        bool owning;       // true if the array should be deleted
        T* data;           // pointer to the underlying array
    
        using storage_t = std::aligned_storage_t<sizeof(T), alignof(T)>;
    
        static T* alloc(size_t sz) { // or just use an Allocator
            return reinterpret_cast<T*>(reinterpret_cast<char*>(new storage_t[sz])); 
        }
    
    public:
        // builds an array of default initialized data
        Holder(size_t sz) requires std::default_initializable<T> : sz(sz), owning(true), data(alloc(sz)) {
            std::uninitialized_default_construct_n(data, sz);
        }
    
        // uses an existing array. Do not own it by default but can steal
        // it if own is true
        // This is now suspect, we should take a deleter here.
        // Holder(T* data, size_t sz, bool own=false) : sz(sz),
        //     owning(own), data(data) {};
    
        // uses an existing array. Do not own it
        Holder(T* data, size_t sz) : sz(sz), owning(false), data(data) {};
    
        // uses an existing array. Do not own it
        template <size_t N>
        Holder(T (&data)[N]) : sz(N), owning(false), data(data) {};
    
        // builds an array of data initialized from a default value
        Holder(const T&value, size_t sz) requires std::copy_constructible<T> : sz(sz), owning(true), data(alloc(sz)) {
            std::uninitialized_fill_n(data, sz, value);
        }
    
        ~Holder() {
            if (owning) {
                std::destroy_n(data, sz);
                delete[] reinterpret_cast<storage_t*>(data);
            }
        }
    
        // Copy ctor. Will borrow the original array
        Holder(const Holder<T>& other) : sz(other.sz), owning(false), data(other.data){}
    
        // Move ctor
        Holder(Holder&& other) : data(std::exchange(other.data, nullptr)) sz(other.sz), owning(std::exchange(other.owning, false)) {
        }
        // assignment operators and accessors omitted for brievety
        ...
    };
    

    【讨论】:

    • 确实是一个不错的方法,因为它允许对默认值和默认值初始化情况进行一致的处理。此外,如果我必须复制数据,它可以使用。如果我得到其他答案,我会等待一段时间,但我可能会接受这个......
    • 为了简单起见,我建议使用std::allocator 而不是我在这里做的aligned_storage_t 舞蹈,或者如果您希望能够获得数组的所有权,您可以使用std::function&lt;void(T*)&gt; dealloc; bool owning;
    猜你喜欢
    • 2018-01-22
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    • 2016-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多