【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++、算法和编程的奥秘。祝您生活愉快,排便顺畅!
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

nodejs+vue慢性胃炎健康管理系统的设计与实现-微信小程序-安卓-python-PHP-计算机毕业设计

随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于慢性胃炎健康管理系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了慢性胃炎健康管理系统&#xff0c; 系统首页、个…

控制您的音乐、视频等媒体内容

跨多个 Chrome 标签页播放音乐或声音 在计算机上打开 Chrome 。在标签页中播放音乐、视频或其他任何有声内容。您可以停留在该标签页上&#xff0c;也可以转到别处。要控制声音&#xff0c;请在右上角点击“媒体控件”图标 。您可暂停播放、转到下一首歌曲/下一个视频&#xf…

python django 小程序图书借阅源码

开发工具&#xff1a; PyCharm&#xff0c;mysql5.7&#xff0c;微信开发者工具 技术说明&#xff1a; python django html 小程序 功能介绍&#xff1a; 用户端&#xff1a; 登录注册&#xff08;含授权登录&#xff09; 首页显示搜索图书&#xff0c;轮播图&#xff0…

时间序列预测实战(十七)利用Prophet实现电力负荷长期预测(附代码+数据集+详细讲解)

一、本文介绍 Prophet是一个由Facebook开发的开源工具&#xff0c;用于时间序列预测。这个工具特别适合于具有强季节性影响和多个历史数据季节的业务时间序列数据。Prophet的主要思想是将数据分解为如下三个部分&#xff1a;趋势、季节性、节假日和特殊事件。这个模型非常适合…

CICD 持续集成与持续交付——jenkins

部署 软件下载&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/jenkins/redhat/ [rootcicd2 ~]# rpm -ivh jdk-11.0.15_linux-x64_bin.rpm[rootcicd2 ~]# yum install -y fontconfig[rootcicd2 ~]# rpm -ivh jenkins-2.432-1.1.noarch.rpm启动服务 [rootcicd2 ~]# systemctl…

【论文阅读】基于隐蔽带宽的汽车控制网络鲁棒认证(二)

文章目录 第三章 识别CAN中的隐藏带宽信道3.1 隐蔽带宽vs.隐藏带宽3.1.1 隐蔽通道3.1.2 隐藏带宽通道 3.2 通道属性3.3 CAN隐藏带宽信道3.3.1 CAN帧ID字段3.3.2 CAN帧数据字段3.3.3 帧错误检测领域3.3.4 时间通道3.3.5 混合通道 3.4 构建信道带宽公式3.5通道矩阵3.6 结论 第四章…

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

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

深入理解注意力机制(上)-起源

一、介绍 近几年自然语言处理有很大的进展&#xff0c;从 2018 年 Google 推出的 BERT&#xff0c;到后来的 GPT、ChatGPT 等&#xff0c;这些模型当时能取得这样的成果&#xff0c;除了庞大的数据量及损害资源外&#xff0c;最重要的是的就是背后的Transformer模型&#xff0c…

C语言——操作符详解

前言&#xff1a;这篇文章主要讲解一下C语言中常见的操作符的使用&#xff0c;做一下整理&#xff0c;便于日后回顾&#xff0c;同时也希望能帮助到大家。 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4dd;CSDN主页 愚润求学 &#x1f304;每日鸡汤&#xff1a;念…

黑马程序员 计算机网络(笔记)

参考文章&#xff1a;【黑马程序员】计算机网络概述~ 程序员必须掌握的入门基础知识&#xff01; 参考文章&#xff1a;黑马程序员Linux运维工作场景解决方案零基础到就业 参考文章&#xff1a;网络安全运维进阶教程&#xff0c;运维工程师深度学习教程 文章目录 linux入门到…

2023.11.17使用flask将多个图片文件上传至服务器

2023.11.17使用flask将多个图片文件上传至服务器 实现功能&#xff1a; 1、同时上传多个图片文件 2、验证文件扩展名 3、显示上传文件的文件名 4、显示文件上传结果 程序结构 main.py from flask import Flask, request, jsonify, render_template import osapp Flask(__n…

Android 10.0 系统修改usb连接电脑mtp和PTP的显示名称

1.前言 在10.0的产品定制化开发中,在usb模块otg连接电脑,调整为mtp文件传输模式的时候,这时可以在电脑看到手机的内部存储 显示在电脑的盘符中,会有一个mtp名称做盘符,所以为了统一这个名称,就需要修改这个名称,接下来分析下处理的 方法来解决这个问题 2.系统修改usb连…

[AutoSar]工程中的cpuload陷阱(三)测试

目录 关键词平台说明背景一、 测试结果对比1.1 不带cache1.2 带cache 二、小结 关键词 嵌入式、C语言、autosar 平台说明 项目ValueOSautosar OSautosar厂商vector芯片厂商TI编程语言C&#xff0c;C编译器HighTec (GCC) 背景 接着工程中的cpuload陷阱&#xff08;二)中的描述…

装饰模式 rust和java的实现

装饰器模式 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其结构。 装饰器模式通过将对象包装在装饰器类中&#xff0c;以便动态地修改其行为。 这种模式创建了一个装饰类&#xff0c;用来包装原有的类&am…

(数据库管理系统)DBMS与(数据库系统)DBS的区别

数据库管理系统&#xff08;DBMS&#xff0c;Database Management System&#xff09;和数据库系统&#xff08;DBS&#xff0c;Database System&#xff09;是两个相关但不同的概念。 DBS是一个更广泛的概念&#xff0c;指的是计算机系统引入数据库后的系统&#xff0c;包括数…

翻译软件Mate Translate mac中文版介绍说明

Mate Translate mac可以帮你翻译超过100种语言的单词和短语&#xff0c;使用文本到语音转换&#xff0c;并浏览历史上已经完成的翻译。你还可以使用Control S在弹出窗口中快速交换语言。 Mate Translate Mac版软件介绍 Mate Translate 可以在你的所有设备之间轻松同步&#x…

微机原理练习题_13

一、单项选择题(本大题共15小题,每小题3分,共45分。在每小题给出的四个备选项中,选出一个正确的答案。&#xff09; 1、十六进制数5BF.C8转换成二进制数是(&#xff09; A. 11011100111111101B B. 010111011011.01101B C. 010110111111.11001B D. 010111011011.11001B 2,最适合…

Ps:变换

可以向选区、整个图层、多个图层或图层蒙版应用变换 Transform&#xff0c;还可以向路径、矢量形状、矢量蒙版、选区边界或 Alpha 通道应用变换。 若要变换栅格&#xff08;像素&#xff09;图像&#xff0c;建议先将其转换为智能对象&#xff0c;以便进行非破坏性的变换。 Ps菜…

Python编程技巧 – 使用字符串(Strings)

Python编程技巧 – 使用字符串&#xff08;Strings) Python Programming Essentials – Using Strings 本文简要介绍如何使用字符串&#xff0c;来进行Python编程。字符串有很多用途&#xff0c;包括输出结果、反馈状态、数据处理以及切片和文本筛选等 1. 字符串 字符串(St…

NSS [鹤城杯 2021]Middle magic

NSS [鹤城杯 2021]Middle magic 源码直接给了。 粗略一看&#xff0c;一共三个关卡 先看第一关&#xff1a; if(isset($_GET[aaa]) && strlen($_GET[aaa]) < 20){$aaa preg_replace(/^(.*)level(.*)$/, ${1}<!-- filtered -->${2}, $_GET[aaa]);if(preg_m…