c++--类型行为控制

1.c++的类

1.1.c++的类关键点
c++类型的关键点在于类存在继承。在此基础上,类存在构造,赋值,析构三类通用的关键行为。
类型提供了构造函数,赋值运算符,析构函数来让我们控制三类通用行为的具体表现。
为了清楚的说明类的构造,析构,赋值行为。我们采用一个具体的实例来验证这种行为。

// "test.h"
#include <iostream>class Norm1
{
public:Norm1() {printf("Norm1()_%c\n", m_i);}Norm1(char i):m_i(i) {printf("Norm1(int)_%c\n", m_i);}Norm1(const Norm1 &a):m_i(a.m_i){printf("Norm1(const Norm1&)_%c\n", m_i);}Norm1(Norm1&& a):m_i(a.m_i) {printf("Norm1(Norm1&&)_%c\n", m_i);}Norm1& operator=(const Norm1& a){m_i = a.m_i;printf("Norm1=(const Norm1&)_%c\n", m_i);return *this;}Norm1& operator=(Norm1&& a){m_i = a.m_i;printf("Norm1=(Norm1&&)_%c\n", m_i);return *this;}~Norm1(){printf("~Norm1()_%c\n", m_i);}
private:char m_i;
};class Norm2
{
public:Norm2() {printf("Norm2()_%c\n", m_i);}Norm2(char i):m_i(i) {printf("Norm2(int)_%c\n", m_i);}Norm2(const Norm2 &a):m_i(a.m_i){printf("Norm2(const Norm2&)_%c\n", m_i);}Norm2(Norm2&& a):m_i(a.m_i) {printf("Norm2(Norm2&&)_%c\n", m_i);}Norm2& operator=(const Norm2& a){m_i = a.m_i;printf("Norm2=(const Norm2&)_%c\n", m_i);return *this;}Norm2& operator=(Norm2&& a){m_i = a.m_i;printf("Norm2=(Norm2&&)_%c\n", m_i);return *this;}~Norm2(){printf("~Norm2()_%c\n", m_i);}
private:char m_i;
};class Base
{
public:Base():m_n2('b'),m_n1('b') {printf("Base()\n");}Base(char i):m_n2(i),m_n1(i) {printf("Base(int)\n");}Base(const Base &a):m_n2(a.m_n2), m_n1(a.m_n1){printf("Base(const Base&)\n");}Base(Base&& a):m_n2(a.m_n2),m_n1(a.m_n1) {printf("Base(Base&&)\n");}Base& operator=(const Base& a){m_n1 = a.m_n1;m_n2 = a.m_n2;printf("Base=(const Base&)\n");return *this;}Base& operator=(Base&& a){m_n1 = a.m_n1;m_n2 = a.m_n2;printf("Base=(Base&&)\n");return *this;}~Base(){printf("~Base()\n");}
private:Norm1 m_n1;Norm2 m_n2;
};class A : public Base
{
public:A():Base('b'),m_n1('a'),m_n2('a') {printf("A()\n");}A(char i): Base(i),m_n1(i),m_n2(i) {printf("A(int)\n");}A(const A &a):Base(a),m_n1(a.m_n1),m_n2(a.m_n2){printf("A(const A&)\n");}A(A&& a):Base(a),m_n1(a.m_n1),m_n2(a.m_n2) {printf("A(A&&)\n");}A& operator=(const A& a){Base::operator=(a);m_n1 = a.m_n1;m_n2 = a.m_n2;printf("A=(const A&)\n");return *this;}A& operator=(A&& a){Base::operator=(a);m_n1 = a.m_n1;m_n2 = a.m_n2;printf("A=(A&&)\n");return *this;}~A(){printf("~A()\n");}
private:Norm1 m_n1;Norm2 m_n2;
};

2.关于类的构造

2.1.构造函数执行顺序

#include "test.h"
int main()
{A a1;return 0;
}

上述代码编译运行后的输出:
在这里插入图片描述

这是因为对任意一个c++类型A其构造顺序为:
1.基类构造
2.成员变量按类内出现顺序构造
3.类自身的构造函数体
上述定义一个递归式的定义。

