C++初始化总结

(6 mins to read)

initialization occurs only when an object is first created

initialization gives an object its initial state

for a class type with constructors, initialization invokes a constructor

copy-initialization

T object = other;

aggregate-initialization

1
2
3
4
5
struct S {
int a;
float b;
};
S s = {1};

remaining fields are zero-initialized.

aggregate type的要求粗糙地说是:

  1. 无用户自定义构造函数
  2. 无私有、保护成员
  3. 无虚函数

aggregate-initialization的缺点是使用者有完全的自由,但是有时候类的成员是存在一些潜在的数据依赖的(contract),比如size_t len; char* buf;{5, nullptr}是不该被允许的。

without explicit initializer

  • zero-initialized: object with static or thread storage duration (static, thread_local)
  • indeterminate value: accessing the value is UB

constructors

direct-initialization

  • T object (arg1, arg2, ...);:直接调用构造函数初始化
  • T object {arg1, arg2, ...};
  • T (arg1, arg2, ...);

explicit constructors

拷贝初始化不考虑explicit的构造函数。

这也是直接初始化和拷贝初始化最关键的不同,拷贝初始化需要考虑类型转换问题,它会构造一个隐式的类型转换链。

default initialization

类会调用默认构造函数。

value initialization

  • T();
  • T{};
  • T object {};

注意T object()被视作一个函数声明。

before C++03 缺点:

  • scalar, aggregate, class type do not have uniform initialization syntax
  • potential narrow conversion (short s = i;)
  • value initialization is difficult (the most vexing parse)
  • intialize STL containers is difficult
  • the above issues make template type initialization difficult

Modern C++ adopts brace initialization!

uniform initialization syntax

all types can be initialized via braces {},并且它直接阻止缩窄转换。

empty braces conduct value-initialization

direct-list-initialization

类似于使用圆括号的direct-initialization,这里使用大括号

copy-list-initialization

使用大括号的copy-initialization

initializer list

初始化列表通常按值传递,因为它采用浅拷贝语义,可以视作视图。

STL容器都包含了初始化列表为参数的构造函数。并且初始化列表的优先级是很高的,能解释成初始化列表,就会解释成初始化列表。

如果提供了初始化列表的构造函数,要特别注意其它带有多个相同类型的构造函数。

1
2
std::vector<int> v(12); // with size 12
std::vector<int> v{12}; // with one element 12 (calls construtor taking initializer_list)
1
2
3
4
5
std::make_unique<std::vector<int>>(1, 2);
// whats this?
// it depends on std::unique_ptr<T>(new T(std::forward<Args>(args...)))
// 注意标准库用的是()而不是{}初始化
// 这又导致一个问题是aggregate type没法初始化了,所以C++20又允许aggregate type使用()初始化

总结

direct-initialization, copy-initialization, brace-initialization

  • scalar type使用copy-initialization,因为这符合习惯 (most readable),而且编译器会帮你优化
  • aggregate and class type尽量使用brace-initialization (direct-list-initialization),更安全并避免不必要的拷贝与隐式转换,但有时为了区别与初始化列表必须选择圆括号,注意区分

zero-initialization, value-initialization, default-initialization