文章目录
- 1.列表初始化
- 1.1内置类型列表初始化
- 1.2自定义类型列表初始化
- 2.变量类型的推导
- 2.1 编译时类型推导auto
- 2.2运行时类型推导 decltype
- 3.final、override
- 4.新增容器
- 5.默认成员函数控制
- 6.右值引用
- 6.1普通引用和右值引用
- 6.2 移动语义
- 6.3为什么提出右值引用
- 6.4总结
- 6.5右值引用引用左值
1.列表初始化
C++ 98之中,只能对数组进行列表初始化,在C++ 11 之中添加了新语法,可以对内置类型列表初始化、自定义类型的列表初始化
1.1内置类型列表初始化
1.2自定义类型列表初始化
1.支持单个对象的列表初始化
2.多个对象的列表初始化(不支持序列式类型)
多个对象想要初始化,需要给该类添加一个带有initializer_list类型参数的构造函数即可。
initializer_list是系统自定义的类模板,该模板中主要有三个方法:begin(),end()迭代器,以及获取区间中元素个数的方法size()
2.变量类型的推导
2.1 编译时类型推导auto
作用:简化类型写法
缺点:可读性变差
auto是编译时,根据初始化表达式类型进行推导的
因此,auto对运行时的类型推导是无能为力的
2.2运行时类型推导 decltype
C++ 98支持的RTTI(运行时类型识别)
typeid只能查看类型不能用其结果定义类型
dynamic_cast只能应用于含有虚函数的继承体系中
运行时类型识别的缺陷是降低程序运行的效率
decltype是根据表达式的实际类型推演出定义变量时所用的类型:
1.推演表达式类型作为变量的定义类型
2.推演函数返回值的类型
3.final、override
final:修饰虚函数,表示该虚函数不能再被继承
override:检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
详情
4.新增容器
静态数组 array:没什么用,在栈上开辟的静态数组,灵活性不够
forward_list:单链表
unordered系列
5.默认成员函数控制
C++中的空类,会默认生成一些成员函数,但是这些函数如果程序员自己编写了,就不会默认生成。然而有时候又需要默认生成,这就容易造成混乱,因此C++11,提供两个关键字,让程序员自己决定是否需要编译器生成
default
在默认函数定义或者声明时加上=default,可以显示的指示编译器生成该函数的默认版本,用=default修饰的函数称为显示缺省函数
delete
在C++98之中,将函数设置成private并且不给定义,其它人就无法调用
在C++11之中,主需要在函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,常用于防止拷贝
6.右值引用
左值:使用空间
右值:使用内容
C++ 11 中对右值进行了严格的区分(除了右值就是左值):
纯右值: 比如 a+b, 100
将亡值: 比如表达式的中间结果,函数按照值的方式进行返回
6.1普通引用和右值引用
C++98之中:普通引用只能引用左值,不能引用右值,const引用既可以引用左值,又可以引用右值
C++11中的右值引用:只能引用右值,一般情况下不能直接引用左值
6.2 移动语义
C++ 11 提出了移动语义的概念:将一个对象中资源转移到另一个对象的方式,可以有效的缓解拷贝构造对象时,资源浪费的情况
6.3为什么提出右值引用
右值引用表示指向的实体为右值,实体的资源可以直接被拿走,提高拷贝的效率,本质上为浅拷贝
#include "test.h"
class String
{
public:
String(const char *str="")
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
cout << "String(const char *str&#61;"")" << endl;
}
String(const String &str)
:_str(new char[strlen(str._str)&#43;1])
{
strcpy(_str, str._str);
cout << "String(const String &str)" << endl;
}
String(String &&str)
:_str(str._str)
{
str._str &#61; nullptr;
cout << "String(const String &&str)" << endl;
}
String &operator&#61;(String &&str)
{
cout << " String &operator&#61;(String &&str)" << endl;
if (_str !&#61; str._str)
{
_str &#61; str._str;
str._str &#61; nullptr;
}
return *this;
}
String& operator&#61;(const String &str)
{
if (_str !&#61; str._str)
{
cout << "String &operator&#61;(String &str)" << endl;
_str &#61; new char[strlen(str._str) &#43; 1];
strcpy(_str, str._str);
}
return *this;
}
~String()
{
if (_str)
{
delete[]_str;
cout << "~String()" << endl;
}
}
private:
char *_str;
};
String Getstring()
{
String ret("123");
return ret;
}
void test()
{
cout << "使用右值&#xff0c;移动构造" << endl;
String str &#61; Getstring();
cout << endl;
cout << "使用左值&#xff0c;正常构造" << endl;
String str2(str);
cout << endl;
cout << "使用左值&#xff0c;正常赋值" << endl;
str &#61; str2;
cout << endl;
cout << "使用右值&#xff0c;移动赋值" << endl;
str &#61; "123";
cout << endl;
}
int main()
{
test();
system("pause");
return 0;
}
6.4总结
6.5右值引用引用左值
6.5.1使用方法介绍
在某些场景之下&#xff0c;需要用右值去引用去引用左值实现移动语义。当需要一个右值引用引用一个左值时&#xff0c;可以通过move函数将左值转化为右值。C&#43;&#43;11中&#xff0c;std::move()函数位于头文件之中&#xff0c;它并不搬移任何东西&#xff0c;唯一的功能就是将一个左值强制转化为右值引用&#xff0c;然后实现移动语义
6.5.2使用场景分析
自定义类型String和上述一致
场景1&#xff1a;
class Person
{
public:
Person(const String &name)
:_name(name)
{}
Person(const Person& pl)
:_name(pl._name)
{
cout << "Person(const Person& pl)" << endl;
}
private:
String _name;
};
场景2:对场景1的拷贝构造进行优化&#xff0c;使用右值引用:
class Person
{
public:
Person(const String &name)
:_name(name)
{}
Person(const Person& pl)
:_name(pl._name)
{
cout << "Person(const Person& pl)" << endl;
}
Person(const Person&& pl)
:_name(pl._name)
{
cout << "Person(const Person&& pl)" << endl;
}
private:
String _name;
};
场景3:使用进一步的优化
class Person
{
public:
Person(const String &name)
:_name(name)
{}
Person(const Person& pl)
:_name(pl._name)
{
cout << "Person(const Person& pl)" << endl;
}
Person(Person&& pl)
:_name(move(pl._name))
{
cout << "Person(Person&& pl)" << endl;
}
private:
String _name;
};
移动语义错误用例:
移动语义正确使用场景&#xff1a;需要保证属性被修改的左值后面不会再用到