【C++】--类和对象(2)
👌个人主页: 起名字真南
👆个人专栏:【数据结构初阶】 【C语言】 【C++】
目录
- 1 类的默认成员函数
- 2 构造函数
- 3 析构函数
- 4 拷贝构造
- 5 赋值运算符重载
- 5.1 运算符重载
- 5.2 赋值运算符的重载
1 类的默认成员函数
默认成员函数就是用户没有显示实现,编译器自动生成的的成员函数称为默认成员函数
一个类我们不写的情况下会默认生成六个成员函数,分别是构造函数,析构函数,拷贝构造,赋值重载以及关于取地址对普通对象和const对象的重载。
2 构造函数
构造函数是特殊的成员函数,构造函数的主要任务是在对象实例化时对其进行初始化对象。他的本质就是Stack和Date类中Init函数的功能,而且构造函数可以自动调用的特点就完美的替代了Init函数。
构造函数的特点 :
- 函数名和类型名一致
- 无返回值(不需要写void )
- 对象实例化时系统会自动调用对应的构造函数。
- 构造函数可以重载
- 如果类中没有定义构造函数那么编译器会自动生成一个无参的构造函数,如果显示定义了就不会自己生成
- 无参构造函数,全缺省构造函数,以及我们不写编译器自动生成的构造函数都叫做默认构造函数,并且这三个函数不能同时存在,同时存在虽然无参和全缺省构成了函数的重载但是调用函数时会存在歧义。总结不传实参就可以调用的构造函数统称为默认构造。
- 我们不写编译器默认生成的构造函数对内置类型没有影响,具体怎么初始化取决于编译器怎么初始化,但是对于我们用class/struct来创建的自定义类型成员变量就需要调用他的构造函数进行初始化,如果没有构造函数就会报错,我们要初始化成员变量就需要用到初始化列表(后面会解释)
#include<iostream>using namespace std;
class Date
{
public://无参构造函数/*Date(){_year = 1;_month = 1;_day = 1;}*///带参构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}//全缺省构造函数/*Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}*/void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1; //调用无参的构造函数d1.Print();Date d2(2024, 10, 6); //调用带参的构造函数d2.Print();return 0;
}
如果我们将第一个无参构造函数和第三个全缺省构造函数注释掉编译器就会报错显示没有可用的构造函数。
当我们使用无参的构造函数进行初始化时后面不需要加()否则编译器无法区分这里是函数声明还是实例化对象。
typedef int STLDateType;
class Stack
{
public:Stack(int n = 4){_a = (STLDateType*)malloc(sizeof(STLDateType) * n);if (_a == nullptr){perror("malloc fail!");return;}_top = 0;_capacity = n;}
private:STLDateType* _a;size_t _top;size_t _capacity;
};//两个Stack实现队列
class MyQueue
{
public://编译器默认生成MyQueue的构造函数调用了Stack的构造函数,完成了两个成员的初始化
private:Stack _STPush;Stack _STPop;
};
int main()
{MyQueue mq;return 0;
}
当一个类类型的成员变量是自定义类型的时候例如上面的代码MyQueue类的成员变量是自定义类型Stack类,所以编译器不会生成MyQueue的默认构造函数需要我们自己来实现,但是为什么我们明明没有写但是还是可以运行,因为编译器默认生成MyQueue的构造函数调用了Stack的构造函数,完成了两个成员的初始化。如果我们将Stack的构造函数注释掉就会报错。出现下面这种情况
3 析构函数
析构函数与构造函数的功能相反,析构函数不是完成对对象本身的销毁比如局部对象创立是在栈帧,如果函数结束那么栈帧就会被销毁,不需要我们去管。C++规定对象在销毁时会自动调用析构函数完成对对象中资源的清理和释放工作。析构函数的功能就类似与我们Stack类中Destory函数的功能,而向Date类没有Destory就不需要析构函数。
析构函数的特点 :
- 析构函数的函数名是在类名的前面加上‘~’。
- 无参数无返回值和构造函数类似
- 一个类只能有一个析构函数,如果未显示定义编译器就会自动生成一个默认析构函数
- 函数声明周期结束会自动调用析构函数
- 跟构造函数类似,我们不写编译器自动生成的析构函数对内置类型的成员变量不做处理,自定义类型成员会调用他自己的析构函数
- 需要注意的是如果我们写了析构函数但是对于自定义类型还是会调用他自己的析构函数,也就是说自定义类型成员变量无论什么时候都会调用自己的析构函数
- 一个类中如果没有资源申请那么使用编译器自己生成的析构函数就可以比如Date类,如果有资源申请像Stack/MyQueue类都需要自己写析构函数,否则会造成资源泄露。
- 一个局部域有多个对象,C++规定后定义的先析构。
#include<iostream>using namespace std;
typedef int STLDateType;
class Stack
{
public:Stack(int n = 4){_a = (STLDateType*)malloc(sizeof(STLDateType) * n);if (_a == nullptr){perror("malloc fail!");return;}_top = 0;_capacity = n;}~Stack(){cout << "~Stack()" << endl;free(_a);_a = nullptr;_top = _capacity = 0;}
private:STLDateType* _a;size_t _top;size_t _capacity;
};//两个Stack实现队列
class MyQueue
{
public://编译器默认生成MyQueue的构造函数调用了Stack的构造函数,完成了两个成员的初始化
private:Stack _STPush;Stack _STPop;
};
int main()
{Stack st;MyQueue mq;return 0;
}
从运行结果我们可以看到一共调用了三次析构函数,分别是st一次和mq内的两次
4 拷贝构造
如果说构造函数的第一个参数是自身类类型的引用,并且额外的参数都有默认值,那么这个构造函数可以叫做拷贝构造函数,拷贝构造也是一种特殊的构造函数。
拷贝构造的特点 :
- 拷贝构造函数是构造函数的重载
- 拷贝构造的函数参数有且必须只有一个就是自身类类型的引用,使用传值的方式编译器会报错,因为会引发无穷递归。
- C++规定自定义类型对象进行拷贝行为必须调用拷贝构造,所以这里自定义类型传值传参和传值返回都会调用拷贝构造来完成
- 若未显示定义拷贝构造,编译器会自动生成拷贝构造函数,自动生成的内置类型成员变量会进行值拷贝/浅拷贝(一个字节一个字节的拷贝),对自定义类型成员会调用他自己的拷贝构造
- 像Date类型的成员变量都是内置类型可以不写拷贝构造直接使用编译器自己生成的拷贝构造函数,但是如果是Stack类型虽然内部的成员变量都是内置类型但是其中的_a涉及到了资源的指向一起开辟,如果使用浅拷贝会发生冲突,所以需要我们自己实现并且将指向的资源同样进行拷贝。像MyQueue类型的虽然他的成员变量都是自定义类型但是在调用拷贝构造的时候也会调用Stack的拷贝构造所以也不用自己去实现。
- 传值返回会产生一个临时对象调用拷贝构造,传引用返回,返回的是对象的别名(引用),没有产生拷贝。但是如果返回对象是当前局部域的临时对象当前函数结束时该局部对象也会销毁就会出现野引用的问题,类似于野指针,传引用返回可以减少拷贝,但是一定要确定返回的对象不会因为当前函数结束而消失才可以使用。
#include<iostream>using namespace std;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//编译报错 Date类的复制构造函数不能有Date类型的参数,但是可以有 Date& 以及 Date* 类型//Date(Date d)Date(Date& d){_year = d._year;_month = d._month;_day = d._day;}Date(Date* d){_year = d->_year;_month = d->_month;_day = d->_day;}void print(){cout << _year << '-' << _month << '-' << _day << endl;}
private:int _year;int _month;int _day;
};void func1(Date d)
{cout << &d << endl;d.print();
}
Date& func2()
{Date tmp(2024, 10, 01);tmp.print();return tmp;
}
int main()
{//C++规定传值传参必须调用拷贝构造,所以这里调用了拷贝构造Date d1(2024,10,07);//传值传参给d会调用拷贝构造,使用传引用传参可以减少拷贝func1(d1);cout << &d1 << endl;//传引用传参,这里不是拷贝构造只是单纯的拷贝Date d2(&d1);d1.print();d2.print();//这里也是拷贝构造Date d3 = d1;d3.print();//拷贝构造用同类型的对象初始化而不是指针Date d4(d1);d4.print();//传引用返回的tmp在函数结束的时候就已经被销毁了,所以返回的是野引用Date ret = func2();ret.print();return 0;
}
class Stack
{
public:Stack(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (_a == nullptr){perror("malloc fail");}_capacity = n;_top = 0;}Stack(const Stack& st){//需要对_a指向的资源同样进行拷贝_a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);if (_a == nullptr){perror("malloc fail");}memcpy(_a, st._a, sizeof(STDataType) * st._top);_capacity = st._capacity;_top = st._top;}void Push(STDataType data){if (_top == _capacity){int newcapacity = _capacity * 2;STDataType* tmp = (STDataType*)realloc(_a, sizeof(STDataType) * newcapacity);if (tmp == nullptr){perror("realloc fail");}_a = tmp;_capacity = newcapacity;}_a[_top++] = data;}~Stack(){cout << "~Stack()" << endl;free(_a);_a = nullptr;_top = _capacity = 0;}
private:STDataType* _a;size_t _capacity;size_t _top;
};
//用两个栈实现队列
class MyQueue
{
private:Stack push;Stack pop;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);MyQueue m1;MyQueue m2 = m1;return 0;
}
5 赋值运算符重载
5.1 运算符重载
- 当运算符被用于类类型对象时,C++允许我们通过运算符重载的方式指定新的含义
- 重载运算符的参数个数和运算符本来的操作对象数量一样多一元操作符有一个参数,二元运算符有两个参数。
- 如果一个重载运算符函数是成员函数那么他的第一个参数是隐式指针this,因此运算符重载作为成员函数时会少一个参数。
- . :: ?: . sizeof* 这五个运算符不能重载
- 运算符重载是具有图书名字的函数,他的名字是由operator加上后面定义的运算符共同构成。和其他函数一样他也有返回类型 函数体和参数列表
- 重载++运算符要注意后置++多一个int类型的形参用来区分两个运算符
- 重载‘<<’ '>>'两个运算符的时候需要重载为全局函数,如果是成员函数他的第一个默认类型是隐式类型this作为他的左操作数调用时就变成了 ‘ 输出的对象<<cout ’ 不符合使用习惯和可读性,重载为全局函数只需要第一个参数类型时ostream/istream就可以了,第二个参数位置为该类类型的对象。
- 运算符重载后优先级和结合性要保持一致
- 重载操作符必须有一个类类型的参数,不能改变其原有的含义。
class A
{
public:void func(){cout << "A::func()" << endl;}
};
typedef void(A::* PF)(); //成员函数指针类型int main()
{//C++规定成员函数要加&才可以取到函数指针PF pf = &A::func;A obj;(obj.*pf)();//对象调用成员函数指针使用.*运算符return 0;
}
//重载为全局函数会面临的问题
// 无法访问私有变量
// 解决办法
// 1 用public
// 2 提供get函数
// 3 友元函数
// 4 重载为成员函数//public成员变量
bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
}
int main()
{Date d1(2024, 10, 06);Date d2(2024, 10, 07);//显示调用operator==(d1, d2);//编译器会转换成operator==(d1, d2)d1 == d2;return 0;
}
5.2 赋值运算符的重载
赋值运算符重载是一个默认成员函数,用于完成两个已经存在的的对象直接拷贝赋值,但这里和拷贝构造的区分,拷贝构造用于一个对象拷贝初始化给另一个创建的对象。
赋值运算符重载的特点 :
- 赋值运算符重载是一个运算符重载,必须重载为成员函数,他的参数必须是当前的类类型建议加上const,否则传值传参会有拷贝。
- 有返回值,建议写成当前类类型的引用,引用返回可以提高效率,有返回值的母体是为了可以连续赋值
- 没有显式实现时,编译器会⾃动⽣成⼀个默认赋值运算符重载,默认赋值运算符重载⾏为跟默认构造函数类似,对内置类型成员变量会完成值拷⻉/浅拷⻉(⼀个字节⼀个字节的拷⻉),对⾃定义类型成员变量会调⽤他的拷⻉构造
#include<iostream>using namespace std;
typedef int STDataType;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//编译报错 Date类的复制构造函数不能有Date类型的参数,但是可以有 Date& 以及 Date* 类型//Date(Date d)Date(Date& d){_year = d._year;_month = d._month;_day = d._day;}Date(Date* d){_year = d->_year;_month = d->_month;_day = d->_day;}void print(){cout << _year << '-' << _month << '-' << _day << endl;}Date& operator=(const Date& d1){if (this != &d1){_year = d1._year;_month = d1._month;_day = d1._day;}return *this;}
//private:int _year;int _month;int _day;
};
int main()
{Date d1(2024, 10, 06);Date d2(2024, 10, 07);//显示调用operator==(d1, d2);//编译器会转换成operator==(d1, d2)d1 == d2;d2.print();d2 = d1;d2.print();//注意这里不是赋值重载,而是拷贝构造,赋值重载只用于两个已经存在的对象,// 而拷贝构造用于一个对象初始化另一个对象Date d3 = d1;return 0;
}
相关文章:

