Const成员堆栈与堆

时间:2014-11-29 08:13:01

标签: c++ constructor

如果我尝试编译this code

struct A {
    const int j;
};
A a;

我会收到预期的错误:

  

错误:'struct A'中未初始化的const成员

但是,如果我尝试编译this one

struct A {
    const int j;
};
A * a = new A();

我会成功建立。

问题是:为什么new分配允许使用const成员创建变量而没有显式构造函数和堆栈分配 - 不是吗?

2 个答案:

答案 0 :(得分:10)

这不是因为堆分配,而是因为分配时使用的括号。如果你这样做。

A* a = new A;

它也会失败。

添加括号时它起作用的原因是因为那时你的结构是值初始化的,对于像A这样的POD类型的值 - 初始化值 - 初始化每个成员,以及{的默认值初始化{1}}为零。

这意味着如果只添加值初始化括号,它可能也可以在堆栈上创建变量:

int

虽然这会带来其他潜在的问题,但如果可以的话,最好使用统一初始化(要求C ++ 11):

A a = A(); // watch out for the http://en.wikipedia.org/wiki/Most_vexing_parse

答案 1 :(得分:6)

从C ++ 14开始,这是不正确的。 §12.1[class.ctor]说

  

4类X的默认默认构造函数定义为已删除   如果:

     
      
  • [...]
  •   
  • 没有 brace-or-equal-initializer 的const-qualified类型(或其数组)的任何非变量非静态数据成员都没有   用户提供的默认构造函数,
  •   
  • [...]
  •   
     

如果默认构造函数不是用户提供的,并且如果:

,则默认构造函数是微不足道的      
      
  • 其类没有虚函数(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 issue 1301改变。

编译器供应商通常会实现缺陷解决方案,因此可以将其视为GCC 4.8中已在4.9中修复的错误。