常平网站公司,如何在网站申请做co,开封市建设局网站,网络推广站目录 0.前言 1.C11介绍 2.统一的列表初始化 2.1{}初始化 2.2initializer_list 2.2.1initializer_list 的基本用法 2.2.2用于类的 initializer_list 构造函数 2.2.3与标准库容器的结合 2.2.4优势与注意事项 3.新声明 3.1auto 3.1.1基本用法 3.1.2优势 3.1.3注意事项 3.2declt… 目录 0.前言 1.C11介绍 2.统一的列表初始化 2.1{}初始化 2.2initializer_list 2.2.1initializer_list 的基本用法 2.2.2用于类的 initializer_list 构造函数 2.2.3与标准库容器的结合 2.2.4优势与注意事项 3.新声明 3.1auto 3.1.1基本用法 3.1.2优势 3.1.3注意事项 3.2decltype 3.2.1基本用法 3.2.2与 auto 的区别 3.2.3用于返回类型推导 3.3nullptr 3.3.1基本用法 3.3.2与 NULL 的区别 3.3.3优势 4.小结 图源网络侵删
0.前言
在现代软件开发中数据结构和编程语言的特性直接影响代码的效率和可维护性。尽管 C98 提供了丰富的数据结构和功能但在代码简洁性和类型安全性方面仍有不足。C11 引入了许多新特性如右值引用、lambda 表达式、智能指针和多线程库等极大地增强了语言的现代化和功能性。本文将重点介绍 C11 的列表初始化和类型声明新特性通过实例展示它们的用法和优势帮助开发者更好地理解和应用这些特性。
1.C11介绍
C11也被称为 C0x是 C 标准的一次重要更新于 2011 年正式发布。它的出现标志着 C 语言的一个重大转折点旨在解决 C98 中的各种局限性和问题使 C 更加现代化和高效。C11 的开发始于 2005 年其目标是提升语言的性能和可用性同时保持与现有代码的兼容性。标准委员会通过广泛的讨论和实验引入了大量的新特性包括右值引用Rvalue References、lambda 表达式、智能指针、多线程库、以及更为简洁和安全的初始化和类型声明方式。C11 不仅极大地丰富了语言本身的功能也为开发者提供了更为强大的工具帮助他们编写更高效、更可靠的代码。
2.统一的列表初始化
C11 引入了统一的列表初始化语法使得对象的初始化方式更加一致和简洁。新的列表初始化方式通过使用花括号 {} 来统一不同类型对象的初始化过程从而简化代码减少错误。
2.1{}初始化
在 C11 之前初始化对象的方式因对象类型的不同而各异。对于基本类型、数组、结构体、类等各自有不同的初始化语法。这种多样化的初始化方式不仅让代码显得杂乱无章还容易引发各种隐式转换错误和未定义行为。C11 的 {} 初始化通过提供一种统一的语法解决了这些问题。
基本类型的初始化
在 C11 中花括号 {} 可以用于初始化基本类型
int a{10}; // 直接初始化
int b {20}; // 复制初始化
int c{}; // 值初始化c 被初始化为 0这种初始化方式的一个显著优点是它能够防止窄化转换narrowing conversion
int x{3.14}; // 错误不能将 double 赋值给 int
int y 3.14; // 有效但 y 被截断为 3通过使用 {} 初始化编译器会检测并阻止潜在的错误转换增强了代码的安全性。
类和结构体的初始化
C11 的 {} 初始化同样适用于类和结构体对象
struct Point {int x;int y;
};Point p1{1, 2}; // 直接初始化
Point p2 {3, 4}; // 复制初始化对于类对象C11 允许通过 {} 初始化来调用构造函数
class MyClass {
public:MyClass(int a, int b) : x(a), y(b) {}
private:int x, y;
};MyClass obj{10, 20}; // 调用 MyClass 的构造函数数组和容器的初始化
C11 使得数组和标准库容器的初始化更加简洁和直观
int arr[] {1, 2, 3, 4, 5}; // 数组初始化
std::vectorint vec{1, 2, 3, 4, 5}; // std::vector 初始化
std::mapint, std::string m {{1, one},{2, two},{3, three}
}; // std::map 初始化这种统一的列表初始化方式不仅简化了代码还增强了代码的可读性和一致性使开发者能够更轻松地管理复杂的数据结构。
2.2initializer_list
C11 引入了 initializer_list 类型以支持更加灵活和统一的列表初始化。initializer_list 是标准库中的一个模板类允许构造函数和其他函数接收一个初始化列表从而简化了容器和对象的初始化过程。
2.2.1initializer_list 的基本用法
initializer_list 提供了一种方式使得函数可以接收一组由花括号 {} 包裹的初始值。以下是一个简单的示例
#include initializer_list
#include iostreamvoid printList(std::initializer_listint list) {for (auto elem : list) {std::cout elem ;}std::cout std::endl;
}int main() {printList({1, 2, 3, 4, 5});return 0;
}在这个例子中函数 printList 接受一个 initializer_listint 类型的参数并通过范围循环range-based for loop来遍历并打印列表中的元素。
2.2.2用于类的 initializer_list 构造函数
initializer_list 常用于定义容器类的构造函数使得容器类可以接受初始化列表从而简化对象的初始化。例如
#include initializer_list
#include vector
#include iostreamclass MyContainer {
public:MyContainer(std::initializer_listint list) {for (auto elem : list) {data.push_back(elem);}}void print() const {for (auto elem : data) {std::cout elem ;}std::cout std::endl;}private:std::vectorint data;
};int main() {MyContainer container {1, 2, 3, 4, 5};container.print();return 0;
}在这个例子中MyContainer 类的构造函数接受一个 initializer_listint 类型的参数并使用该列表初始化 data 成员。这样用户可以直接使用花括号语法来初始化 MyContainer 对象。
2.2.3与标准库容器的结合
initializer_list 在标准库容器中得到了广泛应用。例如std::vector、std::set 和 std::map 等容器都支持通过 initializer_list 进行初始化
#include vector
#include set
#include map
#include iostreamint main() {std::vectorint vec {1, 2, 3, 4, 5};std::setstd::string strSet {one, two, three};std::mapint, std::string intToStrMap {{1, one},{2, two},{3, three}};for (int num : vec) {std::cout num ;}std::cout std::endl;for (const auto str : strSet) {std::cout str ;}std::cout std::endl;for (const auto pair : intToStrMap) {std::cout pair.first : pair.second ;}std::cout std::endl;return 0;
}输出结果 1 2 3 4 5 one three two 1: one 2: two 3: three 在这个示例中std::vector、std::set 和 std::map 都通过 initializer_list 进行了初始化使得代码更加简洁和易读。
2.2.4优势与注意事项
使用 initializer_list 具有以下几个优势
简洁性通过花括号 {} 初始化列表使得代码更加简洁和直观。一致性提供了一种统一的初始化语法适用于各种类型的对象。安全性避免了某些类型转换问题提高了代码的安全性。
然而使用 initializer_list 时也需注意以下几点
不可修改initializer_list 本身是不可修改的不能添加或删除元素。生命周期initializer_list 的元素是对外部数组的引用使用时需要确保数组的生命周期长于 initializer_list。
3.新声明
C11 引入了几种新的类型声明方式使得代码更加简洁和易于维护。新的类型声明方式包括 auto、decltype 和 nullptr它们极大地增强了 C 语言的类型推导能力和代码安全性。
3.1auto
auto 关键字允许编译器根据初始化表达式自动推导变量的类型从而简化了变量声明特别是在复杂类型的声明中。
3.1.1基本用法
auto 可以用于推导任何类型的变量包括基本类型、指针、引用、容器迭代器等
int x 10;
auto a x; // a 被推导为 int 类型double y 5.5;
auto b y; // b 被推导为 double 类型auto c x; // c 被推导为 int* 类型std::vectorint vec {1, 2, 3};
auto it vec.begin(); // it 被推导为 std::vectorint::iterator 类型3.1.2优势
使用 auto 的优势包括
简化代码减少冗长的类型声明特别是在使用 STL 容器和迭代器时。提高可读性让代码更简洁更易读。减少错误降低手动写错类型的风险特别是在类型复杂或容易变化时。
3.1.3注意事项
尽管 auto 带来了诸多便利但在使用时需要注意以下几点 明确性在某些情况下auto 可能会降低代码的明确性和可读性需要开发者对推导出的类型有清晰的认识。 推导规则auto 会根据右值表达式推导类型有时可能会与预期不符。例如数组类型会被推导为指针类型 int arr[3] {1, 2, 3};
auto p arr; // p 被推导为 int*3.2decltype
decltype 关键字用于查询表达式的类型并返回该类型。它在泛型编程和模板编程中尤为有用可以精确地推导出变量或表达式的类型。
3.2.1基本用法
decltype 主要用于变量声明、函数返回类型推导等
int x 10;
decltype(x) y 20; // y 的类型与 x 相同都是 intconst int z x;
decltype(z) w x; // w 的类型与 z 相同都是 const int3.2.2与 auto 的区别
auto 是根据初始化表达式推导类型而 decltype 则是根据表达式的类型进行推导。以下是一个对比示例
int x 10;
auto a x; // a 是 int
decltype(x) b x; // b 是 intconst int y x;
auto c y; // c 是 int丢弃了 const 和引用
decltype(y) d y; // d 是 const int3.2.3用于返回类型推导
在模板编程中decltype 常用于推导函数的返回类型
template typename T1, typename T2
auto add(T1 a, T2 b) - decltype(a b) {return a b;
}3.3nullptr
C11 引入了 nullptr 关键字用于表示空指针替代了传统的 NULL 宏。nullptr 是一个类型安全的空指针字面值避免了 NULL 可能引发的类型转换问题。
3.3.1基本用法
nullptr 可以与任意指针类型兼容
int* p1 nullptr; // int* 类型的空指针
double* p2 nullptr; // double* 类型的空指针void func(int* ptr) {if (ptr nullptr) {std::cout Pointer is null std::endl;}
}int main() {func(nullptr); // 安全地传递空指针return 0;
}3.3.2与 NULL 的区别
NULL 宏通常定义为 0 或 (void*)0在某些情况下可能引发类型不匹配的问题
void f(int);
void f(void*);f(0); // 调用 f(int)
f(NULL); // 调用 f(int)可能意图是 f(void*)
f(nullptr); // 调用 f(void*)避免了歧义3.3.3优势
使用 nullptr 的优势包括
类型安全避免了 NULL 引发的类型转换问题。代码清晰显式表示指针为空使代码更具可读性。
4.小结
C11 引入的列表初始化和新的类型声明方式使得 C 语言更加强大和易用。花括号 {} 初始化和 initializer_list 提供了统一的初始化方式简化了对象的创建过程而 auto、decltype 和 nullptr 则增强了类型推导能力减少了代码中的冗余和错误。掌握这些新特性可以显著提高 C 编程的效率和代码质量。