【C++】--类和对象(2)
👌个人主页: 起名字真南 👆个人专栏:【数据结构初阶】 【C语言】 【C】 目录 1 类的默认成员函数2 构造函数3 析构函数4 拷贝构造5 赋值运算符重载5.1 运算符重载5.2 赋值运算符的重载 1 类的默认成员函数 默认成员函数就是用户没有显示实现,…...

最新BurpSuite2024.9专业中英文开箱即用版下载
1、工具介绍 本版本更新介绍 此版本对 Burp Intruder 进行了重大改进,包括自定义 Bambda HTTP 匹配和替换规则以及对扫描 SOAP 端点的支持。我们还进行了其他改进和错误修复。 Burp Intruder 的精简布局我们对 Burp Intruder 进行了重大升级。现在,您可…...

C++ 观察者模式
观察者模式(Observer Pattern)是一种行为设计模式,用于在对象之间建立一对多的依赖关系,当一个对象的状态发生变化时,它的所有依赖对象都会得到通知并自动更新。 在观察者模式中,主题和观察者之间是松耦合…...

基于pytorch的手写数字识别-训练+使用
import pandas as pd import numpy as np import torch import matplotlib import matplotlib.pyplot as plt from torch.utils.data import TensorDataset, DataLoadermatplotlib.use(tkAgg)# 设置图形配置 config {"font.family": serif,"mathtext.fontset&q…...

