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

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

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

相关文章

百度智能云推出“千帆AI原生应用开发工作台” 助力开发者创新

在这个智能互联的世界&#xff0c;每一次技术的飞跃都预示着无限可能。你是否曾梦想&#xff0c;一款产品能够打破创新的边界&#xff0c;让开发不再是高门槛的技术挑战&#xff1f;12月20日&#xff0c;百度云智大会智算大会将为你揭开这幕神秘面纱——“千帆AI原生应用开发工…

Python中的并发编程

目录 一、引言 二、Python中的线程 1、线程的概念 2、创建线程 3、线程同步和锁 4、线程池 三、Python中的进程 1、进程的概念 2、创建进程 四、Python中的异步IO 1、异步IO的概念 2、异步IO的实现 3、异步IO的并发执行 五、总结 一、引言 并发编程是一种计算机…

消息中间件——RabbitMQ(七)高级特性 2

前言 上一篇消息中间件——RabbitMQ&#xff08;七&#xff09;高级特性 1中我们介绍了消息如何保障100%的投递成功&#xff1f;,幂等性概念详解,在海量订单产生的业务高峰期&#xff0c;如何避免消息的重复消费的问题&#xff1f;,Confirm确认消息、Return返回消息。这篇我们…

LLM推理部署(四):一个用于训练、部署和评估基于大型语言模型的聊天机器人的开放平台FastChat

FastChat是用于对话机器人模型训练、部署、评估的开放平台。体验地址为&#xff1a;https://chat.lmsys.org/&#xff0c;该体验平台主要是为了收集人类的真实反馈&#xff0c;目前已经支持30多种大模型&#xff0c;已经收到500万的请求&#xff0c;收集了10万调人类对比大模型…

6-69.鸭子也是鸟

按要求完成下面的程序&#xff1a; 1、定义一个Bird类&#xff0c;包含一个void类型的无参的speak方法&#xff0c;输出“Jiu-Jiu-Jiu”。 2、定义一个Duck类&#xff0c;公有继承自Bird类&#xff0c;其成员包括&#xff1a; &#xff08;1&#xff09;私有string类型的成员na…

4个解决特定的任务的Pandas高效代码

在本文中&#xff0c;我将分享4个在一行代码中完成的Pandas操作。这些操作可以有效地解决特定的任务&#xff0c;并以一种好的方式给出结果。 从列表中创建字典 我有一份商品清单&#xff0c;我想看看它们的分布情况。更具体地说&#xff1a;希望得到唯一值以及它们在列表中出…

【高效开发工具系列】jackson入门使用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

指针的综合运用第二期

1.指针数组 char *arr[5];//字符指针数组 int *arr[5];//整型指针数组 int ADD(int x,int y) { return xy; } int LOSE(int a,int b) { return a-b; } int *pa(int,int)ADD; int *pb(int,int)LOSE;//函数指针 int (*pc[2])(int,int){ADD,LOSE};//函数指针数组 //调用直接按数组…

VisionPro---PatMaxTool工具使用

CogPMAlignTool PatMax是一种图案位置搜索技术&#xff08;识别定位&#xff09;&#xff0c;PatMax图案不依赖于像素格栅&#xff0c;是基于边缘特征的模板匹配而不是基于像素的模板匹配&#xff0c;支持图像中特征的旋转与缩放&#xff0c;边缘特征表示图像中不同区域间界限…

数据链路层之广域网、PPP协议、HDLC协议

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

内部培训平台的系统 PlayEdu搭建私有化内部培训平台

PlayEdu是由白书科技团队多年经营的线上教育系统&#xff0c;专为企业提供的全新企业培训方案 我们的目标是为更多的企业机构搭建私有化内部培训平台&#xff0c;以满足不断增长的培训需求 通过PlayEdu&#xff0c;企业可以有效地组织和管理培训资源&#xff0c;提供高质量的…

Leetcode—1423.可获得的最大点数【中等】

2023每日刷题&#xff08;四十八&#xff09; Leetcode—1423.可获得的最大点数 思路&#xff1a;逆向求长为 n−k 的连续子数组和的最小值 参考灵茶山艾府题解 实现代码 class Solution { public:int maxScore(vector<int>& cardPoints, int k) {int mins 0, …

nodejs基于vue的社区物业缴费报修管理系统7vwc6

运行软件:vscode 前端nodejsvueElementUi 语言 node.js 框架&#xff1a;Express/koa 前端:Vue.js 数据库&#xff1a;mysql 开发软件&#xff1a;VScode/webstorm/hbuiderx均可 数据库用MySQL,后台用vue框架 基本要求&#xff1a; 1. 对项目进行详细实际的需求分析。 2. 在网…

Java项目调用C/C++ SDK的方案汇总

Java项目调用C/C SDK的方案汇总 背景调研JNIJNativeJNAJavaCPP 背景 Java项目中需要调用到一个C项目&#xff0c;于是对目前通用的解决方案做了一些调研&#xff0c;这里做一个汇总。 调研 JNI JNI&#xff1a;Java Native Interface&#xff0c;JNI是一套编程接口&#xf…

某60区块链安全之Create2实战一学习记录

区块链安全 文章目录 区块链安全Create2实战一实验目的实验环境实验工具实验原理实验内容Create2实战一 实验步骤分析合约源代码漏洞Create2实战一 实验目的 学会使用python3的web3模块 学会分析以太坊智能合约Create2引发的漏洞及其利用 找到合约漏洞进行分析并形成利用 实…

基于SpringBoot + vue的在线视频教育平台

qq&#xff08;2829419543&#xff09;获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;springboot 前端&#xff1a;采用vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xf…

肖sir__mysql之视图__009

mysql之视图 一、什么是视图 视图是一个虚拟表&#xff08;逻辑表&#xff09;&#xff0c;它不在数据库中以存储形式保存&#xff08;本身包含数据&#xff09;&#xff0c;是在使用视图的时候动态生成。 二、视图作用 1、查询数据库中的非常复的数据 例如&#xff1a;多表&a…

微信支付/

微信支付准备工作 3.2.1 如何保证数据安全&#xff1f; 完成微信支付有两个关键的步骤&#xff1a; 第一个就是需要在商户系统当中调用微信后台的一个下单接口&#xff0c;就是生成预支付交易单。 第二个就是支付成功之后微信后台会给推送消息。 这两个接口数据的安全性&#x…

论文解读--Visual Lane Tracking and Prediction for Autonomous Vehicles

自动驾驶汽车视觉车道线跟踪和预测 摘要 我们提出了一种用于自动驾驶汽车跟踪水平道路车道标记位置的可视化方法。我们的方法是基于预测滤波的。预测步骤估计在每个新的图像帧中期望的车道标记位置。它也是基于汽车的运动学模型和嵌入式测程传感器产生的信息。使用适当准备的测…

区块链媒体:Web3.015个方法解析-华媒舍

Web3.0是第三代互联网的发展阶段&#xff0c;相较于Web2.0&#xff0c;它具有更高的可信性、安全性和去中心化特点。在Web3.0时代&#xff0c;推广变得更为重要&#xff0c;因为吸引用户和提高品牌知名度对于在竞争激烈的市场中脱颖而出至关重要。本文将揭秘推广Web3.0的15个秘…