C++11 -- 类的新功能

news2025/2/24 4:26:32

文章目录

  • 类的新功能
    • 默认成员函数
    • 类成员变量初始化
    • 强制生成默认函数的关键字default
    • 禁止生成默认函数的关键字delete
    • 继承和多态中的final和override关键字

类的新功能

默认成员函数

原来在C++类中,有6个默认成员函数:
1: 构造函数
2: 拷贝构造函数
3: 拷贝赋值重载
4: 析构函数
5: 取地址重载
6: const取地址重载

最后重要的是前4个,后两个用处不大,默认成员函数就是我们不写编译器会生成一个默认的,但是C++11中新增了两个默认成员函数:移动构造函数和移动赋值运算符重载.

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

( 1 ) : 如果我们没有实现移动构造函数,且没有实现析构函数,
拷贝构造,拷贝赋值重载中的任意一个(意味着没有实现深拷贝).那么编译器会主动生动生一个默认移动构造.默认生成的移动构造函数:

  • 对于内置类型成员会执行逐成员按字节拷贝,
  • 对于自定义类型成员,如果该成员实现了移动构造就主动调用移动构造.

( 2 ): 如果我们实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值:

  • 对于内置类型成员会执行逐成员按字节拷贝.
  • 对于自定义类型成员,则需要看这个成员是否实现移动赋值如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似).

( 3 ): 如果我们提供了移动构造和移动赋值,编译器就不会主动提供拷贝构造和拷贝赋值.

如果我们需要对上述结论进行验证,我们则需要使用到以前模拟实现的string类.

namespace yzh
{
	class string
	{
	public:
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		//构造函数
		string(const char* str = "")
			:_size(strlen(str))
			, _capacity(_size)
		{
			//cout << "string(char* str)" << endl;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		//构造函数(现代写法)
		void swap(string& s)
		{
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}
		// 拷贝构造
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 深拷贝" << endl;
			string tmp(s._str);
			swap(tmp);
		}
		// 赋值重载
		string& operator=(const string& s)
		{
			cout << "string& operator=(string s) -- 深拷贝" << endl;
			string tmp(s);
			swap(tmp);
			return *this;
		}
		// 移动构造
		string(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移动语义" << endl;
			swap(s);
		}
		// 移动赋值
		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s) -- 移动语义" << endl;
			swap(s);
			return *this;
		}
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}
		void push_back(char ch)
		{
			if (_size >= _capacity)
			{
				size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newcapacity);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}
		//string operator+=(char ch)
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		const char* c_str() const
		{
			return _str;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity; // 不包含最后做标识的\0
	};
}

此外,我们还需要一个简单的Person类,其中Person类中包含着一个自定义成员Date;

class Date
{
  public:
	Date()
		:_a(0)
	{
	}
	Date( const Date& de)
		:_a(de._a)
	{
		cout << "Date中的拷贝构造" << endl;
	}
	/*Date(const Date&& de)
		:_a(de._a)
	{
		cout << "Date中的移动构造" << endl;
	}*/

	Date& operator=(const Date& de)
	{
		_a = de._a;
		cout << "Date中的移动赋值" << endl;
		return *this;
	}
private:
	int _a;
};
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:
	cl::string _name; //姓名
	int _age;         //年龄
};

现在,我们对以上结论验证:
当我们屏蔽Person类中的移动构造时,对于内置类型会完成值拷贝,对于自定义类型

类成员变量初始化

由于默认生成的构造函数,会对自定义成员调用其构造函数进行初始化,但并不会对内置成员进行相关处理(一般为随机值),C++11支持非静态成员在声明时进行初始化,默认生成的构造函数会调用缺省值对内置成员初始化.

class Person
{
public:

private:
	yzh::string _name;
	int _age = 1;
	int _Id = 10163;
	yzh::A _aa;
};

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

C++1可以让我们更好的控制要使用的默认构造函数,如果当我们要默认使用某个默认的成员函数,但是因为一些原因导致这个函数没有默认生成.
例如: 当我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用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:
 bit::string _name;
 int _age;
};
int main()
{
 Person s1;
 Person s2 = s1;
 Person s3 = std::move(s1);
 return 0;
}

我们了解,如果当我们显示写了拷贝构造,编译器就不会默认生成移动构造,可是如果我们在移动构造声明处加上关键字default,编译器就可以默认生成了.
在这里插入图片描述

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

如果想要限制某些默认函数的生成,在C++98中,是将该函数设置成private,并且只声明补丁而已,这样他人调用时就会报错,在C++11中更简单了,只需要在函数声明处加上=delete即可,该语法指示编译器不再生成对应函数的默认版本.

class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
	Person(const Person& p)
		:_name(p._name)
		, _age(p._age)
	{}
	//C++11
	Person(Person&& p) = delete;
