【C++入门到精通】新的类功能 | 可变参数模板 C++11 [ C++入门 ]

在这里插入图片描述

阅读导航

  • 引言
  • 一、新的类功能
    • 1. 默认成员函数
    • 2. 类成员变量初始化
    • 3. 强制生成默认函数的关键字default
    • 4. 禁止生成默认函数的关键字delete
    • 5. override 和 final
      • (1)override
      • (2)final
  • 二、可变参数模板
    • 递归函数方式展开参数包
    • 逗号表达式展开参数包
  • 温馨提示

引言

随着C++11标准的发布,引入了许多令人振奋的新特性,其中包括强大的类功能和可变参数模板。这些新增的功能为C++编程带来了更加灵活和高效的可能性,极大地丰富了语言的表达能力和应用范围。本文将重点探讨C++11中这些新特性的优势和用法,帮助读者更好地理解和运用现代C++编程的最新技术。😍

一、新的类功能

1. 默认成员函数

在C++11标准中,引入了两个重要的默认成员函数:移动构造函数和移动赋值运算符重载。这两个功能的引入极大地提升了C++语言的性能和效率。

⭕移动构造函数

移动构造函数允许对象通过移动资源而不是复制资源来进行构造。在传统的复制构造函数中,对象的构造是通过逐个复制成员变量来完成的,这可能导致资源的不必要拷贝和分配,从而降低程序的性能。而移动构造函数则允许对象直接获取资源的所有权,而无需进行复制,从而提高了程序的效率。移动构造函数通过使用右值引用来实现,可以显著减少资源的拷贝和内存分配,特别适用于管理大量数据的类对象。

⭕移动赋值运算符重载

移动赋值运算符重载与移动构造函数类似,它也通过移动资源而不是复制资源来实现对象之间的赋值操作。传统的赋值运算符重载会对已有的资源进行释放和重新分配,这样的操作可能会消耗大量的时间和系统资源。而移动赋值运算符重载则通过将资源的所有权转移给目标对象,避免了资源的拷贝和分配,提高了程序的性能和效率。

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

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

以下代码在vs2013中不能体现,在vs2019下才能演示体现上面的特性。

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& operator=(const Person& p){if(this != &p){_name = p._name;_age = p._age;}return *this;}*//*~Person(){}*/
private:std::string _name;int _age;
};int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);Person s4;s4 = std::move(s2);return 0;
}

2. 类成员变量初始化

在C++11标准中,新增了一种方便的类成员变量初始化方式,即在类定义中直接对成员变量进行初始化。这种初始化方式使得在定义类的同时就可以为成员变量赋予初始值,而不需要依赖于构造函数来完成初始化操作

具体来说,我们可以在类定义的同时为成员变量提供默认值,例如:

class MyClass {
public:int x = 0;  // 直接对成员变量进行初始化double y = 3.14;std::string name = "C++";
};

在上面的示例中,成员变量x、y和name都在类定义中直接进行了初始化赋值,这样在创建对象时,如果没有显式地指定初始值,那么这些成员变量将会自动以指定的默认值进行初始化。

这种类成员变量的直接初始化方式简化了代码,使得类的定义更加清晰和简洁。同时,它也提供了对类成员变量进行默认值设置的便利途径,使得开发者可以更加方便地管理和维护类的成员变量初始化状态。

3. 强制生成默认函数的关键字default

在C++11标准中,引入了关键字"default",它可以用来显式地指示编译器生成默认的特殊成员函数,例如默认构造函数、析构函数、拷贝构造函数、移动构造函数和赋值操作符等。使用"default"关键字可以方便地告诉编译器去生成这些函数,而不需要手动编写它们的定义。

例如,假设我们有一个类需要生成默认的构造函数和析构函数,我们可以这样使用"default"关键字:

class MyDefaultClass {
public:// 显式指示编译器生成默认构造函数和析构函数MyDefaultClass() = default;~MyDefaultClass() = default;
};

在上面的示例中,我们使用"default"关键字来告诉编译器生成默认的构造函数和析构函数。这样做的好处在于,我们无需手动编写这些默认函数的定义,而是交由编译器自动生成,从而简化了代码并提高了代码的可读性。

"default"关键字的另一个重要应用是在移动构造函数和移动赋值操作符中。我们可以使用"default"关键字来告诉编译器生成默认的移动构造函数和移动赋值操作符,例如下面的代码使用"default"关键字来告诉编译器生成默认的移动构造函数