2.2.默认构造
(1).定义
不需要传递任何参数即可执行的构造函数称为默认构造。

class A1
{
public:A1(){}
};class A2
{
public:A2(int i = 0){}
};

上述A1()算默认构造,A2(int i = 0)也算默认构造。因为这些构造函数可以不用传递任何实参即可执行。如A1 a1;A2 a2;将分别执行上述构造函数。

(2).合成版本
a.当一个c++类型定义中用户没有自定义任何构造函数时,编译器默认为其提供一个默认构造函数。

#include <iostream>
class A
{
public:A(const A&){printf("A(const A&)\n");}
};int main()
{A a;// err,因为已经存在自定义构造函数,无合成版本return 0;
}

b.默认构造函数可行的前提是:
1.基类支持默认构造
2.类的所有成员变量支持默认构造

#include <iostream>
class T
{
private:T(){}
};class A
{
public:private:T t;
};int main()
{A a;// err,因为类的成员t对A来说不支持默认构造return 0;
}

c.合成的默认构造的行为
1.基类默认构造
2.成员变量按类内出现顺序执行默认构造
3.类自身的构造函数体(合成下函数体为空)
上述定义是一个递归式的定义。

(3).显式请求合成版本
当用户自定义了构造函数,而我们依然希望编译器提供合成版本默认构造时可以用下述方式

#include <iostream>class A
{
public:A(int i){printf("A(int)\n");}A()=default;
};int main()
{A a;return 0;
}

3.拷贝构造

拷贝构造其实就是构造的一种类型。但是使用比较频繁,故单独拿出来再说一遍。
3.1.拷贝构造执行顺序
拷贝构造依然属于构造,所以遵循构造执行顺序。

#include "test.h"
int main()
{A a1;A a2(a1);return 0;
}

上述代码编译后,输出为:
在这里插入图片描述
其中蓝色部分对应A a2(a1);这是因为对任意一个c++类型A其拷贝构造顺序为:
1.基类构造
2.成员变量按类内出现顺序构造
3.类自身的构造函数体
上述定义一个递归式的定义。

A的基础类型,A的成员变量的构造可能存在多个版本,具体采用那个版本呢:
1.匹配到的A的构造函数中构造列表指定了如何构造下,以构造列表中指定的为准。
2.匹配到的A的构造函数中构造列表未指定时,采用类型的默认构造。

3.2.默认构造
(1).定义
只需要传递类型自身一个实例即可执行的构造函数称为拷贝构造。

class A1
{
public:A1(const A1& a){}A1() = default;
};class A2
{
public:A2(const A2& a, int i = 0){}A2() = default;
};

上述A1(const A1& a)算默认构造,A2(const A2& a, int i = 0)也算默认构造。因为这些构造函数只需要传递类型自身一个实例即可执行。如A1 a1;A2 a11(a1);A2 a2;A2 a22(a2);A2 a11(a1)A2 a22(a2)将分别执行上述拷贝构造。

因为拷贝构造也属于构造函数,所以定义了拷贝构造下,若希望提供合成版本默认构造函数,必须采用A1()=default这样的方式显式请求。

(2).合成版本
a.对于拷贝构造,不同于默认构造。
默认构造是只要存在任何构造函数,就不会提供编译器合成的默认构造版本。
拷贝构造,允许存在其他构造函数,只要用户没有定义只接收自身实例即可执行的构造函数,编译器就会提供合成的拷贝构造。

b.合成版本行为表现

#include "test.h"
class B : public Base
{
public:
private:Norm1 m_n1;Norm2 m_n2;
};int main()
{B b1;B b2(b1);return 0;
}

在这里插入图片描述
上述输出中红色对于合成版本默认构造。蓝色对应合成版本拷贝构造。
合成拷贝构造下,构造顺序依然是:
1.基类构造
2.成员变量按类内出现顺序构造
3.类自身的构造函数体
上述定义一个递归式的定义。

不过此时,对基类,执行的是基类的拷贝构造。对成员变量,执行的也是其拷贝构造。类自身的拷贝构造函数体是空的。

