默认初始化
语法
语法
T 对象 ; (1)
new T (2) 注意没有 “()”
解释
默认初始化在三种情况下进行:
- 当不带初始化器而声明具有自动、静态或线程局部存储期的变量时;
- 当以不带初始化器的 new 表达式创建具有动态存储期的对象时,或当以带有由一个空括号对组成的初始化器的 new 表达式创建对象时 (C++03 前);
- 当构造函数初始化器列表中未提及某个基类或非静态数据成员,且调用了该构造函数时。
默认初始化的效果是:
如果 T 是非 POD (C++11 前)类类型,那么考虑各构造函数并实施针对空实参列表的重载决议。调用所选的构造函数(即默认构造函数之一),以提供新对象的初始值;
如果 T 是数组类型,那么该数组的每个元素都被默认初始化;
否则不进行初始化(见注解)。
const 对象的默认初始化
POD类类型是指在C++中,POD(Plain Old Data)是指那些没有用户定义的构造函数、析构函数和虚函数,并且没有包含任何用户声明的非静态成员变量的类。简单来说,POD类型是一种简单的数据结构,其成员变量只包含基本数据类型或其他POD类型,不包含自定义的构造函数或析构函数。
#include <iostream>
int global_var; // 静态存储期,将被初始化为0
void foo() {
int local_var; // 自动存储期,未初始化,其值是未定义的
static int static_var; // 静态存储期,将被初始化为0
thread_local int tls_var; // 线程局部存储期,每个线程有自己的实例,未初始化,其值是未定义的
}
int main() {
foo();
return 0;
}
2.创建具有动态存储期的对象:
#include <iostream>
class MyClass {
public:
MyClass() {
std::cout << "Constructor called\n";
}
};
int main() {
MyClass* ptr = new MyClass; // 动态存储期对象,未初始化,将调用默认构造函数
delete ptr;//可以安全销毁,不会造成指针多次释放
return 0;
}
- 当构造函数初始化器列表中未提及某个基类或非静态数据成员,且调用了该构造函数时。
#include <iostream>
class Base {
public:
Base() {
std::cout << "Base constructor called\n";
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived constructor called\n";
}
};
int main() {
Derived obj; // 派生类对象的构造过程中,基类Base会被默认构造初始化
return 0;
}
const 对象的默认初始化
如果程序调用有 const 限定类型的 T 对象的默认初始化,那么 T 必须是可 const 默认构造 的类类型或它的数组。
如果类类型 T 的默认初始化会调用用户提供(不是从基类继承) (C++11 起)的 T 构造函数,或满足以下条件,那么 T 可 const 默认构造:
#include <iostream>
class X {
public:
X() {
std::cout << "X constructor called\n";
}
};
class Y {
public:
Y() {
std::cout << "Y constructor called\n";
}
};
class Z {
public:
Z(int value) : m_value(value) {
std::cout << "Z constructor called with value " << m_value << "\n";
}
private:
int m_value;
};
class MyClass {
public:
const X x;
const Y y;
const Z z;
MyClass() {
std::cout << "MyClass constructor called\n";
}
};
int main() {
MyClass obj; // MyClass对象的默认初始化
return 0;
}
从不确定字节读取
使用由默认初始化任何非类类型的变量所取得的不确定的值是未定义行为(特别是,它可能是一种陷阱表示),除了下列情况:
将 unsigned char 或 std::byte (C++17 起) 类型的不确定值赋值给另一拥有(可有 cv 限定的)unsigned char 或 std::byte (C++17 起) 类型的变量(变量的值变为不确定,但该行为不是未定义);
用 unsigned char 或 std::byte (C++17 起) 类型的不确定值初始化另一拥有(可有 cv 限定的)unsigned char 或 std::byte (C++17 起)类型的变量;
从以下场合产生 unsigned char 或 std::byte (C++17 起) 类型的不确定值:
条件表达式的第二或第三操作数,
逗号运算符的右操作数,
转型或转换到(可有 cv 限定的)unsigned char 或 std::byte (C++17 起)的操作数,
弃值表达式。
int f(bool b)
{
int x; // OK:x 的值不确定
int y = x; // 未定义行为
unsigned char c; // OK:c 的值不确定
unsigned char d = c; // OK:d 的值不确定
int e = d; // 未定义行为
return b ? d : 0; // 如果 b 为 true 则行为未定义
}
注解
具有自动和动态存储期的非类变量的默认初始化产生具有不确定值的对象(静态和线程局部对象进行的是零初始化)。
不能默认初始化引用和 const 标量对象。
#include <string>
struct T1 { int mem; };
struct T2
{
int mem;
T2() {} // "mem" 不在初始化器列表中
};
int n; // 静态非类,进行两阶段初始化:
// 1) 零初始化将 n 初始化为零
// 2) 默认初始化不做任何事,令 n 保留为零
int main()
{
[[maybe_unused]]
int n; // 非类,值不确定
std::string s; // 类,调用默认构造函数,值是 ""(空字符串)
std::string a[2]; // 数组,默认初始化其各元素,值是 {"", ""}
// int& r; // 错误:引用
// const int n; // 错误:const 的非类
// const T1 t1; // 错误:const 的带隐式默认构造函数的类
[[maybe_unused]]
T1 t1; // 类,调用隐式默认构造函数
const T2 t2; // const 类,调用用户提供的默认构造函数
// t2.mem 被默认初始化(为不确定值)
}