C++11引入了哪些资源管理新规则?右值引用与移动语义为何是性能优化的关键?
ok,我们通过代码来看一下——其实我们看到这个 { } 初始化对于内置类型的便捷之处好像没有那么明显,那我们接着看对于自定义类型——代码语言:javascriptAI代码解释。
一、C++11的发展历史
C++11是C++的第二个主要版本,并且是从C++98起的最重要更新。它引入了大量更改,标准化了既有实践,并改正了C++程序员可用的抽象。在它最终由ISO在2011年8月12日采纳前,人们曾使用名称“C++0x”,因为它曾被期待在2010年之前发布。C++03与C++11期间花了8年时间,故而这是迄今为止最长的版本间隔。从那时起,C++有规律地每3年更新一次

ok,接下来,我们就开始学习C++11的相关知识~
二、列表初始化
也许会有很多UU看到这个标题之后,会想到我们之前学的初始化列表,ok,初始化列表和列表初始化列表是没有任何关系的,这两个不是一回事儿~
2.1 C++98传统的{ }
在C++98中,我们通常使用 { } 进行数组或者结构体的初始化——
代码语言:javascript
AI代码解释
struct A
{
int _a1;
int _a2;
};
void test1()
{
//使用{ }对数组初始化
int a[] = { 1,2,3,4,5,6,7 };
//使用{ } 对结构初始化
A b={ 1,2};
}
但是在C++11中使用{ } 的方法更高级点——
2.2 C++11中的 { }
- C++11以后想统一初始化方式,试图实现一切对象皆可使用 { } 初始化,{ } 初始化也叫做列表初始化
- 内置类型支持,自定义类型也支持,自定义类型本质是类型转换,中间会产生临时对象,最后优化了以后变成直接构造
- { }初始化的过程中,可以省略=
ok,我们通过代码来看一下——

其实我们看到这个 { } 初始化对于内置类型的便捷之处好像没有那么明显,那我们接着看对于自定义类型——
代码语言:javascript
AI代码解释
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{
cout << "Date(int year, int month, int day)" << endl;
}
Date(const Date& d)
:_year(d._year)
, _month(d._month)
, _day(d._day)
{
cout << "Date(const Date& d)" << endl;
}
private:
int _year;
int _month;
int _day;
};
void Insert(const Date& d)
{
}
Date func()
{
/*Date d{ 2025,11,16 };
return d;*/
//return { 2025,11,16 };
//返回默认构造
Date d;
return d;
//return {};
}
int main()
{
int array1[] = { 1, 2, 3, 4, 5 };
int array2[5] = { 0 };
Date d2 = 1025;//单参数的隐式类型转换
Date d2 = { 2025,11,17 };
Date d3{ 2025,11,17 };
Date d4{};//调用默认构造,用缺省值
Date d5; //也是调用默认构造,用缺省值
Insert(2025);
Insert({ 2025,11,17 });
// C++11⽀持的
// 内置类型⽀持
int x1 = { 2 };
// ⾃定义类型⽀持
// 这⾥本质是⽤{ 2025, 1, 1}构造⼀个Date临时对象
// 临时对象再去拷⻉构造d1,编译器优化后合⼆为⼀变成{ 2025, 1, 1}直接构造初始化d1
// 运⾏⼀下,我们可以验证上⾯的理论,发现是没调⽤拷⻉构造的
Date d1 = { 2025, 1, 1 };
// 这⾥d2引⽤的是{ 2024, 7, 25 }构造的临时对象
const Date& d2 = { 2024, 7, 25 };
// 需要注意的是C++98⽀持单参数时类型转换,也可以不⽤{}
Date d3 = { 2025 };
Date d4 = 2025;
// 可以省略掉=
Point p1{ 1, 2 };
int x2{ 2 };
Date d6{ 2024, 7, 25 };
const Date& d7{ 2024, 7, 25 };
// 不⽀持,只有{}初始化,才能省略=
// Date d8 2025;
vector<Date> v;
v.push_back(d1);
v.push_back(Date(2025, 1, 1));
// ⽐起有名对象和匿名对象传参,这⾥{}更有性价⽐
v.push_back({ 2025, 1, 1 });
return 0;
}

- 可以将 = 省略——


我们可以从图中看到,内置类型同样也可以用{ }初始化,但是我们看习惯了直接赋值,那么这里还要用{ }初始化的意义是什么呢?
- C++就是要让一切皆可用{}初始化
2.3 C++11中的std::initializer_list
上面的初始化已经很方便,但是对象容器初始化还是不太方便,比如一个 vector 对象,我想用 N 个值去构造初始化,那么我们得实现很多个构造函数才能支持,vector<int> v1 = {1,2,3};vector<int> v2 = {1,2,3,4,5};
initializer_list - C++ Reference
- C++11 库中提出了一个std::initializer_list的类,auto il = { 10, 20, 30 }; // the type of il is an initializer_list,这个类的本质是底层开一个数组,将数据拷贝过来,std::initializer_list内部有两个指针分别指向数组的开始和结束
- 容器支持一个std::initializer_list的构造函数,也就支持任意多个值构成的 {x1,x2,x3...} 进行初始化。STL 中的容器支持任意多个值构成的 {x1,x2,x3...} 进行初始化,就是通过std::initializer_list的构造函数支持的
代码语言:javascript
AI代码解释
int main()
{
vector<int> v1 = { 1,2,3,4,5,6 };
vector<int> v2{ 7,8,9 };
//vector(initializer_list<T> l) vector传给initializer_list<T>
//{
// for (auto e : l)
// push_back(e)
//}
map<string, string>dict = { {"sort","排序"},{"string","字符串"} };
v1 = { 10,20,30 };
auto il = { 10,20,30 };
cout << typeid(il).name() << endl;
std::initializer_list<int> mylist;
mylist = { 10, 20, 30 };
cout << sizeof(mylist) << endl;
// 这⾥begin和end返回的值initializer_list对象中存的两个指针
// 这两个指针的值跟i的地址跟接近,说明数组存在栈上
int i = 0;
cout << mylist.begin() << endl;
cout << mylist.end() << endl;
cout << &i << endl;
return 0;
}
三、右值引用和移动语义
在C++98中的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,C++11之后我们就将之前学习的引用叫做左值引用。但是无论是左值引用还是右值引用,都是给对象去别名。(并且底层都是由指针实现的)
ok,那接下来,我们就来看看什么是左值,什么是右值?
3.1 左值和右值
左值是一个数据的表达式:
- 变量名
- 解引用的指针
- 函数调用
- ……
一般是有持久状态,存储在内存中,我们就可以获取它的地址。
左值可以出现在赋值符号的左边,也可以出现在赋值符号的右边。定义时const修饰符后的左值,不能给它赋值,但是可以取它的地址左值通常具有一个相对持久的一个状态,比如说是一个局部变量,他至少在当前的函数的栈帧里面是持续存在的
更多推荐



所有评论(0)