c.默认构造函数可行的前提是:
1.基类支持拷贝构造
2.类的所有成员变量支持拷贝构造

#include <iostream>
class T
{
public:T(){}
private:T(T& t){}
};class A
{
public:private:T t;
};int main()
{A a;A a2(a);// err,因为A的成员t不支持拷贝构造。return 0;
}

(3).显式请求合成版本
拷贝构造严格来说没有显式请求的必要,因为只要用户没有通过只接收类型自身实例即可执行的构造函数,编译器总是会提供合成的拷贝构造。

4.关于类的赋值

4.1.赋值顺序

#include "test.h"
int main()
{A a1;A a2;a2 = a1;return 0;
}

在这里插入图片描述
上述紫红色的1,2,3对应a2=a1;
值得注意的是,赋值运算符不像构造函数,析构函数。
像构造函数无论如何总是会对基类执行构造,对类的成员执行构造。类的定义者只能通过构造初始化列表决定采用那个版本的基类构造,成员构造。但基类构造,成员构造作为两个执行阶段无论针对合成版本构造函数,还是用户自定义的构造函数总是存在的。
对赋值,如何用户自定义了赋值运算符,则,基类部分赋值,成员赋值全部依赖自定义函数体的实现。
上述紫红色包含1,2,3的原因是我们提供的自定义赋值运算符里面分别处理了基类赋值,成员赋值。

class A : public Base
{
public:A& operator=(const A& a){Base::operator=(a);m_n1 = a.m_n1;m_n2 = a.m_n2;printf("A=(const A&)\n");return *this;}private:Norm1 m_n1;Norm2 m_n2;
};

4.2.默认赋值运算符
(1).定义
赋值运算符属于运算符的范畴,其定义形式有相对较为严格的要求

class A1
{
public:A1& operator=(const A1& a){...	}
};

作为赋值运算符:
1.返回值必须是类型自身的引用类型。
2.只能接收一个形参。
3.传递类型自身实例时,可以匹配到形参。所以下述,也是合法的自定义赋值运算符。

class A1
{
public:A1& operator=(A1 a){...	}
};

(2).合成版本
a.何时合成
用户没有自定义赋值运算符时。

b.合成版本行为表现

#include "test.h"
class B : public A
{
private:Norm1 n1;
};int main()
{B b1;B b2 = b1;return 0;
}

在这里插入图片描述
上述紫红色1,2对应B的合成版本赋值运算符。
合成的赋值运算符:
1.通过基类赋值运算符完成基类赋值。
2.对每个成员执行其赋值运算符完成成员赋值。

c.合成版本可行的前提是:
1.基类支持赋值运算符
2.类的所有成员变量支持赋值运算符

#include <iostream>
class T
{
public:T(){}
private:T& operator=(T&){}
};class A
{
public:private:T t;
};int main()
{A a1;A a2;a2 = a1;// err,因为A的成员t不支持赋值运算符(t的赋值运算符对A不可见)return 0;
}

(3).显式请求合成版本
赋值运算符严格来说没有显式请求的必要,因为只要用户没有定义赋值运算符,编译器总是会提供合成的赋值运算符。

4.关于类的析构

4.1.析构顺序

#include "test.h"
int main()
{A a;return 0;
}

在这里插入图片描述
上述蓝色1,2,3对应A a;中实例a的析构过程。
析构函数执行顺序为:
1.类自身的析构函数体
2.成员变量按类内出现顺序反向析构
3.基类析构
上述定义一个递归式的定义。

不像构造函数,析构函数没有初始值列表这样的东西。每个类型的析构函数也不存在多个版本。

4.2.合成版本
a.何时合成
只要用户没有自定义析构函数,编译器就会合成析构函数。

b.合成版本行为表现
1.类自身的析构函数体。合成版本函数体为空。
2.成员变量按类内出现顺序反向析构
3.基类析构
上述定义一个递归式的定义。

c.合成版本可行前提
1.类的基类支持析构
2.类的成员支持析构

4.3.显式请求合成版本
析构函数严格来说没有显式请求的必要,因为只要用户没有定义析构函数,编译器总是会提供合成的析构函数。

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

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

相关文章