SpringBoot接收前端传递参数
1)URL 参数 参数直接 拼接在URL的后面,使用 ? 进行分隔,多个参数之间用 & 符号分隔。例如:http://localhost:8080/user?namezhangsan&id1后端接收(在Controller方法的参数列表中使用 RequestParam 注解&…...

【LeetCode周赛】第 418 场
3309. 连接二进制表示可形成的最大数值 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接 数组 nums 中所有元素的 二进制表示 ,请你返回可以由这种方法形成的 最大 数值。 注意 任何数字的二进制表示 不含 前导零 思路:暴力枚举 class Soluti…...

Android学习7 -- NDK2 -- 几个例子
学习 Android 的 NDK(Native Development Kit)可以帮助你用 C/C 来开发高性能的 Android 应用,特别适合对性能要求较高的任务,如音视频处理、游戏开发和硬件驱动等。下面是学习 NDK 的建议步骤和具体例子: ### 1. **准…...

问:说说JVM不同版本的变化和差异?
在Java程序的执行过程中,Java虚拟机(JVM)扮演着至关重要的角色。它不仅负责解释和执行Java字节码,还管理着程序运行时的内存。根据JVM规范,JVM将其所管理的内存划分为多个不同的数据区域,包括程序计数器、J…...

计算机毕业设计 基于Python的社交音乐分享平台的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档
🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…...