private:
	//C++98
	//Person(Person&& p)
	//{}
	yzh::string _name;
	int _age;
};
int main()
{
	Person s1;
	Person s2 = s1;               //拷贝构造
	Person s3 = std::move(s1);   //移动赋值
	return 0;
}

继承和多态中的final和override关键字

final修饰基类

如果final修饰基类的话,代表该类无法被继承:

//基类
class Person final  //Person类无法被继承,编译错误.
{
public:
	virtual void Print()
	{
		cout << "hello Person" << endl;
	}
};
//子类
class Student : public Person
{
public:
	virtual void Print() 
	{
		cout << "hello Student" << endl;
	}
};

final修饰虚函数

如果final修饰虚函数,代表该虚函数不能在子类中重写:

//基类
class Person 
{
public:
	virtual void Print() final
	{
		cout << "hello Person" << endl;
	}
};
//子类
class Student : public Person
{
public:
	virtual void Print() //重写,编译报错
	{
		cout << "hello Student" << endl;
	}
};

override修饰虚函数

override用于修饰子类的虚函数,主要用于判断子类中是否重写了该虚函数,如果没有重写,则编译报错,所以为了在项目中方便调试,一般在子类的虚函数中加上override.

//基类
class Person 
{
public:
	virtual void Print() 
	{
		cout << "hello Person" << endl;
	}
};
//子类
class Student : public Person
{
public:
	virtual void Prints() override  
	{
		cout << "hello Student" << endl;
	}
};

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

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

相关文章

mysql 索引有哪几种?主键索引、唯一索引

面试题&#xff1a;mysql索引有哪几种&#xff1f; 答&#xff1a; 索引有两类&#xff0c;一是单列索引&#xff0c;二是组合索引。 单列索引&#xff0c;即一个索引只包含单个列&#xff0c;一个表可以有多个单列索引&#xff0c;但这不是组合索引。组合索引&#xff0c;即一…

SQL查询语言(3) 聚集查询和窗口函数的概念

查询结果排序 排序 规则如下: 1.语句: SELECT A1,A2.... FROM 表名 WHERE 选择条件 order by 属性1(ASC升序),属性3(DESC降序); 如果没有说明默认是升序排列: 2.对于空值的处理 如果是升序排列 NULL放在最后一行&#xff0c;如果是降序则放在第一行 上图为 按升序排列查询…

MyBatis动态SQL,基本语法加实战,一篇搞懂

问题&#xff1a; 有的时候我们需要实现批量删除&#xff1a;delete from t_car where id in(1,2,3,4,5,6,…这⾥的值是动态的&#xff0c;根据⽤户选择的 id不同&#xff0c;值是不同的); 多条件查询:有时我们需要根据多个不同地条件来进行查询&#xff0c;比如&#xff1a;se…

数据集成平台之kettle优缺点分析

数据集成平台前言 数据在业务中发挥着重要的作用&#xff0c;但并非所有数据都具有相同的价值和影响力。事实上&#xff0c;大部分数据业务的核心价值主要来自其中的少部分关键数据。这些关键数据可能包含着重要的业务指标、关键客户信息、市场趋势数据等&#xff0c;它们直接…

【机器学习】之Anaconda中使用的命令

操作之前&#xff0c;点击上图入口&#xff0c;进入Prompt。 //示例是在base环境下&#xff0c;cls清屏 (base) C:\Users\bubusa>cls1、base环境下的操作 //&#xff08;1&#xff09;列出所有虚拟环境 (base) C:\Users\bubusa>conda env list # conda environments: #…

Radeon Vii 系统分析 001记——工具

0. 简介 为了对 vega 7nm 有更感性的编程使用体验&#xff0c;故对 vega 7nm做各种测试&#xff1b; 工具&#xff1a; CLRadeonExtender ubuntu ROCm 资料&#xff1a; &#xff08;1&#xff09; 一张安装了 Radeon Vii vega 7nm 的台式机&#xff0c;win10 或 ubunt…

Mit6.006-lecture08-BinaryHeaps

一、优先队列接口 记录一些项目&#xff0c;快速地访问/移除最重要的 例&#xff1a;有限带宽的路由器&#xff0c;必须优先某些信息 例&#xff1a;操作系统内核中的进程调度 例&#xff1a;离散事件模拟&#xff08;下一件事何时发生&#xff09; 例&#xff1a;图算法&am…

js程序运行时在本机与外部app交互

js程序运行时在本机与外部app交互 目录 js程序运行时在本机与外部app交互 一、序言 1.1、问题 1.2、简要回答 二、原理 2.1、插件/web扩展/应用配置/权限 2.2、获取权限-原生应用交换信息的权限nativeMessaging 2.2.1、runtime(运行时的API) 2.3、连接本地应用程序的…

《微服务实战》 第十四章 RabbitMQ应用

