这是关于一个普通双非本科大一学生的C++的学习记录贴
在此前,我学了一点点C语言还有简单的数据结构,如果有小伙伴想和我一起学习的,可以私信我交流分享学习资料
那么开启正题
今天分享的是关于C++的语法知识点
1.列表初始化
在C++11中扩大了花括号的括起的初始化列表的使用范围,使用初始化列表时可以使用=也可以不使用
void Test1()
{
vector<int> v1{ 2,6,23,1,6,4 };
vector<int> v2 = { 2,6,23,1,6,4 };
string s{ "hello" };
int x1 = 1;
int x2{ 2 };
}
这个语法了解即可,不常用
这里还涉及到initializer_list,它一般作为构造函数的参数,使初始化容器更加方便
2.声明
C++提供了多种简化声明的方式,尤其是在使用模板时
2.1auto
auto我们已经很熟悉了,它可以自动识别类型,增强代码可读性
注意:auto不能作为函数参数列表以及返回值的类型
2.2decltye
我们知道typeid可以返回一个数据的类型
void Test2()
{
int a;
cout << typeid(a).name() << endl;
cout << typeid(string).name() << endl;
}
但是如果我们想利用一个已知数据,创建一个与其相同类型的数据,typeid还是无法做到,因此C++11引入了decltye关键字,可以完成这个任务
void Test3()
{
int x = 1;
double y = 3.14;
decltype(x * y) a;
decltype(&x) b;
cout << typeid(a).name() << endl;
cout << typeid(b).name() << endl;
}
2.3nullptr
在C语言中空指针是NULL,实质上他是由0定义而来的,很显然这不够严谨,于是C++11引入了nullptr,他是由(void*)0定义而来的
#define 0 NULL
#define (void*)0 nullptr
3.范围for
这个我们已经很熟悉了,这里不展开说了,简单回顾一下,范围for会被编译器转化为迭代器,所以支持迭代器就支持范围for,这也是queue,stack不支持范围for的原因
4.新容器
array:定长数组,实际中并不常用,这里不做详细展开
forward_list:单链表,实际中并不常用,这里不做详细展开
void Test4()
{
array<int, 100> arr = { 1,2,4, };
forward_list<int> fl = { 3,5,6,2 };
}
unordered_set,unordered_map:
哈希表实现的set,map,因为比map,set效率高,是我们常用,也是需要重点掌握的容器,由于刚学完这块内容,也不过多介绍了
5.右值引用与移动语义
5.1左值引用与右值引用
前面我们学过左值引用,他是为已经开辟空间的对象再取一个别名,右值引用实质上也是对对象取别名,但是它们操作的场景不同
我们先来思考一下,什么是左值,什么是右值
可以修改的可以认为是左值,左值一般是变量
右值通常是常量,表达式或者函数返回值(临时对象)
void Test5()
{
int a = 0;
int& b = a;
int&& c = 0;
}
左值引用右值引用的定义如上
5.2左值引用右值引用比较
左值引用:左值引用只能引用左值,加上const后才能引用右值
右值引用:右值引用只能引用右值,但可以引用move后的左值
void Test6()
{
int a = 0;
int& b = a;
//int& c = 0; // err
const int& c = 0;
int&& x = 0;
int y = 0;
//int&& z = y; //err
int&& z = move(y);
}
5.3右值引用的意义
通过上面的例子我们好像看不出来右值引用的意义,而左值引用的意义我们熟悉,减少不必要的深拷贝,提高效率,右值引用的意义到底是什么呢?
右值引用引用的对象右值,我们得对右值下文章
右值可以像以下这样分类
纯右值:基本类型的常量或临时对象
将亡值:自定义类型的临时对象
我们的右值引用就是对将亡值下文章的
我们来看以下情景
void Test7()
{
string s1("hello");
string s2(string("world"));
}
如果我们用一个临时对象对s2初始化,(不考虑编译器优化)那么会先创建一个临时匿名对象,再深拷贝给s2,再析构临时匿名对象,这里很明显消耗了不必要的效率,如果我们将临时匿名对象的_str与s2的交换,就不用深拷贝了,而用右值引用(与左值引用的拷贝构造 构成重载)我们就可以检测到并且节省效率
string& string(const string& s)
{
//...
}
string& string(string&& s)
{
swap(_str, s._str);
return *this;
}
我们把这里的构造叫做移动构造,实际运用中,不仅仅有移动构造,还有移动赋值
前面我们说到,右值引用可以引用move后的左值,实际上,他会强制将左值转化为右值,实现移动语义
新手写博客,有不对的位置希望大佬们能够指出,也谢谢大家能看到这里,让我们一起学习进步吧!