C++面向对象(中)

news2024/9/25 7:14:46

文章目录

  • 前言
  • 1.类的6个默认成员函数介绍
  • 2.构造函数
  • 3.析构函数
    • 1.概念
    • 2.析构函数特征
  • 4.拷贝构造
    • 1.概念
    • 2.拷贝构造函数特征
    • 3.注意事项
  • 5.赋值运算符重载
    • 1.概念
  • 6.补充知识const成员函数
  • 7.取地址运算符和const取地址运算符重载
  • 8.总结

前言

本文主要介绍C++中的六个天选之子,也就是类中的6个默认成员函数。它们分别:构造函数 析构函数 拷贝构造函数 赋值运算符重载 取地址及const取地址操作符重载。本文主要围绕前4个进行介绍,后面两个基本上不太重要也不太需要我们自己去实现。


1.类的6个默认成员函数介绍

类的6个默认成员函数如果一个类中什么成员都没有,简称为空类。空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数

在这里插入图片描述


2.构造函数

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
其特征如下:
1. 函数名与类名相同。
2. 无返回值。
3. 对象实例化时编译器自动调用对应的构造函数(隐式调用).
4. 构造函数可以重载。

代码示例
在这里插入图片描述


因为构造函数是天选之子如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

我们来看这样的代码观察一下
在这里插入图片描述

看到这里我们不仅有个疑问:编译器默认生成的构造函数初始化为啥打印a的成员变量是随机值呢?看起来默认构造函数好像没啥用。其实C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char/各种类型的指针…,自定义类型就是我们使用class/struct/union等自己定义的类型,对于自定义类型编译器生成的默认构造函数会调用该类型的默认构造函数,对于内置类型不做处理。之前我们栈相关练习的博客中结束过一道题用两个栈实现队列,假如我们自定义首先了栈类,这个时候实现队列类的时候,就只用编译器默认生成的默认构造函数即可。

注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值,相当于给成员变量缺省值。

在这里插入图片描述


关于默认构造函数的概念简单说一下:无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。简单来说不用传参数的构造函数就是默认构造函数。

如果自己实现默认构造函数的话一般建议采用全缺省的形式


3.析构函数

1.概念

概念通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的?析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

伪代码示例
在这里插入图片描述

在C语言中我们每次使用完栈后需要手动调用销毁函数释放空间,这太麻烦了,完一哪天忘记释放了就会造成内存泄漏,C++的祖师爷创造了析构函数编译器自动调用来清理对象中的资源。

2.析构函数特征

析构函数是特殊的成员函数,其特征如下:
1. 析构函数名是在类名前加上字符 ~。
2. 无参数无返回值类型。
3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

当我们没有自己实现析构函数时,编译器也会生成默认的析构函数。编译器默认生成的析构的函数会对自定义类型会调用该类型的析构函数,内置类型不做处理。如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类.


4.拷贝构造

1.概念

在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?这个时候就用到了拷贝构造函数。拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。


2.拷贝构造函数特征

拷贝构造函数也是特殊的成员函数,其特征如下:
1. 拷贝构造函数是构造函数的一个重载形式。
2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。

为什么会引发无穷递归呢?当我们自己实现拷贝构造的时候,需要传参。如果是传值传参这个参数是类类型的,是类类型的就需要调用拷贝构造将类对象赋值给形参初始化。但是拷贝构造还没实现,所以就会发生实现拷贝构造需要用到拷贝构造,这就是递归死循环了,所以只能引用传参。

若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。(对于类中成员变量有类类型时,该类的拷贝构造函数必须存在,这样编译器才能正常调用默认生成的拷贝构造函数)。

3.注意事项

对于一些简单的类,类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。对于某些类来说浅拷贝会引发一些问题。

在这里插入图片描述
在上图的情况下就要自己写拷贝构造函数了,给出一下简单的伪代码示例

