C++11(新的类功能,可变参数模板,empalce函数)

简介: C++11(新的类功能,可变参数模板,empalce函数)



一、类的新功能

1、默认成员函数

原来C++类中,有6个默认成员函数:

1、 构造函数

2、 析构函数

3、 拷贝构造函数

4、拷贝赋值重载

5、取地址重载

6、const 取地址重载

默认成员函数就是我们不写编译器会生成一个默认的。

C++11 新增了两个:移动构造函数和移动赋值运算符重载。

而在下面的情况中,我们需要自己写移动构造和移动赋值:

1、拷贝对象需要深拷贝时,自己写移动构造和移动赋值,比如:string,vector,list。

2、针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:

* 如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。

* 如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)

* 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

注:一般来说,要写析构函数的类那么就需要自己写移动构造和拷贝构造。

2、类成员变量初始化

C++11支持非静态成员变量在声明时进行初始化赋值,但是要注意这里不是初始化,这里是给声明的成员变量缺省值。

class B
{
public:
  B(int b = 0)
    :_b(b)
  {}
  int _b;
};
class A
{
public:
  void Print()
  {
    cout << a << endl;
    cout << b._b << endl;
  }
private:
  // 非静态成员变量,可以在成员声明时给缺省值。
  int a = 10;
  B b = 20;
};

3、关键字default

强制生成默认函数的关键字。

C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字显示指定移动构造生成。

class Person
{
public:
    Person(const char* name = "", int age = 0)
    :_name(name)
    , _age(age)
    {}
    //拷贝构造函数
    Person(const Person& p)
    :_name(p._name)
    ,_age(p._age)
    {}
    Person(Person&& p) = default;
private:
    string _name;
    int _age;
};

4、关键字delete

禁止生成默认函数的关键字。

如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明补丁,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。

5、final与override关键字

final : 修饰一个类,使它不能够被继承。final既可以修饰类也可以修饰虚函数。修饰虚函数,表示该虚函数不能再被重写。

override : 在继承中,检查子类是否重写了父类的虚函数,没有重写就报错。


二、可变参数模板

C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板,而在C++98/03中,类模版和函数模版中只能含固定数量的模版参数。

下面就是一个基本可变参数的函数模板:

Args是一个模板参数包,args是一个函数形参参数包

声明一个参数包 Args...args,这个参数包中可以包含0到任意个模板参数。

template <class ...Args>
void ShowList(Args... args)
{}

我们在使用时可以传入不同个数的参数,如下:

int main()
{
    string s("hello");
    ShowList();
    ShowList(1);
    ShowList(1, 'A');
    ShowList(1, 'A', s);
    return 0;
}

语法不支持使用args[i]这样方式获取可变参数,那么我们要如何一一取出参数包里的参数呢? 取出方法如下:只能通过展开参数包的方式来获取参数包中的每个参数。

1、递归函数方式展开参数包

// 递归终止函数
void ShowList()
{
    cout << endl;
}
// 展开函数
template<class T, class ...Args>
void ShowList(const T& val, Args... args)
{
    cout<<val<<" ";
    ShowList(args...);
}

2、逗号表达式展开参数包

这种展开参数包的方式,不需要通过递归终止函数。这种就地展开参数包的方式实现的关键是逗号表达式。我们知道逗号表达式会按顺序执行逗号前面的表达式。

template <class T>
void PrintArg(const T& t)
{
    cout << t << " ";
}
//展开函数
template <class ...Args>
void ShowList(Args... args)
{
    int arr[] = { (PrintArg(args), 0)... };
    cout << endl;
}

三、empalce_back函数

我们看vector中的empalce_back 就使用了上面所讲到的参数包。我们来看个关于push_back 和 emplace_back用法的例子:

vector<pair<string, int>> v;
v.push_back(make_pair("sort", 1));
v.emplace_back(make_pair("sort", 1));
v.emplace_back("sort", 1));

push_back 和 emplace_back都可以插入数据,但是后者的第二种插入方式是不是更简便一些呢?两者又有什么区别呢?

首先,push_back 的插入需要先构造一个 pair 的变量,然后再拷贝构造。emplace_back 的插入则是既可以像 push_back那样用,也可以用参数包的形式,依次取出参数,第一次取出的参数去初始化 pair的first,第二次取出的参数去初始化 pair 的 second。所以在这种情况下 emplace_back 的效率比push_back的高。只有模板的可变参数包能够实现 emplace_back 的这种用法。

目录
相关文章
|
4天前
|
编译器 C++
【C++】继续学习 string类 吧
首先不得不说的是由于历史原因,string的接口多达130多个,简直冗杂… 所以学习过程中,我们只需要选取常用的,好用的来进行使用即可(有种垃圾堆里翻美食的感觉)
7 1
|
4天前
|
算法 安全 程序员
【C++】STL学习之旅——初识STL,认识string类
现在我正式开始学习STL,这让我期待好久了,一想到不用手撕链表,手搓堆栈,心里非常爽
10 0
|
4天前
|
存储 安全 测试技术
【C++】string学习 — 手搓string类项目
C++ 的 string 类是 C++ 标准库中提供的一个用于处理字符串的类。它在 C++ 的历史中扮演了重要的角色,为字符串处理提供了更加方便、高效的方法。
15 0
【C++】string学习 — 手搓string类项目
|
4天前
|
自然语言处理 编译器 C语言
【C++】C++ 入门 — 命名空间,输入输出,函数新特性
本文章是我对C++学习的开始,很荣幸与大家一同进步。 首先我先介绍一下C++,C++是上个世纪为了解决软件危机所创立 的一项面向对象的编程语言(OOP思想)。
30 1
【C++】C++ 入门 — 命名空间,输入输出,函数新特性
|
4天前
|
存储 算法 编译器
C++的模板与泛型编程探秘
C++的模板与泛型编程探秘
9 0
|
5天前
|
编译器 C++
【C++从练气到飞升】08---模板
【C++从练气到飞升】08---模板
|
5天前
|
设计模式 安全 算法
【C++入门到精通】特殊类的设计 | 单例模式 [ C++入门 ]
【C++入门到精通】特殊类的设计 | 单例模式 [ C++入门 ]
16 0
|
7天前
|
C语言 C++
【C++】string类(常用接口)
【C++】string类(常用接口)
19 1