51单片机的水位检测系统【proteus仿真+程序+报告+原理图+演示视频】
1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块水位传感器继电器LED、按键和蜂鸣器等模块构成。适用于水位监测、水位控制、水位检测相似项目。 可实现功能: 1、LCD1602实时显示水位高度 2、水位传感器采集水位高度 3、按键可设置水位的下限 4、按键可手动加…...

Python和R及Julia妊娠相关疾病生物剖析算法
🎯要点 算法使用了矢量投影、现代优化线性代数、空间分区技术和大数据编程利用相应向量空间中标量积和欧几里得距离的紧密关系来计算使用妊娠相关疾病(先兆子痫)、健康妊娠和癌症测试算法模型使用相关性投影利用相关性和欧几里得距离之间的关…...

Web安全 - 重放攻击(Replay Attack)
文章目录 OWASP 2023 TOP 10导图1. 概述2. 重放攻击的原理攻击步骤 3. 常见的重放攻击场景4. 防御重放攻击的技术措施4.1 使用时效性验证(Time-Based Tokens)4.2 单次令牌机制(Nonce)4.3 TLS/SSL 协议4.4 HMAC(哈希消息…...

Python项目文档生成常用工具对比
写在前面: 通过阅读本片文章,你将了解:主流的Python项目文档生成工具(Sphinx,MkDocs,pydoc,Pdoc)简介及对比,本文档不涉及相关工具的使用。 概述 近期,由于…...

教育领域的技术突破:SpringBoot系统实现
2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…...

RabbitMQ入门3—virtual host参数详解
在 RabbitMQ 中,创建 Virtual Host 时会涉及到一些参数配置,比如 tags 和 Default Queue Type。下面是对这两个参数的详细解释: 1. Tags Tags 是 Virtual Host 的标记,用来为 Virtual Host 添加元数据,帮助你管理和组…...

【Nacos入门到实战十四】Nacos配置管理:集群部署与高可用策略
个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[2435024119qq.com] 📱…...

UE5+ChatGPT实现3D AI虚拟人综合实战
第11章 综合实战:UE5ChatGPT实现3D AI虚拟人 通过结合Unreal Engine 5(UE5)的强大渲染能力和ChatGPT的自然语言处理能力,我们可以实现一个高度交互性的AI虚拟人。本文将详细介绍如何在UE5中安装必要的插件,配置OpenAI…...

[图形学]smallpt代码详解(2)
一、简介 本文紧接在[图形学]smallpt代码详解(1)之后,继续详细讲解smallpt中的代码,包括自定义函数(第41到47行)和递归路径跟踪函数(第48到74行)部分。 二、smallpt代码详解 1.自…...

vmstat命令:系统性能监控
一、命令简介 vmstat 是一种在类 Unix 系统上常用的性能监控工具,它可以报告虚拟内存统计信息,包括进程、内存、分页、块 IO、陷阱(中断)和 CPU 活动等。 二、命令参数 2.1 命令格式 vmstat [选项] [ 延迟 [次数] ]2…...

linux部署NFS和autofs自动挂载
目录 (一)NFS: 1. 什么是NFS 2. NFS守护进程 3. RPC服务 4. 原理 5. 部署 5.1 安装NFS服务 5.2 配置防火墙 5.3 创建服务端共享目录 5.4 修改服务端配置文件 (1). /etc/exports (2). nfs.conf 5.5 启动nfs并加入自启 5.6 客户端…...

WPF RadioButton 绑定boolean值
<RadioButtonMargin"5"Content"替换"IsChecked"{Binding CorrectionOption.ReCorrectionMode}" /> <RadioButtonMargin"5"Content"平均"IsChecked"{Binding CorrectionOption.ReCorrectionMode, Converter{St…...

2024 ciscn WP
一、MISC 1.火锅链观光打卡 打开后连接自己的钱包,然后点击开始游戏,答题八次后点击获取NFT,得到有flag的图片 没什么多说的,知识问答题 兑换 NFT Flag{y0u_ar3_hotpot_K1ng} 2.Power Trajectory Diagram 方法1: 使用p…...

代码随想录--字符串--重复的子字符串
题目 给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。 示例 1: 输入: "abab" 输出: True 解释: 可由子字符串 "ab" 重复两次构成。示例 2: 输入: "…...

No.5 笔记 | 网络端口协议概览:互联网通信的关键节点
1. 常用端口速览表 端口范围主要用途1-1023系统或特权端口1024-49151注册端口49152-65535动态或私有端口 远程访问类(20-23) 端口服务记忆技巧安全风险21FTP"File Transfer Port"爆破、嗅探、溢出、后门22SSH"Secure Shell"爆破、…...

手机地址IP显示不对?别急,这里有解决方案
在当今的数字化生活中,手机已成为我们连接世界的重要工具。而手机的IP地址,作为我们在网络上的“身份证”,其准确性对于网络体验至关重要。然而,有时我们可能会遇到手机IP地址显示不正确的问题,这不仅会影响网络连接质…...

人工智能对未来工作影响的四种可能性
随着人工智能(AI)技术的迅速发展,其对人类工作的影响已成为讨论的热点话题。我们经常听到有关AI威胁论的观点,担心它将取代人类工作,但也有专家认为AI将成为一种辅助工具,帮助人类提升工作效率。宾夕法尼亚…...

SpringBoot+ElasticSearch7.12.1+Kibana7.12.1简单使用
案例简介 本案例是把日志数据保存到Elasticsearch的索引中,并通过Kibana图形化界面的开发工具给查询出来添加的日志数据,完成从0到1的简单使用 ElasticSearch职责用法简介 ElasticSearch用在哪 ElasticSearch在我这个案例中,不是用来缓解增…...

RESTful风格接口+Swagger生成Web API文档
RESTful风格接口Swagger生成Web API文档 文章目录 RESTful风格接口Swagger生成Web API文档1.RESTful风格接口RESTful简介RESTful详细图示常见http状态码springboot实现RESTfulRESTful springboot设计实例demo 2.Swagger生产Web API文档Swagger简介使用Swagger1.加入依赖2.配置S…...

性能测试学习2:常见的性能测试策略(基准测试/负载测试/稳定性测试/压力测试/并发测试)
一.基准测试 1)概念 狭义上讲:就是单用户测试。测试环境确定后,对业务模型中的重要业务做单独的测试,获取单用户运行时的各项性能指标。 广义上:是一种测量和评估软件性能指标的活动。可以在某个时刻通过基准测试建立…...

【C++】—— 继承(上)
【C】—— 继承(上) 1 继承的概念与定义1.1 继承的概念1.2 继承定义1.2.1 定义格式1.2.2 继承父类成员访问方式的变化 1.3 继承类模板 2 父类和子类对象赋值兼容转换3 继承中的作用域3.1 隐藏规则3.2 例题 4 子类的默认成员函数4.1 构造函数4.1.1 父类有…...