现代雷达车载应用——第2章 汽车雷达系统原理 2.1节

经典著作&#xff0c;值得一读&#xff0c;英文原版下载链接【免费】ModernRadarforAutomotiveApplications资源-CSDN文库。 2.1 基本雷达功能 雷达系统通过天线或天线阵列向空间辐射电磁能量。辐射的电磁能量“照亮”周围的目标。“被照亮”的目标拦截一些辐射能量&#xff0…

图片帧数超过300帧,调整后重新上传

发表公众号的时候&#xff0c;传了一些 GIF 格式的动图&#xff0c;但是提示&#xff1a;图片帧数超过300帧&#xff0c;调整后重新上传。怎么解决这个问题呢&#xff1f;GIF 格式的图片&#xff0c;是将多个图片保存在一起&#xff0c;并逐帧显示的&#xff0c;帧数越多&#…

[NAND Flash 2.2] NAND闪存及控制器的市场趋势 [2023]

依公知及经验整理&#xff0c;原创保护&#xff0c;禁止转载。 专栏 《深入理解NAND Flash》 <<<< 返回总目录 <<<< 一、NAND闪存市场分析 据欧洲知名半导体分析机构Yole发布的报告显示&#xff0c;2020年起&#xff0c;NAND闪存市场发展趋势保持稳定…

Redis中HyperLogLog的使用

目录 前言 HyperLogLog 前言 在学习HyperLogLog之前&#xff0c;我们需要先学习两个概念 UV&#xff1a;全称Unique Visitor&#xff0c;也叫独立访客量&#xff0c;是指通过互联网访问、浏览这个网页的自然人。1天内同一个用户多次访问该网站&#xff0c;只记录1次。PV&am…

二值图像分割统一项目

1. 项目文件介绍 本章为二值图像的分割任务做统一实现&#xff0c;下面是项目的实现目录 项目和文章绑定了&#xff0c;之前没用过&#xff0c;不知道行不行 data 文件夹下负责摆放数据的训练集测试集inference 负责放待推理的图片(支持多张图片预测分割)run_results 是网络训…

centos7 yum安装nginx

1.安装源 rpm -ivh http://nginx.org/packages/centos/5/noarch/RPMS/nginx-release-centos-5-0.el5.ngx.noarch.rpm 2.安装 (-y 的意思是自动yes) yum install nginx -y 3.查找安装到哪里了 whereis nginx 一般都是在 /etc/nginx下面 4.常用命令 检查配置文件是否正确 …

InnoDB的数据存储结构

一 数据库的存储结构&#xff1a;页 索引结构提供了高效的检索方式&#xff0c;不过索引信息和数据记录都是保存在文件上的&#xff0c;确切的说是存储在页结构中。另一方面&#xff0c;索引是在引擎中实现的&#xff0c;MySQL服务器上的存储引擎负责对表中数据的读取和写入。…

R语言学习

Part1阶段1&#xff1a;入门基础 1安装R和RStudio&#xff1a; 下载并安装R&#xff1a;https://cran.r-project.org/ 下载并安装RStudio&#xff1a;https://www.rstudio.com/products/rstudio/download/ 2Hello World&#xff1a; 学习如何在R中输出"Hello, World!"…

软考高项第四版五组十域表+ITTO背诵笔记及助记

基于第四版做的笔记&#xff0c;助记是自己编的 还是得靠理解记忆&#xff0c;下面是文档&#xff0c;也用anki制作了记忆卡片&#xff0c;需要的可以自行导入卡包

高德地图加载三维模型vue(.obj转.gltf)

官方glTF模型案例 obj2gltf 的开发文档 第一步&#xff1a;这里首先要将我们的.obj文件转换为.gltf文件 全局安装 npm install -g obj2gltf终端打开.obj文件所在的文件夹执行 obj2gltf -i model.obj -o model.gltf -t &#xff08;-i model.obj对应你的obj文件的名字&#x…

企业部署Windows活动目录有什么好处?

