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 的这种用法。

目录
相关文章
|
2月前
|
缓存 算法 程序员
C++STL底层原理:探秘标准模板库的内部机制
🌟蒋星熠Jaxonic带你深入STL底层:从容器内存管理到红黑树、哈希表,剖析迭代器、算法与分配器核心机制,揭秘C++标准库的高效设计哲学与性能优化实践。
C++STL底层原理:探秘标准模板库的内部机制
|
6月前
|
存储 算法 安全
c++模板进阶操作——非类型模板参数、模板的特化以及模板的分离编译
在 C++ 中,仿函数(Functor)是指重载了函数调用运算符()的对象。仿函数可以像普通函数一样被调用,但它们实际上是对象,可以携带状态并具有更多功能。与普通函数相比,仿函数具有更强的灵活性和可扩展性。仿函数通常通过定义一个包含operator()的类来实现。public:// 重载函数调用运算符Add add;// 创建 Add 类的对象// 使用仿函数return 0;
241 0
|
6月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
191 0
|
6月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
282 0
|
8月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
326 12
|
9月前
|
编译器 C++
模板(C++)
本内容主要讲解了C++中的函数模板与类模板。函数模板是一个与类型无关的函数家族,使用时根据实参类型生成特定版本,其定义可用`typename`或`class`作为关键字。函数模板实例化分为隐式和显式,前者由编译器推导类型,后者手动指定类型。同时,非模板函数优先于同名模板函数调用,且模板函数不支持自动类型转换。类模板则通过在类名后加`&lt;&gt;`指定类型实例化,生成具体类。最后,语录鼓励大家继续努力,技术不断进步!
|
9月前
|
编译器 C++
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。
|
9月前
|
编译器 C++
类和对象(下)C++
本内容主要讲解C++中的初始化列表、类型转换、静态成员、友元、内部类、匿名对象及对象拷贝时的编译器优化。初始化列表用于成员变量定义初始化,尤其对引用、const及无默认构造函数的类类型变量至关重要。类型转换中,`explicit`可禁用隐式转换。静态成员属类而非对象,受访问限定符约束。内部类是独立类,可增强封装性。匿名对象生命周期短,常用于临时场景。编译器会优化对象拷贝以提高效率。最后,鼓励大家通过重复练习提升技能!
|
10月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
9月前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
190 16