没有默认构造函数的动态数组创建
Dynamic array creation without default constructor
免责声明:我已经知道原始数组不是 C++ 中的第一个 class 元素,而且在许多地方,我们希望用向量替换它们。但我还是希望换一种方式...
上下文:
我正在尝试使用连续数据而不是向量的向量来构建多维容器库。一个简单的类比是二维 C 数组与指针数组。多维部分作为一维数组上的代理 classes 处理。这意味着使用 代理迭代器 但我当前的问题是底层 1D 数组的处理。
理想情况下,一维数组可以使用静态或自动内存(并且不应在使用后释放)或应该使用的私有动态内存。到目前为止一切顺利,我目前使用布尔值 (owning
) 来记住是否应删除数组。我需要
手动管理该内存,因为我希望子数组重新使用其容器的内存并且不能使用标准容器的复制语义。
问题:
如果底层数据类型有一个默认构造函数,一切都很好,我可以安全地用 new[]
构建一个动态数组,然后用 delete[]
释放它。但我也想知道是否可以只需要一个可复制类型,并使用默认值初始化数组(std::vector
可以做什么)。
问题:
如何构建非默认可构造对象的动态数组,而不是使用默认值的复制初始化?
当前研究:
如果默认构造函数可用于 class T,我可以写:
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[]
和 delete[]
。换句话说,我可以 delete
一个用 operator new[]
分配并用 uninitialized_fill
初始化的数组吗?
I am unsure of how to correctly use that and whether it is compatible with new[]
and 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
...
};
免责声明:我已经知道原始数组不是 C++ 中的第一个 class 元素,而且在许多地方,我们希望用向量替换它们。但我还是希望换一种方式...
上下文:
我正在尝试使用连续数据而不是向量的向量来构建多维容器库。一个简单的类比是二维 C 数组与指针数组。多维部分作为一维数组上的代理 classes 处理。这意味着使用 代理迭代器 但我当前的问题是底层 1D 数组的处理。
理想情况下,一维数组可以使用静态或自动内存(并且不应在使用后释放)或应该使用的私有动态内存。到目前为止一切顺利,我目前使用布尔值 (owning
) 来记住是否应删除数组。我需要
手动管理该内存,因为我希望子数组重新使用其容器的内存并且不能使用标准容器的复制语义。
问题:
如果底层数据类型有一个默认构造函数,一切都很好,我可以安全地用 new[]
构建一个动态数组,然后用 delete[]
释放它。但我也想知道是否可以只需要一个可复制类型,并使用默认值初始化数组(std::vector
可以做什么)。
问题:
如何构建非默认可构造对象的动态数组,而不是使用默认值的复制初始化?
当前研究:
如果默认构造函数可用于 class T,我可以写:
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[]
和 delete[]
。换句话说,我可以 delete
一个用 operator new[]
分配并用 uninitialized_fill
初始化的数组吗?
I am unsure of how to correctly use that and whether it is compatible with
new[]
anddelete[]
.
与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
...
};