从C++开始的编程生活(25)——C++11标准Ⅱ

前言

本系列文章承接C++基础的学习,需要有**++C语言的基础++** 才能学会哦~

第25篇主要讲的是有关于C++的++C++11标准的第二部分++ 。
C++才起步,都很简单!

可变参数模板

支持可变数量参数的函数模板和类模板,可变数目的参数称为参数包。

参数包分为:模板参数包 ,表示0或多个模板参数;函数参数包,表示0或多个函数参数。

cpp 复制代码
//              模板参数包              函数参数包
template<class ...Args> void Func(Args... args) { }
template<class ...Args> void Func(Args&... args) { } //传左值引用
template<class ...Args> void Func(Args&&... args) { } //传万能引用

代码示例:

cpp 复制代码
template<class ...Args>
void Print(Args&&... args)
{
    cout << sizeof...(args) << endl;//计算可变参数包中参数的个数
}
int main()
{
    double x = 2.2;
    Print();//包中无参数
    Print(1);//包中有1个参数
    Print(1, string("xxxxx"));//包中有2个参数
    Print(1.1, string("xxxx"), x);//包中有3个参数

    return 0;
}

有可变参数的函数模板,**不仅可以传任意类型的参数,而且可以传任意数量的参数。**不仅再类型上有泛型,还有了数量的变化,使得代码更加灵活。

包扩展

扩展一个包,就是将他分解为构成他的元素。

代码示例:

cpp 复制代码
//编译时递归推导的包扩展
void ShowList()
{
    //编译器时递归的终止条件,参数包是0个时,直接匹配这个函数
    cout << endl;
}

template<class T, class ...Args>
void ShowList(T x, Args... args)
{
    //结束条件不可以是运行时判断逻辑,会报错
    //if(sizeof...(args)==0)
    //    return;

    cout << x << " ";
    //参数包第一个传给了x,剩下的N-1个给第二个参数包args
    ShowList(args...);
}

//编译时递归推演解析参数
template<class ...Args>
void Print(Args... args)
{
    showList(args...);
}

在编译时的推演步骤可如下理解:

根据参数包一步步递归解析出参数。

这里只是讲述其中一个扩展原理,在实际应用中,我们一般不这样使用包扩展。

也可以这样不递归扩展

cpp 复制代码
#include <iostream>
using namespace std;

template<typename... Args>
void print(Args... args) 
{
    // 逗号表达式 + 初始化列表,强制展开参数包
    initializer_list<int>
    {
        (cout << args << " ", 0)...  // ... 展开整个括号
    };
    cout << endl;
}

int main() {
    print(1, 3.14, "test");
    return 0;
}

在编译时,逗号表达式会展开为三段代码:

cpp 复制代码
(cout << 1 << " ", 0),
(cout << 3.14 << " ", 0),
(cout << "test" << " ", 0)

完美转发 + 可变参数

配合 C++11 完美转发 std::forward ,可以无损传递任意参数,这是工厂模式、封装函数的核心用法。

cpp 复制代码
#include <iostream>
#include <utility>  // forward
using namespace std;

template<typename T, typename... Args>
T* createObject(Args&&... args) {
    // 完美转发:保持参数的左值/右值属性
    return new T(std::forward<Args>(args)...);
}

// 测试类
class Test {
public:
    Test(int a, double b) {
        cout << a << " " << b << endl;
    }
};

int main() {
    Test* t = createObject<Test>(10, 20.5);
    delete t;
    return 0;
}

可以避免参数包传递时丢失左值或者右值属性导致的增效(如预想是直接构造,但是运行后是拷贝构造)失败。

... 符号

...在参数包前面,++表声明++;

...在参数包后面,++表扩展++。

cpp 复制代码
template<class ...Args>//声明为类型,...放在类型Args前面
void Print(Args... args)//声明为参数,...放在参数args前面
{
    ShowList(args...)//扩展参数包,...放在args后面
}

注意事项

①参数包可以为空。
②一个模板只能有一个参数包。
③参数包要放在参数列表的最后。

新的类功能

默认的移动构造和移动赋值

原本C++类中有6个默认成员函数,构造析构拷贝构造拷贝复制重载、取地址重载和const取地址重载,前四个比较重要。

C++11新增了两个默认成员函数,移动构造函数和移动复制函数。

当你没有自行实现移动构造以及没有实现析构、拷贝、拷贝赋值重载中的其中一个,那么编译器会自动生成一个默认移动构造。

默认生成的移动构造函数,

对内置类型成员会执行逐成员按字节拷贝;

对自定义类型成员,则需要看这个成员,是否实现了移动构造,若实现了就调用移动构造,反之就会调用拷贝构造。
当你没有自行实现移动赋值重载函数,且没有实现析构、拷贝、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。

默认生成的移动赋值重载函数,

对内置类型成员会执行逐成员按字节拷贝;

对自定义类型成员,则需要看这个成员,是否实现了移动赋值,若实现了就调用移动赋值,反之就会调用拷贝赋值(同上类似)。

当你提供了移动构造和移动赋值,编译器就不会自动提供拷贝构造和拷贝赋值。

成员变量声明时给缺省值

