在不调用构造函数的情况下创建对象数组

时间:2017-07-11 13:24:04

标签: c++ memory-alignment c++03

对于嵌入式系统,我们需要一个自定义向量类,其中通过模板参数在编译期间设置容量。

到目前为止,我们有一个对象数组作为成员变量。

template<class T, size_t SIZE>
class Vector {
     ...
     T data[SIZE];
}

这里的问题当然是如果T不是POD,则调用T的默认构造函数。有没有办法让数据在相应的push()调用之前保持未初始化(内置新的放置)?只是使用

uint8_t data[SIZE * sizeof(T)];

可能会破坏T的对齐。我们绝对不能使用动态内存,总容器大小总是需要在编译时知道。我们也不能使用C ++的alignas说明符,因为编译器还不支持C ++ 11 :(

3 个答案:

答案 0 :(得分:1)

您将不得不使用展示位置// use `std::max_align_t` and `std::aligned_storage` when you have it // since don't have access to alignof(), use the presumably max // alignment value using MaxAlign = long; template <typename T, int size> class UninitializedArray { union Node { char data[sizeof(T)]; MaxAlign alignment; }; Node aligned_data[size]; bool initialized; public: UninitializedArray() : initialized(false) {} void initialize() { for (int i = 0; i < static_cast<int>(size); ++i) { new (&this->aligned_data[i].data) T(); } this->initialized = true; } ~UninitializedArray() { if (this->initialized) { for (int i = 0; i < static_cast<int>(size); ++i) { T* ptr = reinterpret_cast<T*>(&this->aligned_data[i].data); ptr->~T(); } } } T& operator[](int index) { if (!this->initialized) { this->initialize(); } T* ptr = reinterpret_cast<T*>(&this->aligned_data[i].data); return *ptr; } }; 以及联合技巧来正确设置对齐方式。

UninitializedArray<Something, 5> arr;
arr[0].do_something();

然后像这样使用它

std::array

如果你让C ++ 17正常工作,那么你可以使用std::optionalstd::optional<std::array<T, N>> optional_array; // construct the optional, this will construct all your elements optional_array.emplace(); // then use the value in the optional by "treating" the optional like // a pointer optional_array->at(0); // returns the 0th object 来实现这一目标

{gridColumn: '1 / 3', gridRow: '2 / 5', gridArea: 'header'}

答案 1 :(得分:1)

首先我会检查编译器是否支持对齐,即gcc有__attribute__(aligned(x)),可能有类似的东西。

然后,如果你必须在没有这种支持的情况下对齐未初始化的数据,你将不得不浪费一些空间

// Align must be power of 2
template<size_t Len, size_t Align>
class aligned_memory
{
public:
    aligned_memory()
      : data((void*)(((std::uintptr_t)mem + Align - 1) & -Align)) {}
    void* get() const {return data;}
private:
    char mem[Len + Align - 1];
    void* data;
};

你可以使用新的随附

template<typename T, size_t N>
class Array
{
public:
    Array() : sz(0) {}
    void push_back(const T& t)
    {
        new ((T*)data.get() + sz++) T(t);
    }

private:
    aligned_memory<N * sizeof(T), /* alignment */> data;
    size_t sz;
};

Live

可以在C ++ 11 T中找到alignof的对齐方式,检查编译器是否支持可用于找出其对齐方式的任何内容。您也可以从打印的指针值中猜测,并希望这足够了。

答案 2 :(得分:1)

另一种方法是将std::vector<>与在堆栈上分配的自定义分配器一起使用。

这样你就可以创建一个空向量,保留所需的空间,该空间应该等于你的分配器在堆栈上为你分配的空间,然后使用vector<>::emplace_back填充向量。您的元素类型可以是不可复制的,但在这种情况下必须是可移动的。

E.g:

#include <vector>

struct X {
    X(int, int);

    // Non-copyable.
    X(X const&) = delete;
    X& operator=(X const&) = delete;

    // But movable.
    X(X&&);
    X& operator=(X&&);
};

template<class T, std::size_t N>
struct MyStackAllocator; // Implement me.

int main() {
    std::vector<X, MyStackAllocator<X, 10>> v;
    v.reserve(10);
    v.emplace_back(1, 2);
    v.emplace_back(3, 4);
}

有关如何实施分配器的信息广泛可用,例如,在YouTube上搜索&#34; c ++分配器&#34;。