第2.4章: 初始化
什么是对象?
在 C++ 中,对象是一块带有类型的存储区域。 每个对象都有一个类型,它决定了该对象能持有哪些值,以及哪些操作是合法的。 当我们声明一个对象时,我们为它预留了内存;当我们初始化它时,我们给它第一个值。 初始化确保对象从确定的状态开始,而不是含有不确定的数据。
初始化风格
C++ 提供了多种初始化风格。
在现代 C++ 中,我们应当优先使用 {} 的统一初始化,因为它一致,并能防止窄化转换。
#include <fast_io.h>
#include <cstddef>
#include <cstdint>
#include <stdfloat>
int main()
{
using namespace ::fast_io::iomnp;
// 1. Copy initialization with =
::std::size_t a = 42; // object a initialized to 42
println("a = ", a); // prints 42
// 2. Direct initialization with ()
::std::int_least32_t b(100); // object b initialized to 100
println("b = ", b); // prints 100
// 3. Uniform initialization with {}
::std::float64_t pi{3.14159}; // object pi initialized to 3.14159
println("pi = ", pi); // prints 3.14159
// 4. Zero initialization with {}
::std::uint_least16_t u{}; // object u initialized to 0
println("u = ", u); // prints 0
// 5. Copy initialization from another object
::std::size_t c = a; // object c initialized with value of a
println("c = ", c); // prints 42
// 6. Multiple objects initialized together
::std::size_t m = 1, n = 2, o = 3;
println("m = ", m, " n = ", n, " o = ", o); // prints 1 2 3
// 7. Boolean initialization
bool flag = true; // object flag initialized to true
println("flag = ", flag); // prints 1
println("flag (boolalpha) = ", boolalpha(flag)); // prints true
// 8. Typed integer literals with suffixes
::std::size_t sz = 30zu; // literal 30 as ::std::size_t
println("sz = ", sz); // prints 30
unsigned int ui = 42u; // literal 42 as unsigned int
println("ui = ", ui); // prints 42
long long big = 1000000ll; // literal as long long
println("big = ", big); // prints 1000000
// 9. Floating-point literals with suffixes
float f = 3.14f; // literal as float
double d = 3.14; // literal as double (default)
long double ld = 3.14L; // literal as long double
println("f = ", f); // prints 3.14
println("d = ", d); // prints 3.14
println("ld = ", ld); // prints 3.14
}
拷贝初始化 vs 直接初始化
乍看之下,拷贝初始化(=)与直接初始化(())很相似。
二者都给对象赋初值,但它们并不相同:
- 拷贝初始化使用
=。它可能允许隐式转换,并可能调用转换构造函数。 - 直接初始化使用
()。它更严格,并优先选择与实参精确匹配的构造函数。
#include <fast_io.h>
#include <string>
int main()
{
using namespace ::fast_io::iomnp;
// Copy initialization allows implicit conversions
::std::string s1 = "Hello"; // const char* converted to std::string
println("s1 = ", s1);
// Direct initialization calls constructor directly
::std::string s2("World");
println("s2 = ", s2);
// Narrowing conversions
int i1 = 3.5; // copy initialization, allowed (truncated to 3)
// int i2(3.5); // direct initialization, error: narrowing not allowed
}
要点:拷贝初始化更宽松,可能执行隐式转换。
直接初始化更严格。
在现代 C++ 中,我们应当优先使用 {} 的统一初始化,因为它一致,并能防止窄化转换。
使用宏表示带类型的常量
除了 zu 或 ll 这类后缀,C++ 还在
<cinttypes> 中提供了宏,用于创建精确整数类型的常量。
当你想保证字面量在跨平台时具有确定的类型时,这些宏很有用。
#include <fast_io.h>
#include <cinttypes>
#include <cstdint>
int main()
{
using namespace ::fast_io::iomnp;
// Constant of type ::std::uint_least32_t
::std::uint_least32_t x = UINT_LEAST32_C(20);
println("x = ", x); // prints 20
// Constant of type ::std::int_least64_t
::std::int_least64_t y = INT_LEAST64_C(1000000);
println("y = ", y); // prints 1000000
}
这些宏会展开为类型正确的字面量。
例如,UINT_LEAST32_C(20) 保证该常量是
::std::uint_least32_t 类型,与底层平台无关。
各种初始化风格对比
| 风格 | 语法 | 示例 | 说明 |
|---|---|---|---|
| 拷贝初始化 | = |
int x = 5; |
传统风格,可能允许隐式转换。 |
| 直接初始化 | () |
int x(5); |
构造函数风格,比拷贝更严格。 |
| 统一初始化 | {} |
int x{5}; |
现代 C++ 中首选,可防止窄化转换。 |
| 零初始化 | {} |
int x{}; |
初始化为 0(或等价物)。 |
| 从对象拷贝 | = other |
int y = x; |
用另一个对象的值初始化。 |
| 多个对象 | int a=1, b=2; |
int a=1, b=2; |
紧凑地声明多个对象。 |
| 布尔 | = true |
bool flag = true; |
初始化为 true/false。 |
| 带类型的整数字面量 | zu, u, l, ll |
auto n = 30zu; |
后缀指定确切的整数类型。 |
| 浮点字面量 | f, L |
float f = 3.14f; |
后缀指定 float 或 long double。 |
| 带类型的常量宏 | UINT_LEAST32_C(...) |
auto x = UINT_LEAST32_C(20); |
来自 <cinttypes> 的宏可跨平台保证确切的整数类型。 |