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.显式请求合成版本
析构函数严格来说没有显式请求的必要,因为只要用户没有定义析构函数,编译器总是会提供合成的析构函数。
相关文章:

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

笔记64:Bahdanau 注意力
本地笔记地址:D:\work_file\(4)DeepLearning_Learning\03_个人笔记\3.循环神经网络\第10章:动手学深度学习~注意力机制 a a a a a a a a a a a...
面试官问:如何手动触发垃圾回收?幸好昨天复习到了
在Java中,手动触发垃圾回收可以使用 System.gc() 方法。但需要注意,调用 System.gc() 并不能确保立即执行垃圾回收,因为具体的垃圾回收行为是由Java虚拟机决定的,而不受程序员直接控制。 public class GarbageCollectionExample …...

操作系统的运行机制+中断和异常
一、CPU状态 在CPU设计和生产的时候就划分了特权指令和非特叔指令,因此CPU执行一条指令前就能断出其类型 CPU有两种状态,“内核态”和“用户态” 处于内核态时,说明此时正在运行的是内核程序,此时可以执行特权指令。 处于用户态…...

Python实战:批量加密Excel文件指南
更多Python学习内容:ipengtao.com 大家好,我是彭涛,今天为大家分享 Python实战:批量加密Excel文件指南,全文3800字,阅读大约10分钟。 在日常工作中,保护敏感数据是至关重要的。本文将引导你通过…...

二叉树链式结构的实现和二叉树的遍历以及判断完全二叉树
二叉树的实现 定义结构体 我们首先定义一个结构来存放二叉树的节点 结构体里分别存放左子节点和右子节点以及节点存放的数据 typedef int BTDataType; typedef struct BinaryTreeNode {BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right; }BTNode;…...

vue中的动画组件使用及如何在vue中使用animate.css
“< Transition >” 是一个内置组件,这意味着它在任意别的组件中都可以被使用,无需注册。它可以将进入和离开动画应用到通过默认插槽传递给它的元素或组件上。进入或离开可以由以下的条件之一触发: 由 v-if 所触发的切换由 v-show 所触…...
qt 5.15.2 网络文件下载功能
qt 5.15.2 网络文件下载功能 #include <QCoreApplication>#include <iostream> #include <QFile> #include <QTextStream> // #include <QtCore> #include <QtNetwork> #include <QNetworkAccessManager> #include <QNetworkRep…...
Wifi adb 操作步骤
1.连接usb 到主机 手机开起热点,电脑和车机连接手机,或者电脑开热点,车机连接电脑,车机和电脑连接同一个网络 因为需要先使用usb,后面切换到wifi usb 2.查看车机ip地址,和电脑ip地址 电脑win键r 输入cmd…...
湿货 - 231206 - 关于如何构造输入输出数据并读写至文件中
TAG - 造数据、读写文件 造数据、读写文件 造数据、读写文件//*.in // #include<bits/stdc.h> using namespace std;/* *********** *********** 全局 ********** *********** */ string Pre_File_Name; ofstream IN_cout; int idx;void Modify_ABS_Path( string& …...

EasyMicrobiome-易扩增子、易宏基因组等分析流程依赖常用软件、脚本文件和数据库注释文件
啥也不说了,这个好用,给大家推荐:YongxinLiu/EasyMicrobiome (github.com) 大家先看看引用文献吧,很有用:https://doi.org/10.1002/imt2.83 还有这个,后面马上介绍:YongxinLiu/EasyAmplicon: E…...
【Python百宝箱】漫游Python数据可视化宇宙:pyspark、dash、streamlit、matplotlib、seaborn全景式导览
Python数据可视化大比拼:从大数据处理到交互式Web应用 前言 在当今数字时代,数据可视化是解释和传达信息的不可或缺的工具之一。本文将深入探讨Python中流行的数据可视化库,从大数据处理到交互式Web应用,为读者提供全面的了解和…...

企业数字档案馆室建设指南
数字化时代,企业数字化转型已经成为当下各行业发展的必然趋势。企业数字化转型不仅仅是IT系统的升级,也包括企业内部各种文件、档案、合同等信息的数字化管理。因此,建设数字档案馆室也变得尤为重要。本篇文章将为您介绍企业数字档案馆室建设…...
JavaScript中处理时间差
ES6版本 function countdown(endTime, includeSeconds true) {// 获取当前时间let now new Date();// 将传入的结束时间字符串转换为日期对象let endDateTime new Date(endTime);// 检查传入的时间字符串是否只包含日期(不包含时分秒)if (endTime.tr…...

Multidimensional Scaling(MDS多维缩放)算法及其应用
在这篇博客中,我将与大家分享在流形分析领域的一个非常重要的方法,即多维缩放MDS。整体来说,该方法提供了一种将内蕴距离映射到显性欧氏空间的计算,为非刚性形状分析提供了一种解决方案。当初就是因为读了Bronstein的相关工作【1】…...

单片机_RTOS_架构
一. RTOS的概念 // 经典单片机程序 void main() {while (1){喂一口饭();回一个信息();} } ------------------------------------------------------ // RTOS程序 喂饭() {while (1){喂一口饭();} }回信息() {while (1){回一个信息();} }void main() {create_task(喂饭);cr…...
Golang rsa 验证
一下代码用于rsa 签名的验签, 签名可以用其他语言产生。也可以用golang生成。 package mainimport ("crypto""crypto/rsa""crypto/sha256""crypto/x509""encoding/pem""errors""fmt" )fun…...

网络安全威胁——跨站脚本攻击
跨站脚本攻击 1. 定义2. 跨站脚本攻击如何工作3. 跨站脚本攻击类型4. 如何防止跨站脚本攻击 1. 定义 跨站脚本攻击(Cross-site Scripting,通常称为XSS),是一种典型的Web程序漏洞利用攻击,在线论坛、博客、留言板等共享…...

Java利用UDP实现简单的双人聊天
一、创建新项目 首先创建一个新的项目,并命名。 二、实现代码 import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.net.*; import java.io.IOException; import java.lang.String; public class liaotian extends JFrame{ pri…...

HBase整合Phoenix
文章目录 一、简介1、Phoenix定义2、Phoenix架构 二、安装Phoenix1、安装 三、Phoenix操作1、Phoenix 数据映射2、Phoenix Shell操作3、Phoenix JDBC操作3.1 胖客户端3.2 瘦客户端 四、Phoenix二级索引1、为什么需要二级索引2、全局索引(global index)3、…...

网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...

FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...

系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文通过代码驱动的方式,系统讲解PyTorch核心概念和实战技巧,涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...

2.3 物理层设备
在这个视频中,我们要学习工作在物理层的两种网络设备,分别是中继器和集线器。首先来看中继器。在计算机网络中两个节点之间,需要通过物理传输媒体或者说物理传输介质进行连接。像同轴电缆、双绞线就是典型的传输介质,假设A节点要给…...