常量成员堆栈与堆
Const member stack vs heap
如果我尝试编译此代码
struct A {
const int j;
};
A a;
我会收到一个预期的错误:
错误:"结构 A"中未初始化的 const 成员
但是,如果我尝试编译这个:
struct A {
const int j;
};
A * a = new A();
我会得到一个成功的构建。
问题是:为什么new
分配允许在没有显式构造函数和堆栈分配的情况下创建具有 const 成员的变量 - 不是吗?
这不是因为堆分配,而是因为您在分配时使用的括号。如果您这样做,例如
A* a = new A;
它也会失败。
添加括号时它起作用的原因是,您的结构是值初始化的,对于像 POD 类型A
值初始化值初始化每个成员,int
的默认值初始化为零。
这意味着如果您只添加值初始化括号,它可能也可以在堆栈上创建变量:
A a = A(); // watch out for the http://en.wikipedia.org/wiki/Most_vexing_parse
虽然这会带来其他潜在的问题,但如果可以的话,最好使用统一初始化(安C++11(:
A a{};
截至 C++14 的格式不正确。 §12.1 [class.ctor] 说
4 类 X 的默认构造函数定义为已删除 如果:
- [...]
- 任何没有大括号或等号初始值设定项的 const 限定类型(或其数组(的非变体非静态数据成员都没有 用户提供的默认构造函数,
- [...]
如果默认构造函数不是用户提供的,并且
如果:
- 它的类没有虚函数(10.3(和虚基类(10.1(,并且
- 其类中没有非静态数据成员具有大括号或等于初始值设定项,并且
- 其类的所有直接基类都有简单的默认构造函数,并且
- 对于其类中属于类类型(或其数组(的所有非静态数据成员,每个此类都有一个简单的默认值 构造 函数。
§8.5 [dcl.init] 反过来说
7 默认初始化 T 类型的对象意味着:
- 如果 T 是(可能符合 cv 条件的(类类型(条款 9(,则调用 T 的默认构造函数 (12.1((初始化为 如果 T 没有默认构造函数或重载解析,则格式不正确 (13.3( 导致歧义或功能被删除或 无法从初始化上下文中访问(;
- [...]
8 对 T 类型的对象进行值初始化意味着:
- 如果 T 是一个(可能符合 cv 条件的(类类型(条款 9(,没有默认构造函数 (12.1( 或默认构造函数 用户提供或删除,则对象默认初始化;
- [...]
空括号对会导致值初始化。A
的默认构造函数定义为根据 [class.ctor]/p4 删除。因此,通过 [dcl.init]/p8,值初始化意味着默认初始化,而到 p7 时,初始化格式不正确,因为构造函数被删除。
C++11版本实际上允许在这种情况下进行值初始化;它说对于值初始化,除其他外,
如果 T 是(可能符合 cv 条件的(非联合类类型,则没有 用户提供的构造函数,则对象初始化为零,如果 T 的隐式声明的默认构造函数是不平凡的,即 构造函数被调用。
由于根据上面的定义,默认构造函数是微不足道的(即使它被删除了(,所以实际上并没有调用它。这被认为是标准的缺陷,CWG第1301期更改了相关措辞。
编译器供应商通常会实现缺陷解决方案,因此这可以被视为 GCC 4.8 中的错误,该错误已在 4.9 中修复。
- C++类成员:堆栈与堆分配
- "new"创建的实例的所有成员变量是否都存在于堆上而不是堆栈上?
- 在C++中使用链表的堆栈实现中,访问结构体headNode成员count和top会导致运行时错误
- C++ 中类成员的堆栈的 pop() 函数
- 局部堆栈变量成员的返回值优化
- C++堆对象具有堆栈或堆成员?
- 复制堆栈上的成员值或使用指针访问它?
- 在使用 In Order 遍历成员函数时引发异常(堆栈溢出)时出现问题
- 何时在堆与堆栈上具有类成员
- 返回可能在C++中分配在堆栈上的成员时,最佳指针/引用类型是什么
- 使用 STL 堆栈作为C++类的成员
- 当对象包含指针成员时,堆栈上的对象销毁出现分段错误
- 成员对象为unique_ptr或在堆栈上
- 如果我将堆栈上分配的元素的指针推入 std::vector,则其成员的值会发生变化
- C++ 使用单个输入字节文件的不同部分实例化堆栈上的成员类
- 在堆或堆栈上的指针类实例中分配C++非指针数据成员
- 成员函数内存分配堆栈或堆
- 将没有成员的函子设置为类成员对象还是堆栈对象更有效
- 常量成员堆栈与堆
- c++对象中的堆栈成员Vs堆成员