做一个国外网站,太原站建设有多长时间,做一个手机网站多少钱,商业性质网站建设步骤C发展历史
C11是C语言的第二个主要版本#xff0c;也是自C98以来最重要的一次更新。它引入了大量的新特性#xff0c;标准化了已有的实践#xff0c;并极大地改进了C程序员可用的抽象能力。在2011年8月12日被ISO正式采纳之前#xff0c;人们一直使用“C0x”这个名称#… C发展历史
C11是C语言的第二个主要版本也是自C98以来最重要的一次更新。它引入了大量的新特性标准化了已有的实践并极大地改进了C程序员可用的抽象能力。在2011年8月12日被ISO正式采纳之前人们一直使用“C0x”这个名称因为它原本预计会在2010年之前发布。然而由于各种原因直到2011年才最终确定。C03与C11之间间隔了8年这是C版本发布史上最长的一次。从那时起C社区每三年发布一次新标准保持了更加稳定的更新节奏。 列表初始化
C11引入了列表初始化List Initialization试图统一所有对象的初始化方式使代码更加简洁和安全。然而这也带来了一些细节和概念上的区别可能会引起混淆。该章节将结合具体代码深入讲解C11中的列表初始化与C98进行对比更清晰地理解这些概念。
C98中的初始化方式
在C98中数组和聚合类型如结构体可以使用大括号{}进行初始化但基本类型和自定义类对象通常不能直接使用{}初始化需要使用构造函数或赋值操作。
数组和结构体的初始化
struct Point {int _x;int _y;
};int main() {// 数组初始化int a1[] {1, 2, 3, 4, 5};int a2[5] {0}; // 所有元素初始化为0// 结构体初始化Point p {1, 2};return 0;
}上述代码中数组a1和a2结构体p都使用{}进行初始化这是C98所支持的。
基本类型和自定义类的初始化
在C98中基本类型的初始化不能使用{}需要使用赋值或构造函数。
int x 2; // 赋值初始化对于自定义类对象需要定义构造函数然后使用括号()进行初始化。
class Date {
public:Date(int year 1, int month 1, int day 1): _year(year), _month(month), _day(day) {}private:int _year;int _month;int _day;
};int main() {Date d(2025, 1, 1); // 使用构造函数初始化return 0;
}C11中的列表初始化
C11引入了列表初始化使得几乎所有类型的对象都可以使用{}进行初始化包括基本类型和自定义类对象。这种统一的初始化方式带来了代码简洁性和安全性的提升。
基本类型的列表初始化
int x1 {2}; // 列表初始化
int x2 2; // 传统赋值初始化
int x3{2}; // 省略等号的列表初始化区别x1和x3使用了列表初始化x2使用了传统的赋值初始化。优势列表初始化可以防止窄化转换。例如int x {2.5};会编译错误防止精度丢失。
自定义类型的列表初始化
Date d1 {2025, 1, 1};
Date d20(2025, 1, 1);
const Date d2 {2024, 7, 25};
Date d3 {2025};
Date d4 2025;
Date d6{2024, 7, 25};
const Date d7{2024, 7, 25};Date d1 {2025, 1, 1};使用列表初始化按照语义应该是先构造一个临时的Date对象然后调用拷贝构造函数复制给d1。但是编译器会进行优化直接构造d1避免了拷贝构造。const Date d2 {2024, 7, 25};引用一个临时的Date对象该对象由列表初始化创建。Date d3 {2025};当只有一个参数时列表初始化也可以使用。Date d4 2025;C98中允许的隐式类型转换调用Date(int, int, int)构造函数剩余参数使用默认值。省略等号的初始化Date d6{2024, 7, 25};和const Date d7{2024, 7, 25};。
结构体的列表初始化
struct Point {int _x;int _y;
};int main() {Point p1 {1, 2}; // C98风格Point p2{3, 4}; // C11省略等号return 0;
}容器的列表初始化
vectorDate v;
v.push_back(d1);
v.push_back(Date(2025, 1, 1));
v.push_back({2025, 1, 1}); // 使用列表初始化vectorint v1 {1, 2, 3, 4};
vectorint v2 {10, 20, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1};
const vectorint v4 {10, 20, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1};vectorint v3({10, 20, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1});v.push_back({2025, 1, 1});直接使用列表初始化创建一个Date对象并插入到向量v中。容器的列表初始化v1、v2、v4、v3都使用了列表初始化其中v3显式地调用了构造函数。
std::initializer_list的使用
initializer_listint il1 {10, 20, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1};std::initializer_list是一个轻量级的只读容器用于保存初始化列表中的元素。容器类如vector的构造函数和赋值运算符都增加了接受std::initializer_list的版本因此可以直接使用{}进行初始化。
map的列表初始化和插入
mapstring, string dict;
dict.insert({xxx, yyyy}); // 使用列表初始化的 pairmapstring, string dict2 {{xxx, yyyy}, {sort, zzzz}};dict.insert({xxx, yyyy});{xxx, yyyy}会被隐式转换为std::pairconst string, string然后插入到dict中。dict2的初始化直接使用列表初始化将多个键值对插入到map中。
std::initializer_list原理和作用
C11引入了std::initializer_list使得初始化容器和自定义类型的方式更加灵活和简洁。
背景
在C98中初始化数组和聚合类型如结构体可以使用大括号{}但对于容器和自定义类的初始化尤其是当需要传入多个参数时显得不够方便。例如要初始化一个std::vector对象并赋予多个初始值可能需要多次调用push_back或者手动实现多个构造函数来支持不同数量的参数。
std::vectorint v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
// 或者手动实现多个构造函数来支持不同数量的参数为了解决这个问题C11引入了std::initializer_list提供了一种统一的方式来初始化容器和自定义类型。
std::initializer_list的原理
std::initializer_list是C11标准库中的一个模板类用于表示由大括号{}括起来的一系列元素。它允许我们使用列表初始化的方式为对象赋值从而简化代码书写提高可读性。
#include initializer_liststd::initializer_listint il {1, 2, 3};内部实现
std::initializer_list内部包含了两个指针分别指向初始化列表中第一个元素和最后一个元素的下一个位置。其实现通常如下
template class E
class initializer_list {
public:// 类型定义typedef E value_type;typedef const E reference;typedef const E const_reference;typedef size_t size_type;typedef const E* iterator;typedef const E* const_iterator;// 构造函数initializer_list() noexcept : _array(0), _length(0) {}// 大小和开始、结束迭代器size_t size() const noexcept { return _length; }const E* begin() const noexcept { return _array; }const E* end() const noexcept { return _array _length; }private:// 私有成员const E* _array;size_t _length;
};_array指向存储元素的数组的指针。_length表示元素的数量。
特性
只读容器std::initializer_list是一个轻量级的只读容器不能修改其中的元素。自动推导类型可以通过auto关键字自动推导类型。范围for循环支持使用范围for循环遍历元素。
std::initializer_list的作用
初始化容器
C11中的标准容器如std::vector、std::list、std::map等都增加了接受std::initializer_list的构造函数和赋值运算符使得容器可以方便地使用列表初始化。
#include iostream
#include vector
#include map
#include stringint main() {// 初始化vectorstd::vectorint v1 {1, 2, 3, 4, 5};std::vectorint v2{6, 7, 8, 9, 10}; // 省略等号// 初始化mapstd::mapstd::string, std::string dict {{apple, 苹果},{banana, 香蕉}};// 输出vector内容for (auto val : v1) {std::cout val ;}std::cout std::endl;// 输出map内容for (const auto pair : dict) {std::cout pair.first : pair.second std::endl;}return 0;
}运行结果
1 2 3 4 5
apple: 苹果
banana: 香蕉std::vector的列表初始化通过{}直接传入初始值列表调用了接受std::initializer_list的构造函数。然后作为initializer_list来构造容器。std::map的列表初始化使用{}传入键值对的列表其中每个键值对也是使用{}初始化的std::pair对象也就相当于initializer_list的嵌套构造。
自定义类型的初始化
除了标准容器用户自定义的类也可以通过定义接受std::initializer_list的构造函数来支持列表初始化。
#include iostream
#include initializer_listclass MyClass {
public:MyClass(std::initializer_listint il) {for (auto it il.begin(); it ! il.end(); it) {data.push_back(*it);}}void print() const {for (auto val : data) {std::cout val ;}std::cout std::endl;}private:std::vectorint data;
};int main() {MyClass obj {1, 2, 3, 4, 5};obj.print(); // 输出1 2 3 4 5return 0;
}接受std::initializer_list的构造函数在自定义类MyClass中定义了一个构造函数接受std::initializer_listint类型的参数。使用列表初始化创建对象在main函数中直接使用{1, 2, 3, 4, 5}来初始化MyClass对象。
函数参数的初始化
std::initializer_list也可以作为函数的参数方便地传递一组值。
#include iostream
#include initializer_listvoid printValues(std::initializer_listint il) {for (auto val : il) {std::cout val ;}std::cout std::endl;
}int main() {printValues({10, 20, 30, 40, 50});return 0;
}10 20 30 40 50函数接受std::initializer_list参数printValues函数接受一个std::initializer_listint类型的参数。调用函数时传入列表在调用printValues时直接传入一个初始化列表{10, 20, 30, 40, 50}也可以作为构造函数或拷贝构造函数等的实参进行传入。
容器对std::initializer_list的支持
构造函数
标准容器都增加了接受std::initializer_list的构造函数。例如
// vector的initializer_list构造函数
std::vectorint v {1, 2, 3, 4, 5};// map的initializer_list构造函数
std::mapstd::string, int m {{one, 1}, {two, 2}};赋值运算符
容器的赋值运算符也支持std::initializer_list可以方便地重置容器的内容。
std::vectorint v {1, 2, 3};
v {4, 5, 6}; // 重新赋值示例代码
#include iostream
#include vector
#include mapint main() {// 使用initializer_list初始化vectorstd::vectorint vec {1, 2, 3, 4, 5};// 使用initializer_list赋值vectorvec {6, 7, 8, 9, 10};// 输出vector内容for (auto val : vec) {std::cout val ;}std::cout std::endl;// 使用initializer_list初始化mapstd::mapstd::string, int mp {{apple, 1}, {banana, 2}};// 输出map内容for (const auto pair : mp) {std::cout pair.first : pair.second std::endl;}return 0;
}运行结果
6 7 8 9 10
apple: 1
banana: 2