C++学习:六个月从基础到就业------C++11/14:列表初始化
本文是我C++学习之旅系列的第四十三篇技术文章,也是第三阶段"现代C++特性"的第五篇,主要介绍C++11/14中的列表初始化特性。查看完整系列目录了解更多内容。
引言
在C++11之前,C++中的初始化语法存在不一致性和局限性,不同类型的对象需要使用不同的初始化语法。C++11引入了统一初始化语法(通常称为花括号初始化或列表初始化),极大地简化和统一了对象初始化过程。这一特性让代码更加一致、安全,并为容器和用户自定义类型提供了更直观的初始化方式。
本文将深入探讨列表初始化的语法、原理、应用场景以及C++14中的改进,帮助你全面掌握这一现代C++的重要特性。
目录
- C++11/14:列表初始化
统一初始化语法基础
传统初始化方式的问题
在C++11之前,C++中存在多种不同的初始化语法,这导致了语言的不一致性和学习难度:
cpp
// 变量初始化
int a = 10; // 赋值初始化
int b(20); // 直接初始化
// 数组初始化
int arr1[] = {1, 2, 3, 4, 5}; // 数组初始化语法
// 结构体和类初始化
struct Point { int x, y; };
Point p1 = {1, 2}; // 聚合初始化
Point p2(1, 2); // 构造函数初始化
// 动态分配的数组初始化
int* pArr = new int[3]; // 无法在创建时初始化内容
pArr[0] = 1; pArr[1] = 2; pArr[2] = 3;
这些不同语法之间的不一致性使得代码难以维护,也增加了初学者的学习负担。
花括号初始化的基本语法
C++11引入的花括号初始化(也称为列表初始化)提供了一种统一的、适用于几乎所有场景的初始化语法:
cpp
#include <iostream>
#include <vector>
#include <map>
#include <string>
struct Point {
int x, y;
};
class Rectangle {
public:
Rectangle(int width, int height) : width_(width), height_(height) {}
int width() const { return width_; }
int height() const { return height_; }
private:
int width_;
int height_;
};
int main() {
// 基本类型初始化
int a{10};
double b{3.14};
bool flag{true};
// 使用空花括号表示零初始化
int c{}; // 等价于 int c{0};
// 数组初始化
int arr[]{1, 2, 3, 4, 5};
// 动态分配的数组初始化
int* pArr = new int[3]{1, 2, 3};
// POD类型的聚合初始化
Point p{1, 2};
// 使用构造函数的对象初始化
Rectangle rect{3, 4};
// 容器初始化
std::vector<int> vec{1, 2, 3, 4, 5};
std::map<std::string, int> ages{{"Alice", 25}, {"Bob", 30}, {"Charlie", 35}};
// 嵌套列表初始化
std::vector<std::vector<int>> matrix{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
// 输出结果
std::cout << "a = " << a << std::endl;
std::cout << "Rectangle: " << rect.width() << "x" << rect.height() << std::endl;
std::cout << "Vector: ";
for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;
std::cout << "Matrix:" << std::endl;
for (const auto& row : matrix) {
for (int num : row) {
std::cout << num << " ";
}
std::cout << std::endl;
}
delete[] pArr;
return 0;
}
花括号初始化的特点
列表初始化相比其他初始化方式具有以下特点:
- 统一性:几乎适用于所有C++类型的初始化
- 防止窄化转换:不允许可能导致数据丢失的类型转换
- 数组初始化:允许直接初始化动态分配的数组
- 集合类型友好:特别适合容器和聚合类型的初始化
std::initializer_list与列表初始化
initializer_list的基本概念
在支持列表初始化的底层,C++11引入了std::initializer_list<T>
模板类,它表示一个常量数组的视图。当使用花括号创建列表时,编译器会自动构造一个std::initializer_list
对象。
std::initializer_list
具有以下特点:
- 轻量级:不拥有元素,只是提供对元素的访问
- 只读:不能修改列表中的元素
- 类似容器:提供了
begin()
、end()
和size()
方法 - 支持范围循环:可以使用范围for循环遍历
示例代码:
cpp
#include <iostream>
#include <initializer_list>
#include <string>
// 接受initializer_list的函数
void printNumbers(std::initializer_list<int> numbers) {
std::cout << "Numbers: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
}
// 具有initializer_list构造函数的类
class DataContainer {
private:
std::vector<int> data;
public:
// 使用initializer_list构造
DataContainer(std::initializer_list<int> values) : data(values) {
std::cout << "Created container with " << values.size() << " elements" << std::endl;
}
// 接受initializer_list的方法
void addValues(std::initializer_list<int> values) {
data.insert(data.end(), values.begin(), values.end());
}
void print() const {
std::cout << "Container content: ";
for (int value : data) {
std::cout << value << " ";
}
std::cout << std::endl;
}
};
int main() {
// 使用initializer_list参数调用函数
printNumbers({1, 2, 3, 4, 5});
// 创建显式initializer_list对象
std::initializer_list<std::string> names = {"Alice", "Bob", "Charlie"};
std::cout << "Names: ";
for (const auto& name : names) {
std::cout << name << " ";
}
std::cout << std::endl;
// 使用initializer_list构造对象
DataContainer container{10, 20, 30};
container.print();
// 使用initializer_list参数调用方法
container.addValues({40, 50, 60});
container.print();
return 0;
}
自定义类的列表初始化
要让自定义类支持列表初始化,有两种主要方法:
- 聚合初始化:用于满足聚合条件的类或结构体
- initializer_list构造函数:用于接受任意数量的同类型值
聚合类型的列表初始化
聚合类型满足以下条件:
- 无用户定义的构造函数
- 无私有或受保护的非静态数据成员
- 无基类
- 无虚函数
聚合类型可以直接使用花括号初始化:
cpp
struct Point {
int x, y;
};
struct Rectangle {
Point topLeft;
Point bottomRight;
};
int main() {
// 聚合初始化
Point p{10, 20};
// 嵌套聚合初始化
Rectangle rect{{0, 0}, {100, 100}};
return 0;
}
使用initializer_list构造函数
非聚合类型可以通过提供接受std::initializer_list
的构造函数来支持列表初始化:
cpp
#include <iostream>
#include <initializer_list>
#include <vector>
#include <string>
class CustomVector {
private:
std::vector<int> data;
public:
// 默认构造函数
CustomVector() = default;
// 从initializer_list构造
CustomVector(std::initializer_list<int> values) : data(values) {}
// 添加元素
void add(int value) {
data.push_back(value);
}
// 打印内容
void print() const {
std::cout << "CustomVector: ";
for (int value : data) {
std::cout << value << " ";
}
std::cout << std::endl;
}
// 返回大小
size_t size() const {
return data.size();
}
};
int main() {
// 使用initializer_list构造函数
CustomVector v1{1, 2, 3, 4, 5};
v1.print();
// 空列表初始化
CustomVector v2{};
v2.print();
// 添加元素
v2.add(10);
v2.add(20);
v2.print();
return 0;
}
为类添加多个列表初始化构造函数
可以添加多个接受不同类型initializer_list
的构造函数:
cpp
#include <iostream>
#include <initializer_list>
#include <string>
#include <vector>
class MultiTypeContainer {
private:
std::vector<int> integers;
std::vector<std::string> strings;
public:
// initializer_list<int>构造函数
MultiTypeContainer(std::initializer_list<int> ints) : integers(ints) {
std::cout << "Constructed with integers" << std::endl;
}
// initializer_list<string>构造函数
MultiTypeContainer(std::initializer_list<std::string> strs) : strings(strs) {
std::cout << "Constructed with strings" << std::endl;
}
// 打印内容
void print() const {
if (!integers.empty()) {
std::cout << "Integers: ";
for (int value : integers) {
std::cout << value << " ";
}
std::cout << std::endl;
}
if (!strings.empty()) {
std::cout << "Strings: ";
for (const auto& str : strings) {
std::cout << str << " ";
}
std::cout << std::endl;
}
}
};
int main() {
// 使用int列表初始化
MultiTypeContainer container1{1, 2, 3, 4, 5};
container1.print();
// 使用string列表初始化
MultiTypeContainer container2{"hello", "world", "initializer", "list"};
container2.print();
return 0;
}
列表初始化的高级特性
防止窄化转换
列表初始化的一个重要安全特性是防止窄化转换(Narrowing Conversion)。窄化转换是指可能导致数据丢失或更改的隐式类型转换,例如从浮点数到整数、从大整数类型到小整数类型等。
使用花括号初始化时,如果发生窄化转换,编译器将产生错误:
cpp
#include <iostream>
int main() {
// 以下代码能够正常编译
int a = 3.14; // 允许从double到int的窄化
int b(3.14); // 允许从double到int的窄化
// 以下代码在编译时会报错
// int c{3.14}; // 错误:从double到int的窄化转换
// char d{1000}; // 错误:从int到char的窄化转换
// 以下代码不是窄化,因此正常
double e{3}; // 正确:从int到double不是窄化
int f{3}; // 正确:相同类型
// 即使是稍大类型到小类型的转换也会报错
short s = 32767;
// int arr[2]{s, 40000}; // 错误:40000超出short范围
std::cout << "Traditional initialization allows narrowing: a = " << a << std::endl;
std::cout << "Direct initialization allows narrowing: b = " << b << std::endl;
return 0;
}
这种防止窄化转换的特性增强了代码的安全性,可以在编译阶段捕获潜在的数据丢失问题。
拷贝列表初始化与直接列表初始化
C++11中的列表初始化有两种形式:
- 直接列表初始化 :
T obj{arg1, arg2, ...};
- 拷贝列表初始化 :
T obj = {arg1, arg2, ...};
这两种形式在大多数情况下行为相似,但存在一些细微差别:
cpp
#include <iostream>
#include <vector>
class ExplicitConstructor {
public:
// 带有explicit关键字的构造函数
explicit ExplicitConstructor(int value) : value_(value) {
std::cout << "Explicit constructor called with " << value << std::endl;
}
int getValue() const { return value_; }
private:
int value_;
};
int main() {
// 直接列表初始化
ExplicitConstructor obj1{42}; // 正确:直接初始化可以使用explicit构造函数
// 拷贝列表初始化
// ExplicitConstructor obj2 = {42}; // 错误:拷贝初始化不能用explicit构造函数
// 对于标准容器的区别
std::vector<int> vec1{5}; // 直接创建了一个包含单个元素5的向量
std::vector<int> vec2 = {5}; // 使用initializer_list创建向量
std::cout << "vec1 size: " << vec1.size() << std::endl; // 输出 1
std::cout << "vec2 size: " << vec2.size() << std::endl; // 输出 1
// 可能导致混淆的例子
std::vector<int> vec3{5, 6}; // 创建包含5和6两个元素的向量
std::vector<int> vec4{5}; // 创建包含一个元素5的向量
std::vector<int> vec5(5); // 创建包含5个值为0的元素的向量
std::cout << "vec3 size: " << vec3.size() << std::endl; // 输出 2
std::cout << "vec4 size: " << vec4.size() << std::endl; // 输出 1
std::cout << "vec5 size: " << vec5.size() << std::endl; // 输出 5
return 0;
}
自动型别推导与列表初始化
C++11中的auto
关键字与列表初始化结合时,遵循特殊规则:
cpp
#include <iostream>
#include <vector>
#include <typeinfo>
int main() {
// auto与列表初始化
auto a = {1, 2, 3}; // a的类型是std::initializer_list<int>
// 验证类型
std::cout << "Type of a: " << typeid(a).name() << std::endl;
// 使用auto和列表初始化的典型应用
for (auto x : {1, 2, 3, 4, 5}) {
std::cout << x << " ";
}
std::cout << std::endl;
// C++17中的变化:列表只有一个元素时
auto b = {42}; // 在C++11/14中:std::initializer_list<int>
// 下面这行在C++17之前会产生错误,C++17中合法
// auto c{42}; // C++17: int,C++11/14: std::initializer_list<int>
return 0;
}
在C++17之前,auto
与列表初始化一起使用时总是创建std::initializer_list
对象。C++17引入了一些变化,单元素的直接列表初始化不再创建initializer_list
。
列表初始化的实际应用
容器初始化
列表初始化最常见的应用是简化容器的初始化:
cpp
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <string>
int main() {
// 向量的列表初始化
std::vector<int> numbers{1, 2, 3, 4, 5};
// 集合的列表初始化
std::set<int> uniqueNumbers{3, 1, 4, 1, 5, 9, 2, 6, 5};
// 映射的列表初始化
std::map<std::string, int> ages{
{"Alice", 25},
{"Bob", 30},
{"Charlie", 35}
};
// 嵌套容器的初始化
std::vector<std::vector<int>> matrix{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 输出内容
std::cout << "Numbers: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
std::cout << "Unique numbers: ";
for (int num : uniqueNumbers) {
std::cout << num << " ";
}
std::cout << std::endl;
std::cout << "Ages:" << std::endl;
for (const auto& [name, age] : ages) {
std::cout << name << ": " << age << std::endl;
}
std::cout << "Matrix:" << std::endl;
for (const auto& row : matrix) {
for (int value : row) {
std::cout << value << " ";
}
std::cout << std::endl;
}
return 0;
}
函数返回值的列表初始化
列表初始化还可以用于简化函数返回值的创建:
cpp
#include <iostream>
#include <vector>
#include <utility>
#include <string>
// 返回vector的函数
std::vector<int> getNumbers() {
return {1, 2, 3, 4, 5}; // 返回初始化列表
}
// 返回pair的函数
std::pair<std::string, int> getNameAndAge() {
return {"Alice", 30}; // 返回初始化列表
}
// 返回嵌套容器的函数
std::vector<std::pair<std::string, int>> getPeople() {
return {
{"Alice", 25},
{"Bob", 30},
{"Charlie", 35}
};
}
int main() {
auto numbers = getNumbers();
auto [name, age] = getNameAndAge(); // C++17结构化绑定
auto people = getPeople();
std::cout << "Numbers: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
std::cout << "Name: " << name << ", Age: " << age << std::endl;
std::cout << "People:" << std::endl;
for (const auto& [person, personAge] : people) {
std::cout << person << ": " << personAge << std::endl;
}
return 0;
}
作为函数参数
列表初始化可以直接在函数调用时使用:
cpp
#include <iostream>
#include <vector>
#include <algorithm>
// 接受vector的函数
void processNumbers(const std::vector<int>& numbers) {
std::cout << "Processing: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
}
// 接受initializer_list的函数
template<typename T>
void printValues(std::initializer_list<T> values) {
std::cout << "Values: ";
for (const auto& value : values) {
std::cout << value << " ";
}
std::cout << std::endl;
}
int main() {
// 函数调用时的列表初始化
processNumbers({1, 2, 3, 4, 5});
// 对模板函数的调用
printValues({1, 2, 3, 4, 5}); // T推导为int
printValues({"a", "b", "c", "d"}); // T推导为const char*
printValues({1.1, 2.2, 3.3}); // T推导为double
// 与算法结合使用
std::vector<int> vec{10, 20, 30};
vec.insert(vec.begin(), {-3, -2, -1, 0});
std::cout << "Vector after insertion: ";
for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
配合类成员使用
列表初始化可以用于类的非静态成员变量初始化:
cpp
#include <iostream>
#include <vector>
#include <string>
class Person {
private:
std::string name;
int age;
std::vector<std::string> hobbies;
public:
// 使用默认成员初始化器
Person() : name("Unknown"), age(0), hobbies{} {}
// 接受name和age的构造函数
Person(std::string n, int a) : name(std::move(n)), age(a), hobbies{} {}
// 接受所有字段的构造函数
Person(std::string n, int a, std::initializer_list<std::string> h)
: name(std::move(n)), age(a), hobbies(h) {}
// 打印信息
void print() const {
std::cout << "Name: " << name << ", Age: " << age << std::endl;
if (!hobbies.empty()) {
std::cout << "Hobbies: ";
for (const auto& hobby : hobbies) {
std::cout << hobby << " ";
}
std::cout << std::endl;
}
}
};
// C++11允许非静态成员变量初始化
class Widget {
private:
int id{0}; // 默认初始化为0
std::string name{"Default"}; // 默认初始化为"Default"
std::vector<int> data{1, 2, 3, 4, 5}; // 使用列表初始化
public:
// 构造函数可以重写成员初始化器
Widget() = default;
Widget(int i) : id(i) {} // name和data使用默认初始化器
void print() const {
std::cout << "Widget ID: " << id << ", Name: " << name << std::endl;
std::cout << "Data: ";
for (int value : data) {
std::cout << value << " ";
}
std::cout << std::endl;
}
};
int main() {
// 使用不同构造函数创建Person对象
Person p1;
Person p2("Alice", 30);
Person p3("Bob", 25, {"Reading", "Gaming", "Hiking"});
std::cout << "Person 1:" << std::endl;
p1.print();
std::cout << "\nPerson 2:" << std::endl;
p2.print();
std::cout << "\nPerson 3:" << std::endl;
p3.print();
// 使用成员初始化器的Widget
std::cout << "\nWidget 1:" << std::endl;
Widget w1;
w1.print();
std::cout << "\nWidget 2:" << std::endl;
Widget w2(42);
w2.print();
return 0;
}
C++14中的列表初始化改进
C++14在列表初始化方面没有引入重大变更,但在相关领域有一些改进:
auto与返回值推导
C++14允许使用auto
作为函数返回类型,这与列表初始化结合提供了更简洁的语法:
cpp
#include <iostream>
#include <vector>
#include <utility>
// C++14: 使用auto返回值
auto createVector() {
return std::vector<int>{1, 2, 3, 4, 5};
}
auto createPair() {
return std::make_pair("Answer", 42);
}
// 返回初始化列表需要指定类型
std::vector<int> getNumbers() {
return {1, 2, 3, 4, 5};
}
int main() {
auto vec = createVector();
auto pair = createPair();
std::cout << "Vector: ";
for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;
std::cout << "Pair: " << pair.first << ", " << pair.second << std::endl;
return 0;
}
通用lambda表达式
C++14引入的通用lambda表达式可以与列表初始化一起使用,提供更灵活的内联函数:
cpp
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
// 使用初始化列表创建向量
std::vector<int> numbers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 使用通用lambda和列表初始化
auto processValues = [](auto container, auto processor) {
for (auto& item : container) {
processor(item);
}
};
// 对奇数加倍,偶数设为零
auto transformer = [](int& num) {
if (num % 2 == 1) {
num *= 2;
} else {
num = 0;
}
};
// 使用lambda处理向量
processValues(numbers, transformer);
// 使用列表初始化直接传递数据
processValues({100, 200, 300}, [](int& num) {
std::cout << "Processing: " << num << std::endl;
});
// 输出结果
std::cout << "Transformed numbers: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
列表初始化的最佳实践与常见陷阱
最佳实践
-
尽可能使用花括号初始化:花括号初始化提供了更一致、更安全的初始化语法
cppint a{42}; // 推荐 double b{3.14}; // 推荐 std::vector<int> v{1, 2, 3}; // 推荐
-
利用防止窄化的特性增强代码安全性:
cpp// 利用防止窄化转换发现潜在问题 float f = 3.14f; // int i{f}; // 编译错误:可能丢失数据 int i{static_cast<int>(f)}; // 显式转换表明意图
-
对容器优先使用列表初始化:
cpp// 推荐 std::vector<int> vec{1, 2, 3, 4, 5}; std::map<std::string, int> map{{"a", 1}, {"b", 2}};
-
对类成员使用列表初始化:
cppclass Example { private: int value{0}; // 良好的默认初始化 std::vector<int> data{}; // 明确初始化为空容器 };
常见陷阱
-
vector初始化歧义:
cppstd::vector<int> v1(3, 5); // 3个值为5的元素:[5, 5, 5] std::vector<int> v2{3, 5}; // 2个元素,值为3和5:[3, 5]
-
auto与列表初始化的交互:
cppauto a = {1, 2, 3}; // std::initializer_list<int>,不是vector或数组 // 更清晰的方式 std::vector<int> v{1, 2, 3}; // 明确指定类型
-
单元素列表vs括号初始化:
cppstd::vector<int> v1(10); // 10个值为0的元素 std::vector<int> v2{10}; // 1个值为10的元素
-
构造函数重载与initializer_list:
cppclass Widget { public: Widget(int a, int b) { /* ... */ } Widget(std::initializer_list<int> list) { /* ... */ } }; Widget w1(10, 20); // 调用第一个构造函数 Widget w2{10, 20}; // 调用第二个构造函数(initializer_list),而非第一个
当类既有常规构造函数又有initializer_list构造函数时,花括号初始化优先选择initializer_list构造函数。
-
空列表初始化:
cppstd::vector<int> v{}; // 空向量 int x{}; // 值初始化为0 // 对于某些类型,{}和()行为不同 int a{}; // 值初始化为0 int b(); // 声明一个名为b的函数,而非变量初始化!
实际应用示例
示例1:简化配置对象创建
cpp
#include <iostream>
#include <string>
#include <vector>
// 应用程序配置
struct AppConfig {
std::string appName;
std::string version;
int maxConnections;
bool debugMode;
std::vector<std::string> supportedFileTypes;
};
// 创建默认配置
AppConfig createDefaultConfig() {
return {
"MyApp",
"1.0.0",
10,
false,
{".txt", ".csv", ".json"}
};
}
// 打印配置
void printConfig(const AppConfig& config) {
std::cout << "Application Configuration:" << std::endl;
std::cout << "Name: " << config.appName << std::endl;
std::cout << "Version: " << config.version << std::endl;
std::cout << "Max Connections: " << config.maxConnections << std::endl;
std::cout << "Debug Mode: " << (config.debugMode ? "Enabled" : "Disabled") << std::endl;
std::cout << "Supported File Types: ";
for (const auto& type : config.supportedFileTypes) {
std::cout << type << " ";
}
std::cout << std::endl;
}
int main() {
// 使用返回的默认配置
AppConfig config = createDefaultConfig();
printConfig(config);
// 直接使用列表初始化创建自定义配置
AppConfig customConfig{
"CustomApp",
"2.1.0",
20,
true,
{".xml", ".bin", ".dat"}
};
std::cout << "\nCustom Configuration:" << std::endl;
printConfig(customConfig);
return 0;
}
示例2:数据处理管线
cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
#include <functional>
// 数据处理步骤函数类型
using DataProcessor = std::function<std::vector<double>(const std::vector<double>&)>;
// 创建一个处理管线
std::vector<double> processPipeline(
const std::vector<double>& input,
const std::vector<DataProcessor>& processors
) {
std::vector<double> result = input;
for (const auto& processor : processors) {
result = processor(result);
}
return result;
}
// 数据处理函数
std::vector<double> normalizeData(const std::vector<double>& data) {
if (data.empty()) return {};
double sum = std::accumulate(data.begin(), data.end(), 0.0);
double mean = sum / data.size();
std::vector<double> result;
result.reserve(data.size());
std::transform(data.begin(), data.end(), std::back_inserter(result),
[mean](double x) { return x - mean; });
return result;
}
std::vector<double> squareValues(const std::vector<double>& data) {
std::vector<double> result;
result.reserve(data.size());
std::transform(data.begin(), data.end(), std::back_inserter(result),
[](double x) { return x * x; });
return result;
}
std::vector<double> filterOutliers(const std::vector<double>& data) {
if (data.empty()) return {};
double threshold = 2.0;
std::vector<double> result;
std::copy_if(data.begin(), data.end(), std::back_inserter(result),
[threshold](double x) { return std::abs(x) <= threshold; });
return result;
}
int main() {
// 使用列表初始化创建输入数据
std::vector<double> inputData{1.2, 3.4, 0.5, 7.8, -2.1, 0.0, 5.5};
// 使用列表初始化创建处理管线
std::vector<DataProcessor> pipeline{
normalizeData,
squareValues,
filterOutliers
};
// 处理数据
auto result = processPipeline(inputData, pipeline);
// 打印结果
std::cout << "Input data: ";
for (double value : inputData) {
std::cout << value << " ";
}
std::cout << std::endl;
std::cout << "Processed data: ";
for (double value : result) {
std::cout << value << " ";
}
std::cout << std::endl;
return 0;
}
示例3:自定义JSON构建器
cpp
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <initializer_list>
// 简单的JSON值类
class JsonValue {
public:
enum class Type { Null, Boolean, Number, String, Array, Object };
private:
Type type;
bool boolValue;
double numberValue;
std::string stringValue;
std::vector<JsonValue> arrayValues;
std::map<std::string, JsonValue> objectValues;
public:
// 构造函数
JsonValue() : type(Type::Null) {}
JsonValue(bool value) : type(Type::Boolean), boolValue(value) {}
JsonValue(int value) : type(Type::Number), numberValue(value) {}
JsonValue(double value) : type(Type::Number), numberValue(value) {}
JsonValue(const char* value) : type(Type::String), stringValue(value) {}
JsonValue(const std::string& value) : type(Type::String), stringValue(value) {}
// 数组构造函数
JsonValue(std::initializer_list<JsonValue> values)
: type(Type::Array), arrayValues(values) {}
// 对象构造函数
JsonValue(std::initializer_list<std::pair<const std::string, JsonValue>> values)
: type(Type::Object), objectValues(values) {}
// 访问方法
Type getType() const { return type; }
// 转换为字符串
std::string toString() const {
std::ostringstream oss;
switch (type) {
case Type::Null:
oss << "null";
break;
case Type::Boolean:
oss << (boolValue ? "true" : "false");
break;
case Type::Number:
oss << numberValue;
break;
case Type::String:
oss << "\"" << stringValue << "\"";
break;
case Type::Array:
oss << "[";
for (size_t i = 0; i < arrayValues.size(); ++i) {
if (i > 0) oss << ", ";
oss << arrayValues[i].toString();
}
oss << "]";
break;
case Type::Object:
oss << "{";
{
size_t i = 0;
for (const auto& [key, value] : objectValues) {
if (i++ > 0) oss << ", ";
oss << "\"" << key << "\": " << value.toString();
}
}
oss << "}";
break;
}
return oss.str();
}
};
int main() {
// 使用列表初始化创建JSON值
// 简单值
JsonValue nullValue;
JsonValue boolValue{true};
JsonValue numberValue{42};
JsonValue stringValue{"Hello, world!"};
// 数组
JsonValue arrayValue{1, 2, 3, 4, 5};
// 嵌套数组
JsonValue nestedArray{
"fruits",
{"apple", "banana", "cherry"}
};
// 对象
JsonValue objectValue{
{"name", "John Doe"},
{"age", 30},
{"isEmployee", true}
};
// 复杂嵌套对象
JsonValue person{
{"name", "Alice"},
{"age", 28},
{"address", {
{"street", "123 Main St"},
{"city", "Anytown"},
{"zipcode", "12345"}
}},
{"hobbies", {"reading", "hiking", "coding"}}
};
// 打印结果
std::cout << "Null: " << nullValue.toString() << std::endl;
std::cout << "Boolean: " << boolValue.toString() << std::endl;
std::cout << "Number: " << numberValue.toString() << std::endl;
std::cout << "String: " << stringValue.toString() << std::endl;
std::cout << "Array: " << arrayValue.toString() << std::endl;
std::cout << "Nested Array: " << nestedArray.toString() << std::endl;
std::cout << "Object: " << objectValue.toString() << std::endl;
std::cout << "Person: " << person.toString() << std::endl;
return 0;
}
总结
列表初始化是C++11引入的一个强大特性,它解决了C++初始化语法不一致的问题,提供了统一、安全且灵活的初始化方式。通过花括号{}
和std::initializer_list
,我们可以以一致的方式初始化几乎所有C++类型,从基本类型到复杂容器和用户自定义类型。
列表初始化的主要优势包括:
- 语法统一性:提供了一致的初始化语法,适用于几乎所有C++类型
- 增强的类型安全:通过防止窄化转换,减少了潜在的数据丢失错误
- 简洁性和表达力:使代码更加简洁明了,特别是对于复杂类型和容器
- 灵活性:能够轻松处理嵌套初始化和复杂对象构造
在实际应用中,列表初始化已经成为现代C++编程的标准实践,它与其他C++11/14特性(如auto
、decltype
、lambda表达式等)结合使用,可以显著提高代码的可读性和安全性。
在下一篇文章中,我们将继续探索C++11/14中的其他语言特性,包括nullptr
、constexpr
、static_assert
和范围for循环等,这些特性共同构成了现代C++编程的基础。
这是我C++学习之旅系列的第四十三篇技术文章。查看完整系列目录了解更多内容。