new表达式中直接初始化与列表初始化的差异及规则问询
这问题问得挺到位的,刚好戳中了C++里new表达式初始化的一个容易混淆的点,我来给你掰扯清楚:
首先直接回答你的两个例子:
auto p1 = new int[10]{5};合法,因为这是C++标准允许的数组列表初始化语法;auto p1 = new int[10](5);不合法,因为动态数组的初始化根本不支持圆括号()这种写法。
先拆解两个例子的差异
合法的花括号版本
当你用new int[10]{5}时,这属于列表初始化(list initialization)的范畴:
- 第一个数组元素会被初始化为
5; - 剩下的9个int元素会被自动值初始化为
0(因为int是算术类型,值初始化的结果就是0)。
这种部分初始化的行为是标准明确规定的,完全合法。
不合法的圆括号版本
而new int[10](5)试图用圆括号传递一个值,想把整个数组的10个元素都初始化为5——但C++标准从一开始就没给动态数组设计这种初始化语法!圆括号的直接初始化语法只适用于单个对象(比如new int(5)是合法的,它会把单个int对象初始化为5),放到数组上就是语法错误,编译器直接拒绝。
再梳理New表达式的完整初始化规则
结合你查到的标准条文,我把它拆成「单个对象」和「数组」两种场景,更容易理解:
场景1:分配单个对象(如new T、new T(args)、new T{args})
- 如果省略初始化器(
new T):对象会被默认初始化- 对于int、char这类内置类型:默认初始化意味着不做初始化,对象的值是不确定的(垃圾值);
- 对于类对象:会调用类的默认构造函数(如果没有默认构造函数,编译器报错)。
- 如果提供初始化器:不管是圆括号
()还是花括号{},都遵循直接初始化规则- 圆括号
():适合传递构造函数的参数(比如new std::string(5, 'a'),调用接受count和char的构造函数); - 花括号
{}:属于列表初始化,除了能调用构造函数,还会禁止窄化转换(比如new int{3.14}会报错,因为double转int是窄化)。
- 圆括号
场景2:分配数组(如new T[N]、new T[N]{...})
- 如果省略初始化器(
new T[N]):数组的每个元素都会被默认初始化- 内置类型元素值不确定,类对象调用默认构造函数(无默认构造则报错)。
- 如果提供初始化器:只能用花括号
{}的列表初始化,圆括号完全不支持- 若花括号内元素数量 ≤ N:前几个元素用对应值初始化,剩余元素被值初始化(内置类型为0,类对象调用默认构造);
- 若花括号内元素数量 > N:编译器直接报错(数组越界式的初始化不允许)。
补充一句:为什么标准这么设计?
其实是为了避免语法歧义,同时让数组初始化的语义更清晰。比如如果允许new int[10](5),那很容易和单个对象的new int(5)混淆,而列表初始化的{}语法,能明确表达「我要给数组的元素逐个/部分赋值」的意图,逻辑上更通顺。
内容的提问来源于stack exchange,提问作者ledonter
相关产品推荐
相关产品推荐

