C++ 在容器中的初始化与作为局部变量的初始化不同
C++ initialize differently in container vs as local variable
这个问题是在我尝试为 OpenGL 缓冲区对象编写 RAII 包装器 class 时出现的。 OpenGL 创建缓冲区对象的方式是调用 void glGenBuffers(GLsizei n, GLuint* buffers)
which "returns n
buffer object names in buffers
." Also, note that the object name 0
is special to OpenGL, being a default value "like a NULL pointer."
所以我的第一个想法是创建一个 class buffer_object
喜欢
class buffer_object {
public:
// constructors, destructor
private:
unsigned int const name;
};
我想要两种不同的初始化行为:
- 如果我创建一个数组
std::array<buffer_object, N> buffer_obj_array {};
,我希望 buffer_object
数组元素的 name
初始化为 0。其他容器也是如此。
- 如果我创建一个局部变量,不要初始化为 0,而是通过调用
glGenBuffers(1, &name)
. 实际给它命名
问题是我没有完全理解列表初始化、值初始化和默认初始化。
关于std::array
的默认构造函数,cppreference.com说它使用聚合初始化,这是一种列表初始化,它的值初始化数组中没有在数组中给出的元素初始化列表。关于值初始化,cppreference.com says,
if T is a class type with at least one user-provided constructor of
any kind, the default constructor is called
那么,这是否意味着无法执行以下操作?
{
buffer_object obj; // has some name given by glGenBuffers; ready to use
int const n = 3;
std::array<buffer_object, n> foo; // each element has name 0
glGenBuffers(n, foo.data()); // gives each element its own name
}
// everything destructed using buffer_object::~buffer_object
— 因为我要求默认构造函数提供两种不同的行为?
也许我采取了错误的方法。
C++ 对象初始化不是,也不能基于 创建对象的位置 (除了普通类型的 default-initializing 对象,zero-initialize 用于全局变量,但不用于自动变量)。
如果你给一个类型一个non-trivial默认构造函数,你就是在做一个强有力的声明,即这种类型的对象需要在它们可以之前执行一些真正的代码被认为是这种类型的有效对象。因此,C++ 将假设您认真对待此声明,并确保在创建该类型的对象时在所有情况下都执行所述“真实代码”。
如果你给 buffer_object
一个生成缓冲区对象的默认构造函数,你就是说这种类型的对象应该始终拥有一个有效的 OpenGL 缓冲区对象(除非它们是 moved-from) .如果你给 buffer_object
一个默认构造函数来清零内部缓冲区对象句柄,你是说没有 OpenGL 缓冲区对象句柄是 buffer_object
处于的正常有效状态。
这两个语句没有half-way。默认为空或不是。
现在,您可以拥有备用构造函数。默认构造函数可以使句柄为零,而另一个构造函数采用简单的标记类型,明确表示它将生成一个句柄。或者更好的是,一个工厂函数如何使 buffer_object
s 已生成句柄:
buffer_object gen_buffer()
{
GLuint handle = 0;
glGenBuffers(1, &handle);
return buffer_object(handle);
};
auto obj = gen_buffer(); //Clearly states what is going on.
现在毫无疑问 obj
中的内容:它有一个生成的缓冲区对象句柄。因为这就是代码所说的内容。
这个问题是在我尝试为 OpenGL 缓冲区对象编写 RAII 包装器 class 时出现的。 OpenGL 创建缓冲区对象的方式是调用 void glGenBuffers(GLsizei n, GLuint* buffers)
which "returns n
buffer object names in buffers
." Also, note that the object name 0
is special to OpenGL, being a default value "like a NULL pointer."
所以我的第一个想法是创建一个 class buffer_object
喜欢
class buffer_object {
public:
// constructors, destructor
private:
unsigned int const name;
};
我想要两种不同的初始化行为:
- 如果我创建一个数组
std::array<buffer_object, N> buffer_obj_array {};
,我希望buffer_object
数组元素的name
初始化为 0。其他容器也是如此。 - 如果我创建一个局部变量,不要初始化为 0,而是通过调用
glGenBuffers(1, &name)
. 实际给它命名
问题是我没有完全理解列表初始化、值初始化和默认初始化。
关于std::array
的默认构造函数,cppreference.com说它使用聚合初始化,这是一种列表初始化,它的值初始化数组中没有在数组中给出的元素初始化列表。关于值初始化,cppreference.com says,
if T is a class type with at least one user-provided constructor of any kind, the default constructor is called
那么,这是否意味着无法执行以下操作?
{
buffer_object obj; // has some name given by glGenBuffers; ready to use
int const n = 3;
std::array<buffer_object, n> foo; // each element has name 0
glGenBuffers(n, foo.data()); // gives each element its own name
}
// everything destructed using buffer_object::~buffer_object
— 因为我要求默认构造函数提供两种不同的行为?
也许我采取了错误的方法。
C++ 对象初始化不是,也不能基于 创建对象的位置 (除了普通类型的 default-initializing 对象,zero-initialize 用于全局变量,但不用于自动变量)。
如果你给一个类型一个non-trivial默认构造函数,你就是在做一个强有力的声明,即这种类型的对象需要在它们可以之前执行一些真正的代码被认为是这种类型的有效对象。因此,C++ 将假设您认真对待此声明,并确保在创建该类型的对象时在所有情况下都执行所述“真实代码”。
如果你给 buffer_object
一个生成缓冲区对象的默认构造函数,你就是说这种类型的对象应该始终拥有一个有效的 OpenGL 缓冲区对象(除非它们是 moved-from) .如果你给 buffer_object
一个默认构造函数来清零内部缓冲区对象句柄,你是说没有 OpenGL 缓冲区对象句柄是 buffer_object
处于的正常有效状态。
这两个语句没有half-way。默认为空或不是。
现在,您可以拥有备用构造函数。默认构造函数可以使句柄为零,而另一个构造函数采用简单的标记类型,明确表示它将生成一个句柄。或者更好的是,一个工厂函数如何使 buffer_object
s 已生成句柄:
buffer_object gen_buffer()
{
GLuint handle = 0;
glGenBuffers(1, &handle);
return buffer_object(handle);
};
auto obj = gen_buffer(); //Clearly states what is going on.
现在毫无疑问 obj
中的内容:它有一个生成的缓冲区对象句柄。因为这就是代码所说的内容。