一、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修饰符后的左值,不能给它赋值,但是可以取它的地址左值通常具有一个相对持久的一个状态,比如说是一个局部变量,他至少在当前的函数的栈帧里面是持续存在的

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