class stack
{   
public:
	stack(const stack& st)
	{  
		_a = (int*)malloc(sizeof(int) * st._top);
		if (_a == nullptr)
		{
			exit(-1);
		}
		memcpy(_a, st._a, st._top);
		_top = st._top;
	}
private:
	int* _a;
	int _top;
};

拷贝构造函数典型调用场景:1.使用已存在对象创建新对象 2.函数参数类型为类类型对象 3.函数返回值类型为类类型对象.简单来说就是用已存在的对象初始化新对象,函数传值传参或者函数返回值类型是类类型时。为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用尽量使用引用。


5.赋值运算符重载

1.概念

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有返回值,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。注意:不能通过连接其他符号来创建新的操作符:比如operator@ 重载操作符必须有一个类类型参数用于内置类型的运算符,其含义不能改变,如:内置的整型+,不能改变其含义。作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this;(.* :: sizeof ?: .) 注意以上5个运算符不能重载。

代码示例

#include<iostream>
using namespace std;
class Date
{  public:
	void Print();
	Date& operator=(const Date& d)
	{  //为了符合操作符=连续赋值 返回值是this
		_year = d._year;
		_month = d._month;
		_day = d._day;
		return *this;
	}
	Date(int year=1,int month=1,int day=1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
void Date::Print()
{
	cout << _year <<" "<< _month<<" " << _day << endl;
}
int main()
{   
	Date d1(2020, 2, 2);
	Date d2(2020, 1, 1);
	//调用赋值运算符重载
	d2 = d1;
	d2.Print();
	d1.Print();


}

赋值运算符重载格式
参数类型:const T&,传递引用可以提高传参效率
返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值检测是否自己给自己赋值
返回*this :要复合连续赋值的含义上述代码示例中为了更加符合=操作符的功能,用返回值传为引用支持连续赋值。

C++为什么要支持运算符重载呢?我们知道编译器对语法规定的内置类型能够用操作符处理的很好,但是如果对一些复杂对象也需要用到操作符该怎么办呢?为了处理这样的场景C++中产生了运算符重载。

注意事项:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。和拷贝构造函数类似:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。

拷贝构造和赋值运算符的区别在于:一个是对象初始化的时候调用,另一个是对已经存在的对象进行赋值。

伪代码示例

   Date d1(2020, 2, 2);
	//拷贝构造
	Date d2=d1;
	Date d3(2000, 1, 1);
	//赋值运算符重载
	d1 = d3;

6.补充知识const成员函数

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。其实准确来说const修饰的*this.

为什么会有const成员函数呢?看如下代码示例
在这里插入图片描述

我们看到程序是会发生报错的,为啥会发生这样的问题呢?d1是被const修饰的,但是在调用类中函数Print函数时,this指针是没有被const修饰的,相当于this指针权限被放大了,就会引发错误,C++语法规定在函数形参列表后面加上cosnt用于修饰隐藏的this指针, * this才是指向的对象准确来说const修饰的是*this。

在这里插入图片描述

在这里插入图片描述


对于一些不与需要访问类成员的成员函数我们都可以用const修饰,这样对于const修饰的对象也能正常调用成员函数。

1. const对象可以调用非const成员函数吗?
2. 非const对象可以调用const成员函数吗?
3. const成员函数内可以调用其它的非const成员函数吗?
4. 非const成员函数内可以调用其它的const成员函数吗?
1.const对象不能调用非const成员函数
2、非const对象可以调用const成员函数
3、const成员函数不能调用其它的非const成员函数
4、非const成员函数可以调用非const成员函数
这一切都源于权限不能被放大只能被缩小

7.取地址运算符和const取地址运算符重载

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容!

代码示例
在这里插入图片描述


8.总结

以上便是对C++中6个天选之子进行简单简单介绍,关于运算符重载后续将会写一个日期类将详细介绍。以上内容如有问题,欢迎指正,谢谢!

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

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

相关文章

MicroBlaze系列教程(6):AXI_IIC的使用

文章目录 @[toc]AXI_IIC简介MicroBlaze硬件配置常用函数使用示例波形实测参考资料工程下载本文是Xilinx MicroBlaze系列教程的第6篇文章。 AXI_IIC简介 一般情况下,使用FPGA实现I2C协议主要有两种方式:一种是基于Verilog实现起始位、停止位、ACK产生和判断、数据的发送和接收…

程终止、进程睡眠、进程对信号处理过程中等的方法

上一章学习了调度的方式&#xff0c;分为主调度器和周期性调度器&#xff0c;明白了进程切换分为自愿(voluntary)和强制(involuntary)两种。 自愿切换&#xff1a; 是指任务由于等待某种资源&#xff0c;将state改为非running状态后&#xff0c;主动调用schedule让出CPU 任务…

html中元素居中的五种方法

在网页开发中&#xff0c;经常会有嵌套元素中将子元素居中的要求。下边将五种常用的居中方法进行总结。 1&#xff1a;原始图&#xff08;父子元素无border&#xff0c;无padding&#xff09;&#xff1a; 2&#xff1a;实现居中效果&#xff1a; 一&#xff1a;使用margin…

一篇文章带你学会Anisble中的如何处理失败任务

目录 一、循环 1、简单循环 2、循环散列或字典列表 3、练习 二、条件 三、触发器 四、处理失败任务 1、ignore_errors 2、force_handlers 3、changed_when 4、failed_when 5、block 练习 一、循环 作用&#xff1a;循环迭代任务 1、简单循环 loop: ##赋值列表 -…

[软件工程导论(第六版)]第4章 形式化说明技术(复习笔记)

文章目录4.1 概述4.2 有穷状态机4.3 Petri网4.4 Z语言按照形式化程度&#xff0c;可以把软件工程使用的方法划分成非形式化、半形式化、形式化三类非形式化方法&#xff1a;使用自然语言描述需求规格说明半形式化方法&#xff1a;使用数据流图或实体-联系图建立模型形式化方法&…

P2P视频聊天技术分析

整个P2P视频过程需要知道双方的媒体类型、流和候选者&#xff0c;所以这里就会用到一下技术&#xff1a; ​ 信令服务器socket.io ​ 状态机 ​ ICE服务器 ​ WebRTC框架 ​ 媒体协商 信令服务器Socket.io 信令服务器说白了作用就是发消息的中转站&#xff0c;A把msg发到…

网络流与图(二)

上一节我们讲到了退化圈方向搜索算法&#xff0c;它能得到全局最优解。然而算法运行过程中需要选择一个可行改进圈方向&#xff0c;对于一个大型网络流来说&#xff0c;这并非容易的。我们需要找到在每次循环中确认可行改进圈方向或者证明不存在的方法。我们现在就来探讨这个问…

Andriod入门级开发

这学期有个课设&#xff0c;我们组我负责一个手机APP的开发&#xff0c;虽然刚开始说要实现什么智能导航&#xff0c;类似高德地图那种&#xff0c;但最后阉割的只剩一个Socket通信了&#xff0c;因为之前没有接触过&#xff08;可能之后也不会再接触&#xff09;&#xff0c;记…

【数据管理】谈谈哈希原理和散列表

一、说明 提起哈希&#xff0c;有人要说&#xff1a;不就是一个稀疏表格么&#xff0c;谈的上什么原理&#xff1f;我说&#xff1a;非也&#xff0c;哈希是是那种看似无物&#xff0c;其实解决大问题的东西。如何提高数据管理效率&#xff1f;这是个问题&#xff0c;随着这个问…

测试2:编写测试用例的方法

2.编写测试用例的方法 7种 测试常用的方法&#xff1a;code review 代码静态分析、CI/CD CI–持续集成–开发成员经常集成它们的工作&#xff0c;尽快发现集成错误 CD–持续部署–将集成后的代码部署到更贴近真实运行的环境 2.1 测试用例的描述&#xff1a; 用例编号 用例…

Python纯Numpy手撕SGD

文章目录简介问题建模数据加载和预处理数据加载预处理分batch损失函数训练运行简介 本博客用多元线性回归展示如何从零实现一个随机梯度下降SGD, 不使用torch等AI框架 问题建模 给定一个数据集X∈RN(D1)\large X \in \R^{N \times (D1)}X∈RN(D1)和对应标签向量Y∈RN\large …

centos7防火墙工具firewall-cmd使用

centos7防火墙工具firewall-cmd使用防火墙概述centos7防火墙工具firewall-cmd使用介绍firewalld的基本使用服务管理工具相关指令配置firewalld-cmd防火墙概述 防火墙是可以帮助计算机在内部网络和外部网络之间构建一道相对隔绝的保护屏障&#xff0c;从而保护数据信息的一种技…

Vulnhub 渗透练习(七)—— FRISTILEAKS: 1.3

环境搭建 下载链接 virtualbox 打开靶机设置为 host-only&#xff0c;攻击机同样。 具体可点此处 信息收集 开了个 80 端口。 用的是 apache 2.2.15 &#xff0c;这个版本有个解析漏洞。 目录 根据首页的图片猜测 /fristi/ 目录&#xff08;不过我没想到 -_-&#x…

由浅入深掌握各种 Python multiprocessing 进程间通信方式

由浅入深掌握各种 Python 多进程间通信方式1、为什么要掌握进程间通信2、进程间各类通信方式简介3、消息机制通信1) 管道 Pipe 通信方式2) 消息队列Queue 通信方式4、同步机制通信(1) 进程间同步锁 – Lock(2) 子进程间协调机制 -- Event5、共享内存方式通信(1) 共享变量(2) 共…