在一个现代化的企业中&#xff0c;高效、安全地管理公司的IT资源是至关重要的。Windows Active Directory&#xff08;活动目录&#xff09;是一个强大的功能&#xff0c;可以帮助企业实现集中管理用户、计算机、组策略和其他资源的目的。本文将探讨部署Windows AD域即活动目录…

【往届见刊检索速度hin OK】 第五届计算机工程与应用国际学术会议 (ICCEA 2024)

第五届计算机工程与应用国际学术会议 (ICCEA 2024) 2024 5th International Conference on Computer Engineering and Application 2024年4月12-14日 中国-杭州 计算机工程与应用在人工智能、大数据、云计算、物联网、网络安全等领域发挥着重要作用&#xff0c;随着科技日…

[NAND Flash 2.3] 闪存芯片国产进程

依公知及经验整理&#xff0c;原创保护&#xff0c;禁止转载。 专栏 《深入理解NAND Flash》 <<<< 返回总目录 <<<< 目录 前言1 闪存介质1.1 NOR 闪存国产技术发展1.2 NAND 闪存国产技术 2 闪存国产厂商与产品2.1 NOR FLASH 国产厂商与产品2.2 NAND FA…

深入Os--动态链接

1.动态链接库的使用 动态库支持以两种模式使用&#xff0c;一种模式下&#xff0c;在程序加载运行时&#xff0c;完成动态链接。一种模式下&#xff0c;在程序运行中&#xff0c;完成动态链接。 1.1.程序加载运行时完成动态链接 我们通过一个实例介绍程序加载运行时&#xff0c…

4G基站BBU、RRU、核心网设备

目录 前言 基站 核心网 信号传输 前言 移动运营商在建设4G基站的时候&#xff0c;除了建设一座铁塔之外&#xff0c;更重要的是建设搭载铁塔之上的移动通信设备&#xff0c;这篇博客主要介绍BBU&#xff0c;RRU以及机房的核心网等设备。 基站 一个基站有BBU&#xff0c;…

Liunx系统使用超详细(四)~文件/文本相关命令1

目录 一、mkdir命令 1.1基本语法 1.2常用示例 1.2.1创建目录 1.2.2创建多级目录 1.2.3设置权限 1.2.4递归修改权限 1.2.5显示帮助信息 二、touch命令 2.1基本语法 2.2常用示例 2.2.1创建新的空文件 2.2.2更新现有文件的访问和修改时间戳 2.2.3创建多个文件 2.2.…

【Midjourney实战】| 新年红包、元宝、灯笼、福袋生成

文章目录 1 红包生成2 灯笼生成3 福袋生成4 元宝生成 1 红包生成 之前我们生成了新年礼盒&#xff0c;这一期我们来生成一些过年时特有的元宝和红包 首先&#xff0c;最重要的是画面主体 red envelope&#xff08;红包&#xff09;颜色红色 黄色的 red and yellow 为了后期方…

跳水比赛(C++)

系列文章目录 进阶的卡莎C++_睡觉觉觉得的博客-CSDN博客数1的个数_睡觉觉觉得的博客-CSDN博客双精度浮点数的输入输出_睡觉觉觉得的博客-CSDN博客足球联赛积分_睡觉觉觉得的博客-CSDN博客大减价(一级)_睡觉觉觉得的博客-CSDN博客小写字母的判断_睡觉觉觉得的博客-CSDN博客纸币(…

Web漏洞分析-SQL注入XXE注入(下)

随着互联网的不断普及和Web应用的广泛应用&#xff0c;网络安全问题愈发引起广泛关注。在网络安全领域中&#xff0c;SQL注入和XXE注入是两个备受关注的话题&#xff0c;也是导致许多安全漏洞的主要原因之一。本博客将深入研究这两种常见的Web漏洞&#xff0c;带您探寻背后的原…

GNSEC 2022年第8届全球下一代软件工程线上峰会-核心PPT资料下载

一、峰会简介 新一代软件工程是指利用新的理论、方法和技术&#xff0c;在软件开发、部署、运维等过程中&#xff0c;实现软件的可控、可预测、可维护的软件生产方式。它涵盖了多个领域&#xff0c;如软件开发、测试、部署、运维等&#xff0c;旨在提高软件生产效率和质量。 …