C++11 -- 类的新功能

2023-10-27

类的新功能

默认成员函数

原来在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类中的自定义成员相关结论验证:
对于Person类,在符合默认生成移动构造的条件下,编译器就会默认调用A类中的移动构造和移动赋值.
在这里插入图片描述
注意:
如果我们在A类中显示写了移动赋值,那么编译器就会默认将A类中的拷贝构造为已删除的函数,所以,为了更加方便,最好在A类中额外实现拷贝构造.

  • 如果传的实参为左值,编译器就会主动调用A类中的拷贝构造.
  • 如果传的实参为右值,编译器就会主动调用A类中的移动构造(移动赋值)

在这里插入图片描述

类成员变量初始化

由于默认生成的构造函数,会对自定义成员调用其构造函数进行初始化,但并不会对内置成员进行相关处理(一般为随机值),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;
	}
};

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++11 -- 类的新功能 的相关文章

  • 在 xaml 中编写嵌套类型时出现设计时错误

    我创建了一个用户控件 它接受枚举类型并将该枚举的值分配给该用户控件中的 ComboBox 控件 很简单 我在数据模板中使用此用户控件 当出现嵌套类型时 问题就来了 我使用这个符号来指定 EnumType x Type myNamespace
  • C# 异步等待澄清?

    我读了here http blog stephencleary com 2012 02 async and await html that 等待检查等待的看看它是否有already完全的 如果 可等待已经完成 那么该方法将继续 运行 同步
  • 类型中的属性名称必须是唯一的

    我正在使用 Entity Framework 5 并且有以下实体 public class User public Int32 Id get set public String Username get set public virtual
  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • C++11 删除重写方法

    Preface 这是一个关于最佳实践的问题 涉及 C 11 中引入的删除运算符的新含义 当应用于覆盖继承父类的虚拟方法的子类时 背景 根据标准 引用的第一个用例是明确禁止调用某些类型的函数 否则转换将是隐式的 例如最新版本第 8 4 3 节
  • 随着时间的推移,添加到 List 变得非常慢

    我正在解析一个大约有 1000 行的 html 表 我从一个字符串中添加 10 个字符串 td 每行到一个list td
  • 如何从本机 C(++) DLL 调用 .NET (C#) 代码?

    我有一个 C app exe 和一个 C my dll my dll NET 项目链接到本机 C DLL mynat dll 外部 C DLL 接口 并且从 C 调用 C DLL 可以正常工作 通过使用 DllImport mynat dl
  • 从经典 ASP 调用 .Net C# DLL 方法

    我正在开发一个经典的 asp 项目 该项目需要将字符串发送到 DLL DLL 会将其序列化并发送到 Zebra 热敏打印机 我已经构建了我的 DLL 并使用它注册了regasm其次是 代码库这使得 IIS 能够识别它 虽然我可以设置我的对象
  • 对类 static constexpr 结构的未定义引用,g++ 与 clang

    这是我的代码 a cp p struct int2 int x y struct Foo static constexpr int bar1 1 static constexpr int2 bar2 1 2 int foo1 return
  • C# 列表通用扩展方法与非通用扩展方法

    这是一个简单的问题 我希望 集合类中有通用和非通用方法 例如List
  • WcfSvcHost 的跨域异常

    对于另一个跨域问题 我深表歉意 我一整天都在与这个问题作斗争 现在已经到了沸腾的地步 我有一个 Silverlight 应用程序项目 SLApp1 一个用于托管 Silverlight SLApp1 Web 的 Web 项目和 WCF 项目
  • 如何定义一个可结构化绑定的对象的概念?

    我想定义一个concept可以检测类型是否T can be 结构化绑定 or not template
  • 实例化类时重写虚拟方法

    我有一个带有一些虚函数的类 让我们假设这是其中之一 public class AClassWhatever protected virtual string DoAThingToAString string inputString retu
  • 为什么使用小于 32 位的整数?

    我总是喜欢使用最小尺寸的变量 这样效果就很好 但是如果我使用短字节整数而不是整数 并且内存是 32 位字可寻址 这真的会给我带来好处吗 编译器是否会做一些事情来增强内存使用 对于局部变量 它可能没有多大意义 但是在具有数千甚至数百万项的结构
  • 为什么 isnormal() 说一个值是正常的,而实际上不是?

    include
  • 有没有办法让 doxygen 自动处理未记录的 C 代码?

    通常它会忽略未记录的 C 文件 但我想测试 Callgraph 功能 例如 您知道在不更改 C 文件的情况下解决此问题的方法吗 设置变量EXTRACT ALL YES在你的 Doxyfile 中
  • C++ 继承的内存布局

    如果我有两个类 一个类继承另一个类 并且子类仅包含函数 那么这两个类的内存布局是否相同 e g class Base int a b c class Derived public Base only functions 我读过编译器无法对数
  • 在OpenGL中,我可以在坐标(5, 5)处精确地绘制一个像素吗?

    我所说的 5 5 正是指第五行第五列 我发现使用屏幕坐标来绘制东西非常困难 OpenGL 中的所有坐标都是相对的 通常范围从 1 0 到 1 0 为什么阻止程序员使用屏幕坐标 窗口坐标如此严重 最简单的方法可能是通过以下方式设置投影以匹配渲
  • MySQL Connector C/C API - 使用特殊字符进行查询

    我是一个 C 程序 我有一个接受域名参数的函数 void db domains query char name 使用 mysql query 我测试数据库中是否存在域名 如果不是这种情况 我插入新域名 char query 400 spri
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l

随机推荐

  • SQL-万能密码

    打开目标站点 随便输入用户名密码 查看返回结果 提示错误 后台管理http 10 225 91 25 知识点 这题需要用到万能密码 首先了解一下万能密码 一般的 库验证登录注册查询数据会用到以下的句型 如果用户名与密码匹配正确则返回真值通过
  • Webpack配置

    Webpack 文章目录 Webpack 一 Webpack的基本功能 二 Webpack的核心概念 三 Webpack常用的Loader 四 Webpack常见的Plugins 五 Loader和Plugin的区别 以及如何自定义Load
  • Ehcache是现在最流行的纯Java开源缓存框架

    Ehcache是现在最流行的纯Java开源缓存框架 配置简单 结构清晰 功能强大 最初知道它 是从Hibernate的缓存开始的 网上中文的EhCache材料以简单介绍和配置方法居多 如果你有这方面的问题 请自行google 对于API 官
  • java枚举和数值的相互转换

    枚举简介 enum 的全称为 enumeration 是 JDK 1 5 中引入的新特性 存放在 java lang 包中 在实际编程中 往往存在着这样的 数据集 它们的数值在程序中是稳定的 而且 数据集 中的元素是有限的 此时枚举可以很方
  • BH1750 光照传感器文档详解 及 驱动设计

    前言 最近接触到一个应用 需要在低功耗的产品上加上光照度采集 正好最近有接触到一款光照传感器 BH1750 性能价格都合适 那么今天就抽空来好好测试一下 那么要写一篇测试文章 我会尽量以新手的角度从资料的获取 资料的阅读理解 以及根据资料进
  • Linux网络编程(7)本地套接字通信

    TCP本地套接字通信 为了实现没有血缘关系的进程之间通信 通常会采用本地套接字进行通信 在两个进程分别绑定好了套接字文件 sock 运行程序后将产生两个套接字文件 这两个文件共享同一片内核缓冲区 内核将完成两个进程之间的数据传输 在不同通信
  • 递推方程求解方法

    总结一下递推方程的求解方法 主要介绍六种方法 迭代法 差消法 递归树 主定理 特征根法 母函数法 欢迎大家批评指正 1 迭代法 不断用递推方程的右部替换左部 下面以汉诺塔为例进行求解 有时候直接迭代可能不太方便 可以使用换元迭代 下面以二分
  • Nginx 安全配置

    Nginx 是一个高性能的 HTTP 和反向代理服务 使用非常广泛 目前很大一部分网站均使用了 Nginx 作为 WEB 服务器 Nginx 虽然非常强大 但是安全防护的配置及恶意访问默认是没用做基础配置的 一 nginx 版本信息隐藏 s
  • STM32学习记录——使用蓝牙点亮LED

    文章目录 前言 一 学习目的 二 模块介绍 三 代码记录 四 实际操作 前言 今天记录一个蓝牙模块的简单应用 有关蓝牙的AT指令模式的设置在前面的记录中已经详细记录过 如果忘记了可以看看下面的文章 HC 05蓝牙模块的使用 KAIs32的博
  • 所有的raft算法

    https raft github io
  • 【java.lang.ref】PhantomReference & jdk.internal.ref.Cleaner

    目录 零 前情概要 ref包内容 系列目录 上一章回顾 一 PhantomReference 适用场景 优雅和提前规避 Java中使用native memory 常规做法 借助于Java引用机制 处理流程 二 jdk internal re
  • KETTLE 异常处理

    kettle未能正常执行任务时 同样需要记录下来执行操作 以便分析任务异常 红色线条代表任务异常时执行的任务 绿色为正常执行的任务序列 任务执行时都有日志记录 但kettle日志表中字段存在执行时间 但不存在业务日期字段 比如今天的任务执行
  • 第11节-PhotoShop基础课程-索套工具

    文章目录 前言 1 索套工具 选中后按Ctrl 可以移动 2 加 减 交叉 shift alt 2 多边形索套工具 手动首尾相连 或者双击空地 1 单击绘制直线选区 2 双击结束绘制 3 加 减 交叉 4 delete可以删除节点 3 磁性
  • MySQL数据管理

    一 外键的创建 方法1 在创建表的时候 增加约束 这个方法比较复杂麻烦 CREATE TABLE grade gradeid INT 10 NOT NULL AUTO INCREMENT COMMENT 年级id gradename VAR
  • 《计算机网络》谢希仁第七版课后答案完整版

    文章目录 第一章 概述 1 01 计算机网络向用户可以提供那些服务 1 02 简述分组交换的要点 1 03 试从多个方面比较电路交换 报文交换和分组交换的主要优缺点 1 04 为什么说因特网是自印刷术以来人类通信方面最大的变革 1 05 因
  • c语言——找零问题

    某人购买物品一共花了x元 x lt 100 用100元现金去支付 售货员需要找零 请设计程序给出一个找零方案 并使找零的张数最少 设现有人民币面值包括 100元 50元 20元 10元 5元 1元 5角和1角 当x包含1角以下的金额时 按照
  • Arduino基本知识

    1 程序结构 2 变量 局部变量和全局变量 3 调用Arduino的函数 4 通过点亮led IO设置为输出模式 学习pinMode 和digitalWrite 5 按键开关 IO设置为输入模式 6 初识赋值运算符和逻辑运算符 和布尔类型
  • uni-popup使用uni-grid显示错误问题

    在uni popup中 使用了uni grid 内部item显示错误 重叠 解决方式 为item设置style 比如我设置的列数是4 那么style中的width设置为25 item中的文字也需要设置宽度 否则文字方向不对 原因 暂时未知
  • c语言 教学目标,c语言选择结构程序设计教学目标

    c语言选择结构程序设计教学目标 选择结构程序设计 教学目标 能力要求 a 能够使用 if 语句编写条件选择程序 b 能够使用嵌套 if 语句和 switch 语句编写多分支选择结构程序 基础能力落实 1 判断一个数的正 负 输出相应信息 2
  • C++11 -- 类的新功能

    文章目录 类的新功能 默认成员函数 类成员变量初始化 强制生成默认函数的关键字default 禁止生成默认函数的关键字delete 继承和多态中的final和override关键字 类的新功能 默认成员函数 原来在C 类中 有6个默认成员函