【Python】控制自己的手机摄像头拍照,并自动发送到邮箱

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 今天这个案例&#xff0c;就是控制自己的摄像头拍照&#xff0c; 并且把拍下来的照片&#xff0c;通过邮件发到自己的邮箱里。 想完成今天的这个案例&#xff0c;只要记住一个重点&#xff1a;你需要一个摄像头 思路…

Android 7.0 OTA升级(高通)

文章目录1. Full OTA 方式升级介绍1.1 Full OTA 制作第一步&#xff1a;生成 msm89xx-target_files-eng.XXX.zip1.2 Full OTA 制作第二步&#xff1a;Modem 等非 HLOS 加入升级包的方法1.3 Full OTA 制作第三步&#xff1a;生成 update.zip 升级包2. Incremental OTA 方式升级介…

Android 基础知识4-2.6LinearLayout(线性布局)

一、LinearLayout的概述 线性布局&#xff08;LinearLayout&#xff09;主要以水平或垂直方式来排列界面中的控件。并将控件排列到一条直线上。在线性布局中&#xff0c;如果水平排列&#xff0c;垂直方向上只能放一个控件&#xff0c;如果垂直排列&#xff0c;水平方向上也只能…

Java基础-xml

1.xml 1.1概述 万维网联盟(W3C) 万维网联盟(W3C)创建于1994年&#xff0c;又称W3C理事会。1994年10月在麻省理工学院计算机科学实验室成立。 建立者&#xff1a; Tim Berners-Lee (蒂姆伯纳斯李)。 是Web技术领域最具权威和影响力的国际中立性技术标准机构。 到目前为止&#…

python基础语法【自用】

✨始发站&#x1f6a9;Python的基础语法&#xff0c;冲冲冲&#xff01; &#x1f6a9;注&#xff1a;本篇为python基础语法篇&#xff0c;因博主之前使用java&#xff0c;所以本基础语法篇实为自用丐版&#xff01; &#x1f332; 你好&#xff0c;世界&#xff01; 安装环境…

虚拟机快照

1. 快照有什么作用&#xff1f; 通俗理解&#xff1a;快照就是备份。 2. VMware Workstation 和 VMware Fusion 都支持制作快照去使用 一、快照 保存当前虚拟机状态。可以恢复 二、 在VMware Workstation Pro中制作并还原快照 三、在VMware Fusion Pro中制作并还原快照 快照制…