#include <iostream>
#include <utility>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:std::string _name;int _age;
};int main() {Person s1; // 调用默认构造函数Person s2 = s1; // 调用拷贝构造函数Person s3 = std::move(s1); // 调用移动构造函数return 0;
}

在这段代码中,我们定义了一个名为Person的类,包括默认构造函数、拷贝构造函数和移动构造函数。在main函数中,我们创建了三个Person对象s1、s2和s3,并展示了它们在不同情况下调用构造函数的过程。

4. 禁止生成默认函数的关键字delete

在C++11标准中,可以使用关键字"delete"来显式地删除默认生成的特殊成员函数,例如默认构造函数、拷贝构造函数、移动构造函数和赋值操作符等。通过使用"delete"关键字,可以阻止特定的函数被默认生成或者调用。

下面是一个简单的示例,展示了如何使用"delete"关键字阻止默认构造函数的生成:

class NoDefault {
public:// 删除默认构造函数NoDefault() = delete;
};int main() {NoDefault nd; // 这里会导致编译错误,因为默认构造函数已被删除return 0;
}

在上述示例中,类"NoDefault"的默认构造函数被使用"= delete"语法删除了,因此在main函数中尝试创建"NoDefault"类的实例将会导致编译错误

类似地,你也可以使用"delete"关键字来删除其他默认生成的特殊成员函数,以满足特定的设计需求。这种方式通常用于禁用某些不希望被调用的函数,或者确保特定的行为不发生。

5. override 和 final

overridefinal 都是 C++11 中引入的关键字,用于标识和修饰虚函数的行为。

(1)override

override 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错

示例:

class Base {
public:virtual void myFunction() {// 基类虚函数的默认实现}
};class Derived : public Base {
public:void myFunction() override {// 派生类重写基类虚函数的实现}
};

在上述示例中,派生类 Derived 使用 override 关键字表明它重写了基类 Base 的虚函数 myFunction()。如果在派生类中意外地使用了错误的函数签名(参数列表或返回类型不匹配),编译器会发出错误提示。

(2)final

final 用于标识类、成员函数或虚函数,表示它们被声明为最终版本,禁止在派生类中进一步继承或重写。

示例:

class Base final {
public:virtual void myFunction() {// 基类虚函数的默认实现}
};class Derived : public Base {
public:void myFunction() /* override 不可使用 */ {// 派生类重写基类虚函数的实现}
};

在上述示例中,基类 Base 使用 final 关键字标识它是最终类,不允许被继续派生。同时,派生类 Derived 中的 myFunction() 不能使用 override 关键字进行标识,因为基类已经被声明为最终类

二、可变参数模板

C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板,相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。然而由于可变模版参数比较抽象,使用起来需要一定的技巧,所以这块还是比较晦涩的。现阶段呢,我们掌握一些基础的可变参数模板特性就够我们用了,所以这里我们点到为止,以后大家如果有需要,再可以深入学习。

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

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。

递归函数方式展开参数包

// 递归终止函数
template <class T>
void ShowList(const T& t)
{cout << t << endl;
}// 展开函数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{cout << value <<" ";ShowList(args...);
}
int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}

逗号表达式展开参数包

这种展开参数包的方式,不需要通过递归终止函数,是直接在expand函数体中展开的, printarg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。这种就地展开参数包的方式实现的关键是逗号表达式。我们知道逗号表达式会按顺序执行逗号前面的表达式。

expand函数中的逗号表达式:(printarg(args), 0),也是按照这个执行顺序,先执行printarg(args),再得到逗号表达式的结果0。同时还用到了C++11的另外一个特性——初始化列表,通过初始化列表来初始化一个变长数组, {(printarg(args), 0)…}将会展开成((printarg(arg1),0),(printarg(arg2),0), (printarg(arg3),0), etc… ),最终会创建一个元素值都为0的数组int arr[sizeof…(Args)]。由于是逗号表达式,在创建数组的过程中会先执行逗号表达式前面的部分printarg(args)打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包。

template <class T>
void PrintArg(T t)
{cout << t << " ";
}
//展开函数
template <class ...Args>
void ShowList(Args... args)
{int arr[] = { (PrintArg(args), 0)... };cout << endl;
}
int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}

温馨提示

感谢您对博主文章的关注与支持!另外,我计划在未来的更新中持续探讨与本文相关的内容,会为您带来更多关于C++以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。期待与您建立更紧密的互动,共同探索C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/196051.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【c++随笔13】多态

【c随笔13】多态 多态性&#xff08;Polymorphism&#xff09;在面向对象编程中是一个重要概念&#xff0c;它允许以统一的方式处理不同类型的对象&#xff0c;并在运行时动态确定实际执行的方法或函数。一、什么是多态性&#xff1f;1、关键概念&#xff1a;C的多态性2、多态定…

3-docker安装centos7

CentOS7.9下安装完成docker后&#xff0c;后续我们可以在其上安装centos7系统。具体操作如下&#xff1a; 1.以root用户登录CentOS7.9服务器&#xff0c;拉取centos7 images 命令&#xff1a; docker pull centos:centos7 2.加载centos7 images并登录验证 命令&#xff1a;…

一种用于脑肿瘤和组织分割的具有体积特征对齐的三维跨模态特征交互网络

A 3D Cross-Modality Feature Interaction Network With Volumetric Feature Alignment for Brain Tumor and Tissue Segmentation 一种用于脑肿瘤和组织分割的具有体积特征对齐的三维跨模态特征交互网络背景贡献实验方法Cross-Modality Feature Interaction ModuleVolumetric …

【面试经典150 | 算术平方根】

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;数学表达式方法二&#xff1a;二分法 其他语言python3 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并…

树,二叉树,二叉树遍历,哈夫曼树(详解+刷题)

&#x1f442; 后街男孩经典之精选 - 歌单 - 网易云音乐 &#x1f442; 年轮&#xff08;电视剧《花千骨》男声版插曲&#xff09; - 汪苏泷 - 单曲 - 网易云音乐 目录 &#x1f33c;5.1 -- 树 &#x1f33c;5.2 -- 二叉树 1&#xff0c;性质 2&#xff0c;存储 3&#x…

Figma 插件学习(一)

一.插件介绍 插件在文件中运行&#xff0c;执行一个或多个用户操作&#xff0c;并允许用户自定义其体验或创建更高效的工作流程。 插件通过专用插件API与Figma的编辑器交互。还可以利用外部Web API。 1.插件API 插件API支持读写功能&#xff0c;允许查看、创建和修改文件的…

开源WIFI继电器之硬件电路

一、原理图 源文件 二、原理图说明 1、器件说明 U4&#xff1a;ESP8285模块 U6&#xff1a;触发器 U3&#xff1a;继电器 2、继电器状态检测说明 检测继电器线圈是否通电来判断继电器是否导通&#xff0c;当Q1不导通时&#xff0c;Q1集电极的电压为3.3V&#xff0c;经…

【项目管理】中途接手的项目应对实用指南

导读&#xff1a;作为项目经理中途接手项目往往不可避免&#xff0c;为了保证项目成功需要项目经理额外考虑更多的因素和处理相关问题&#xff0c;也往往带来很大的挑战性。本文提供可应对借鉴的思路&#xff0c;在一定程度上可以作为最佳实践。 目录 1、首先、了解项目项目背…

[AutoSar]导出task mapping 表到excel

目录 关键词平台说明背景实现方法 关键词 嵌入式、C语言、autosar 平台说明 项目ValueOSautosar OSautosar厂商vector芯片厂商TI编程语言C&#xff0c;C编译器HighTec (GCC) 背景 为了做文档输出&#xff0c;要导出task mapping 到excel。 实现方法 1.按住shift&#xf…

Windows核心编程 静态库与动态库

资源文件 .rc 文件 会被 rc.exe 变成 .res 文件(二进制文件) 在链接时链接进入 .exe 文件 一、如何保护源码 程序编译链接过程 不想让别人拿到源代码&#xff0c;但是想让其使用功能&#xff0c;根据上图观察&#xff0c;把自己生成的obj给对方&#xff0c;对方拿到obj后&…

ps找不到msvcp140.dll怎么办?亲测5个有效的修复方法分享

运行Photoshop时提示找不到MSVCP140.dll&#xff0c;这是因为计算机MSVCP140.dll文件丢失或者损坏。msvcp140.dll是微软Visual C 2015运行库的一部分&#xff0c;它包含了许多用于支持C运行的函数和类。当我们在使用某些程序时&#xff0c;如果这个程序依赖于msvcp140.dll&…

Day35力扣打卡

打卡记录 相邻字符不同的最长路径&#xff08;树状DP&#xff09; 链接 若节点也存在父节点的情况下&#xff0c;传入父节点参数&#xff0c;若是遍历到父节点&#xff0c;直接循环里 continue。 class Solution:def longestPath(self, parent: List[int], s: str) -> in…