成员变量声明时给的缺省值是初始化列表要用的,如果初始化列表中没有显示初始化,那么就会用这个缺省值初始化,这个在前面的文章讲过,欢迎到我的主页观看我的文章~

default和delete

default

用于控制想要使用的默认函数。

如上,若我们提供了拷贝构造,就不会自动生成移动构造了,那么我们就可以用default,显式指定移动构造生成。

cpp 复制代码
#include <string>
using namespace std;

class Person {
    string name;
    int age;

public:
    Person(string n, int a) : name(n), age(a) {}

    //手动实现了拷贝构造
    Person(const Person& other)
        : name(other.name), age(other.age)
    {
        //...
    }
    //此时编译器不再生成移动构造/移动赋值
    
    //显式写 = default ,恢复想要编译器自动生成的函数
    Person(Person&&) = default;            // 要移动构造
    Person& operator=(Person&&) = default;  // 要移动赋值
};

此时,就算手动实现了拷贝构造,编译器还是会自动生成移动构造和移动赋值。

delete

用于禁止编译器自动生成不需要的默认函数。

cpp 复制代码
class NoCopy {
public:
    NoCopy() = default;

    // 禁用拷贝构造,不再会默认生成
    NoCopy(const NoCopy&) = delete;

    // 禁用赋值运算符,不再会默认生成
    NoCopy& operator=(const NoCopy&) = delete;
};

int main() {
    NoCopy a;
    NoCopy b = a;  // 报错!被 delete 了
    NoCopy c;
    c = a;         // 报错!被 delete 了
}

此时,NoCopy类因为被禁止自动生成拷贝构造,所以无法进行拷贝,强行拷贝会报错。

final与override

在继承和多态的文章中有讲到,欢迎到我的主页看相关文章~

STL的变化

①新增了unordered_map与unordered_set。之前的文章也就讲过~

②增加了新的接口,如push/insert/emplace的右值引用和移动语义的接口,还有初始化列表的构造

③新增了范围for

包装器

function

是一个类模板,也是一个包装器,可用于包装存储其他的可调用对象,如函数指针、仿函数、lambda、类成员函数(要配合bind)。

语法格式:

cpp 复制代码
function<[返回类型](参数类型,参数类型,···) [包装变量名] = [包装对象的标识符]

function<int(int, int)> f1 = f;

包装普通函数:

cpp 复制代码
int add(int a, int b) { return a + b; }

function<int(int, int)> f = add;
cout << f(1, 2) << endl;

包装lambda表达式:

cpp 复制代码
function<int(int, int)> f = [](int a, int b) {
    return a * b;
};
cout << f(2, 3) << endl; 

包装仿函数:

cpp 复制代码
struct Sub {
    int operator()(int a, int b) {
        return a - b;
    }
};

function<int(int, int)> f = Sub();
cout << f(5, 3) << endl;

包装成员函数:

cpp 复制代码
struct Calc {
    int add(int a, int b) { return a + b; }
};

Calc obj;
function<int(int, int)> f = bind(&Calc::add, &obj, placeholders::_1, placeholders::_2);
cout << f(10, 20) << endl; 

bind的作用,是把对象和它的成员函数绑定成普通函数。这里是把Calc类的add成员函数和Calc类对象obj绑定,并用两个占位符表示参数数量和位置。

bind

也是一个包装器,它的作用是绑定参数

语法格式:

cpp 复制代码
bind([要绑定的函数], [参数1], [参数2], [占位符...]);

其中占位符是

cpp 复制代码
placeholder::_1
placeholder::_2
placeholder::_3

//可用using简写
using placeholder;

_1
_2
_3

用例1:

固定部分函数参数

cpp 复制代码
#include <functional>
using namespace std;
using namespace placeholders;

int add(int a, int b) {
    return a + b;
}

int main()
{
    add(2, 3);//相当于f(3)

    //绑定之后add的第一个参数绑死为 2不变
    auto f = bind(add, 2, _1);
    //因为第一个参数已经固定为 2,只需传第二个参数即可
    f(3);
    return 0;
}

用例2:

调换参数顺序

cpp 复制代码
//占位符是有顺序的
auto f = bind(add, _2, _1);

f(2, 3);

//实际上执行
add(3, 2);

❤~~本文完结!!感谢观看!!接下来更精彩!!欢迎来我博客做客~~❤

相关推荐
cjforever142 小时前
各STL容器的模拟实现
开发语言·数据结构·c++
Genios2 小时前
今天是我正式开启Python学习之旅的第7天
开发语言·python·学习
怪我冷i2 小时前
在win11进行Rust Web 开发,采用Salvo框架
开发语言·前端·rust
吴声子夜歌2 小时前
Node.js——os操作系统模块
开发语言·node.js·php
曹牧2 小时前
Java:驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立连接
java·开发语言·ssl
郝学胜-神的一滴2 小时前
Linux高性能网络编程基石:epoll核心与文件描述符限制全解
linux·服务器·网络·c++·后端
cch89182 小时前
PHP vs C++:10倍性能差距的编程语言对决
android·java·开发语言
司马万2 小时前
RUST基础1----数据类型
开发语言·算法·rust
cnnews2 小时前
Termux中安装python包
android·linux·开发语言·python·安卓·termux