C++11新增内容详解(二)

一、新增了两个默认成员函数:

1.1新增的默认成员函数名

移动构造函数移动赋值运算符重载函数(即移动拷贝

1.2移动构造函数默认成成条件与效果

默认生成条件

如果没有自己实现移动构造函数,且没有实现析构,拷贝构造,赋值重载中的任意一个,编译器会自动生成一个默认移动构造函数

效果

 内置类型会挨个进行逐字节拷贝

自定义类型会看本身有无移动构造,有就调用,没有就调拷贝构造

1.3移动拷贝函数默认成成条件与效果

(与移动构造条件和效果基本相同) 

默认生成条件

如果没有自己实现移动拷贝函数,且没有实现析构,拷贝构造,赋值重载中的任意一个,编译器会自动生成一个默认移动拷贝函数

效果

 内置类型会挨个进行逐字节拷贝

自定义类型会看本身有无移动构造,有就调用,没有就调拷贝构造

1.4他们的默认生成条件都比较苛刻,这是为什么

只有在本身,析构,拷贝构造,赋值重载都没有被显式实现他们才会自动生成,

这是因为:

移动构造,移动拷贝,析构,拷贝构造,赋值重载五个是一体的,他们都在有资源管理或是需要释放的时候才需要显式实现,所以要么都自己实现,要么都默认生成

1.4补:他们也会反向影响三个函数

如果显式实现移动构造,移动拷贝二者或二者之一的话,析构,拷贝构造,赋值重载都不会再默认生成了

1.5默认生成这两个函数服务于什么

对于Date日期类这种,成员变量全是内置类型的自定义类型作用不大,与拷贝构造功能完全一致;

对于如Student学生类这种,成员变量包含name这种string自定义类型,且自定义类型实现了移动拷贝/构造的类,在遇到Student类为右值,其中name自定义类型对象也为右值时,调用对应的移动拷贝/构造来提高效率

二、C++11开始,在定义类的时候才开始直接给成员变量缺省值

例如:

struct Student
{
    string name="zhangsan";
    int age=10;
    int score=90;
};

这一类直接定义给缺省值的方式,是在C++11才开始支持的 

三、default和delete强制生成和强制删除默认成员函数

3.1deault的一般用途

在遇到需要编译器虽然不满足默认生成条件,但还是需要强制生成默认成员函数时使用

如:

Student(Student&& s)=default;//默认生成移动构造
Student(const Student& s)=default;//默认生成拷贝构造

注:如移动构造,移动拷贝,析构,拷贝构造,赋值重载这种绑定在一起的函数,可能需要集体进行强制生成

3.2delete的一般用途

不希望类被拷贝的时候使用(如io流中的ostream类就因为缓冲区的问题,不希望被外界拷贝)

如:

Student(Student&& s)=delete;//将该函数划归为不可被调用

3.3在C++11之前也有类似delete需求的场景

delete主要解决把拷贝构造和构造等函数划归为不可用,这样的场景并不少,在C++11之前主要是通过只声明不实现来模拟已经显式实现的场景,再设置权限为private防止被调用

四、final与override

final表示类不可被继承或函数不能被重写

override检查派生类的虚函数是否重写了基类某个虚函数,未重写则报错

 具体在链接文章1.8小节提到过

链接:C++中的多态_c++多态-优快云博客

五、可变模板参数

5.1可变模板参数的基本使用格式

template<class ...Args>
void Show(Args... agrs)
{//...}

此处的Args是一个模板参数包

 args是一个函数形参参数包

参数包中可包含0到任意个模板参数(即涵盖多个类型与多个参数)

5.1补:为什么用Args和args,其他的可以吗

 args本意为arguments,是在C语言中使用过的,意思为参数

Args和args也仅仅是名字,换成T和t也是一样的,使用起来只是一种习惯

5.2可以通过sizeof的特殊用法来获取可变模板参数的参数个数

例如:

template<class ...Args>
void Show(Args... args)
{
    cout<<sizeof...(args)<<endl;
}

尤其注意sizeof使用时...的位置, 这是sizeof独有的

调用的时候:

Show(1,2.2,"abc");//打印值为3
Show(1,"abc");//打印值为2

可以获取可变参数列表中的参数个数

5.3获取参数内容方法一:通过递归展开可变模板参数

5.3.1区分C语言中printf的推导

在C语言中,printf的声明是这样的:

int printf ( const char * format, ... );

这意味着它是使用了一个数组去存类型,用余下的...来接收参数列表

因为是数组,所以可以进行“运行时推导” 即无需在编译时做过多工作,而是在函数运行过程中通过如for循环+下标访问等方式依次提取各个参数

5.3.2编译时推导原理与例子

与C语言不同,因为函数模板的特殊性,必须要进行“编译时推导”,即在编译时就明确传入的参数列表

而获取的方法一就是:通过递归进行推导来获取参数内容

例如:

调用时如:

Type_print(1, 2.2, "abcd", 3.3);

 效果:

⭐5.3.3可变模板参数最本质的用途

可变模板参数是为了让程序员更少地书写模板而产生的

例如5.3.2中的例子,其实编译器在接收到调用参数列表时,会生成这样一个函数:

void Type_print(int i1,double d2,string s3,double d4)
{
	//...
}

 在第一个参数被获取以后,生成

void Type_print(double d2,string s3,double s4)
{
	//...
}

以此类推

但如果通过书写函数模板来完成这一项工作呢?

答案很明显,需要去写四个不同的函数模板,大大增加工作量

5.4获取参数内容方法二:通过数组直接展开

5.4.1结合例子说明展开原理

假如调用式子为

Show3(1, 2.2, "abcd", 3.3);

定义则是: 

本质上为通过在数组中展开,依次调用每一个类模板函数args_print 

5.4.2该展开方式并不通用,可以进进一步改善

int类型返回值直接return 0是为了迎合数组arr中的int,但有时返回值因为一些缘故不可以为int,此时可以

5.5可变模板参数实例

list的接口中存在一个emplace_back,它就是可变模板参数的实用举例

5.5.1模板声明

template <class... Args>
  void emplace_back (Args&&... args);

5.5.2作用与功能介绍

其功能与push_back类似,但可以在模板参数为 多参数构造自定义类型的参数列表 时用参数包直接构造,且在具有调用移动构造条件的情况下调用移动构造

将原来构造+拷贝构造的消耗削减为直接构造

作用举例:

一般情况下push_back可以的,他都可以:

list<pair<string,int>> lt1;
list<string> lt2;

pair<string,int> kv("张三",3);
lt1.emplace_back(kv);//本质上是调用pair的构造后调用拷贝构造,再连接到list节点上
lt1.emplace_back(move(kv));//本质上是调用pair的构造后调用移动拷贝,再连接到list节点上

string s1("abcd");
lt2.emplace_back(s1);

 push_back做不到的,它也可以做到:

lt1.emplace_back("李四",4);//本质上是把构造pair的参数包继续向下传,直接在节点上调用构造
lt2.emplace_back("abcdef");

5.5.3模拟实现中的问题

emplace_back的实现原理与push_back类似,只是因为要使用参数包所以需要做出一些改变

①需要实现函数模板,模板参数包含class ...Args

②通过insert复用,需要额外实现一个含有参数包的insert函数模板

③insert中申请节点需要传入参数包,需要额外实现一个构造函数模板

④向下传的过程中,需要利用forward保持右值属性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值