前言 一般MQ用于系统解耦、削峰使用,常见于微服务、业务活动等场景。 1、RabbitMQ概念概念 RabbitMQ整体上是一个生产者与消费者模型,主要负责接收、存储和转发消息。 1.1、生产者和消费者 Producer:生产者,就是投递消息的一方。消息一般可以包含2个部分:消息体和标签…

Vue3+i18n多语言动态国际化设置步骤

1、技术介绍 i18n&#xff1a;Vue.js 的国际化插件。它可以轻松地将一些本地化功能集成到你的 Vue.js 应用程序中 i18n的官网地址安装 | Vue I18n (kazupon.github.io) 2、插件安装 npm install vue-i18n9 --save需要注意的是vue3最好使用9.x以上的版本&#xff01; 3、创建i…

夏日挂脖风扇方案开发设计

夏日挂脖风扇是一种便携式的风扇设备&#xff0c;通过挂在用户的脖子上&#xff0c;为用户提供清凉的风力降温。在夏季高温天气中&#xff0c;挂脖风扇成为了人们追逐的热门产品之一。为了满足市场需求&#xff0c;夏日挂脖风扇的方案开发设计需要考虑多个方面&#xff0c;包括…

Class 09 - Data Frame和查看数据

Class 09 - Data Frame和查看数据 DataFrametibbleshead()str()colnames()mutate()创建 Dataframe DataFrame 在我们开始做数据清洗或者检查数据是否存在偏差之前&#xff0c;我们需要先将我们的数据转换成合适的格式方便我们做后续的处理。 这就要说到DataFrame了。因为他很…

CSDN programmer_ada what the hell

CSDN programmer_ada 1、今天博客收到了1条评论&#xff0c;莫名其妙。2、查看这个账户 原来是CSDN官方机器人3、貌似领了红包 就会自动关注发红包的账户 1、今天博客收到了1条评论&#xff0c;莫名其妙。 一定要坚持创作更多高质量博客哦, 小小红包, 以资鼓励, 更多创作活动请…

【ClickHouse】什么是ClickHouse?CK入门

文章目录 一、ClickHouse入门1、列式存储2、DBMS的功能3、多样化引擎4、高吞吐写入能力5、数据分区与线程级并行6、性能对比7、官网 二、ClickHouse安装1、准备工作2、单机安装 三、ClickHouse的数据类型1、整型2、浮点型3、布尔型4、Decimal型6、枚举类型7、时间类型8、数组 一…

好程序员:女生学Java好学吗?女生学Java有什么优势?

小源经常会听到女生咨询适不适合学习Java开发的问题&#xff0c;提出这种问题归根结底还是缺乏性别自信&#xff0c;默认女性比男性弱。实际上这个问题并不存在&#xff0c;男女平等才是正确的思维&#xff0c;当然&#xff0c;也为了解开女生们的心结&#xff0c;这里好程序员…

开发微信公众号本地调试+-+cpolar内网穿透

文章目录 前言1. 配置本地服务器2. 内网穿透2.1 下载安装cpolar内网穿透2.2 创建隧道 3. 测试公网访问4. 固定域名4.1 保留一个二级子域名4.2 配置二级子域名 5. 使用固定二级子域名进行微信开发 转载自cpolar内网穿透的文章&#xff1a;微信公众号开发&#xff1a;对接本地开发…

股票?看我用python采集数据制作成交量图表

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 开发环境 & 第三方模块: 解释器版本: python 3.8 代码编辑器: pycharm 2021.2 requests: pip install requests 爬虫 pyecharts: pip install pyecharts 数据分析 pandas: pip install pandas 数据分析 基本流…

C++常用的支持中文的GUI库Qt 6之一:下载、安装与简单使用

C常用的支持中文的GUI库Qt 6之一&#xff1a;下载、安装与简单使用 因为Qt发展变化较快&#xff0c;网上许多介绍Qt的下载、安装与使用已过时&#xff0c;初学者常因行不通而受挫&#xff0c;故此发布本文&#xff0c;以Qt 6.2.4开源版在Windows 10安装与使用为例介绍。 C好用…

Kubernetes 之7大CNI 网络插件用法和对比

Kubernetes 它需要网络插件来提供集群内部和集群外部的网络通信。以下是一些常用的 k8s 网络插件&#xff1a; Flannel&#xff1a;Flannel 是最常用的 k8s 网络插件之一&#xff0c;它使用了虚拟网络技术来实现容器之间的通信&#xff0c;支持多种网络后端&#xff0c;如 VXLA…

SpringSecurity权限管理基本概念和整体架构介绍

文章目录 一、权限管理1、认证2、授权3、对权限控制&#xff0c;现有的解决方案 二、SpringSecurity简介1、官方定义2、历史 三、整体架构1、认证AuthenticationManagerAuthenticationSecurityContextHolder 2、授权AccessDecisionManagerAccessDecisionVoterConfigAttribute 一…