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

news2024/11/14 20:46:20

在这里插入图片描述

阅读导航

  • 引言
  • 一、新的类功能
    • 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.coloradmin.cn/o/1228428.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C# Winform围棋棋盘

C# Winform简单的围棋棋盘vs2008winform小游戏C#vs2010winform棋盘C#窗体小游戏 这是一个简单的围棋棋盘小游戏&#xff0c;使用C# Winform编写棋盘界面&#xff0c;玩家可以在空白的交叉点上下棋子 项目获取&#xff1a; 项目获取&#xff1a;typora: typora/img (gitee.co…

支付宝沙箱支付

支付宝沙箱支付 支付宝沙箱&#xff08;Alipay Sandbox&#xff09;是支付宝提供的一个模拟环境&#xff0c;用于开发者在不影响真实交易的情况下进行支付宝相关功能的测试和调试。在软件开发中&#xff0c;沙箱环境通常指的是一个隔离的测试环境&#xff0c;可以模拟真实环境…

【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法

文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中&#xff0c;传感器和控制器产生大量周…

Python 自动化(十八)admin后台管理

admin后台管理 什么是admin后台管理 django提供了比较完善的后台数据库的接口&#xff0c;可供开发过程中调用和测试使用 django会搜集所有已注册的模型类&#xff0c;为这些模型类提供数据管理界面&#xff0c;供开发使用 admin配置步骤 创建后台管理账号 该账号为管理后…

2023年中职“网络安全“—Web 渗透测试①

2023年中职"网络安全"—Web 渗透测试① Web 渗透测试任务环境说明&#xff1a;1.访问地址http://靶机IP/task1&#xff0c;分析页面内容&#xff0c;获取flag值&#xff0c;Flag格式为flag{xxx}&#xff1b;2.访问地址http://靶机IP/task2&#xff0c;访问登录页面。…

【每日一题】三个无重叠子数组的最大和

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;滑动窗口 写在最后 Tag 【滑动窗口】【数组】【2023-11-19】 题目来源 689. 三个无重叠子数组的最大和 题目解读 解题思路 方法一&#xff1a;滑动窗口 单个子数组的最大和 我们先来考虑一个长度为 k 的子数组的最…

【开源】基于Vue.js的创意工坊双创管理系统

项目编号&#xff1a; S 049 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S049&#xff0c;文末获取源码。} 项目编号&#xff1a;S049&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 管理员端2.2 Web 端2.3 移动端 三、…

leetcode算法之分治-快排

目录 1.颜色分类2.排序数组3.数组中的第k个最大元素4.最小的k个数 1.颜色分类 颜色分类 class Solution { public:void sortColors(vector<int>& nums) {int n nums.size();int left -1,rightn,i0;while(i<right){if(nums[i] 0) swap(nums[left],nums[i]);e…

力扣 字母异位词分组 哈表 集合

&#x1f468;‍&#x1f3eb; 力扣 字母异位词分组 ⭐ 思路 由于互为字母异位词的两个字符串包含的字母相同&#xff0c;因此对两个字符串分别进行排序之后得到的字符串一定是相同的&#xff0c;故可以将排序之后的字符串作为哈希表的键。 &#x1f351; AC code class Solut…

设计模式-行为型模式-策略模式

一、什么是策略模式 策略模式是一种行为设计模式&#xff0c;它允许在运行时选择算法或行为&#xff0c;并将其封装成独立的对象&#xff0c;使得这些算法或行为可以相互替换&#xff0c;而不影响使用它们的客户端。&#xff08;ChatGPT生成&#xff09; 主要组成部分&#xff…

一款实用的.NET Core加密解密工具类库

前言 在我们日常开发工作中&#xff0c;为了数据安全问题对数据加密、解密是必不可少的。加密方式有很多种如常见的AES&#xff0c;RSA&#xff0c;MD5&#xff0c;SAH1&#xff0c;SAH256&#xff0c;DES等&#xff0c;这时候假如我们有一个封装的对应加密解密工具类可以直接…

【STL】:反向迭代器

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关反向迭代器的模拟实现&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通…

【kafka】使用docker启动kafka

1.环境准备 docker拉取zookeeper镜像 docker pull zookeeper:3.4.14 创建zookeeper容器&#xff0c;默认端口号为2181 docker run -d --name zookeeper -p 2181:2181 zookeeper:3.4.14 拉取kafka镜像 docker pull wurstmeister/kafka:2.12-2.3.1 创键kafka容器&#xff…

c语言-输入输出详解

文章目录 格式化输入输出占位符printfscanf 字符串输入输出puts&#xff08;&#xff09;gets&#xff08;&#xff09; 字符输入输出putchar&#xff08;&#xff09;getchar&#xff08;&#xff09; 区别 格式化输入输出 输入输出的库函数的头文件&#xff1a; #include<…

2023/11/19总结

项目进度&#xff1a; 地址管理&#xff1a; 显示菜品 购物车相关功能 然后最近在看 支付宝沙盒支付的相关功能&#xff0c;打算把支付给做了 。界面做的不是很好看 &#xff0c;但是后续会改成 手机端的。

WSL 2 更改默认安装的 Linux 发行版

目录 什么是 WSL 2&#xff1f;更改默认安装的 Linux 发行版更改发行版的 WSL 版本 什么是 WSL 2&#xff1f; WSL 2 是适用于 Linux 的 Windows 子系统体系结构的一个新版本&#xff0c;它支持适用于 Linux 的 Windows 子系统在 Windows 上运行 ELF64 Linux 二进制文件。 它的…

基于SSM的北海旅游网站设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

ICCV2023 Tracking paper汇总(二)(多目标跟随、单目标跟随等)

十六、Integrating Boxes and Masks: A Multi-Object Framework for Unified Visual Tracking and Segmentation paper&#xff1a; https://openaccess.thecvf.com/content/ICCV2023/papers/Xu_Integrating_Boxes_and_Masks_A_Multi-Object_Framework_for_Unified_Visual_ICC…

Android 13 - Media框架(14)- OpenMax(二)

这一节我们将来解析 media.codec 这个 HIDL service 究竟提供了什么服务&#xff0c;服务是如何启动的。 1、main 函数 我们先来看 frameworks/av/services/mediacodec/main_codecservice.cpp&#xff1a; int main(int argc __unused, char** argv) {strcpy(argv[0], "…

搭建网关服务器实现DHCP自动分配、HTTP服务和免密登录

目录 一. 实验要求 二. 实验准备 三. 实验过程 1. 网关服务器新建网卡并改为仅主机模式 2. 修改新建网卡IP配置文件并重启服务 3. 搭建网关服务器的dhcp服务 4. 修改server2网卡配置文件重启服务并效验 5. 设置主机1的网络连接为仅主机模式 6. 给server2和网关服务器之…