C++初阶:类和对象(二)

✨✨所属专栏:C++✨✨

✨✨作者主页:嶔某✨✨

类的默认成员函数

默认成员函数就是用户没有显式实现,编译器会⾃动⽣成的成员函数称为默认成员函数。⼀个类,我们不写的情况下编译器会默认⽣成以下6个默认成员函数,需要注意的是这6个中最重要的是前4个,最后两个取地址重载不重要,我们稍微了解⼀下即可。

  其次就是C++11以后还会增加两个默认成员函数,移动构造和移动赋值,这个我们后⾯再学习。默认成员函数很重要,也⽐较复杂,我们要从两个⽅⾯去学习:

• 第⼀:我们不写时,编译器默认⽣成的函数⾏为是什么,是否满⾜我们的需求?
• 第⼆:编译器默认⽣成的函数不满⾜我们的需求,我们需要⾃⼰实现,那么如何⾃⼰实现?

初始化和清理

构造函数

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象(我们常使⽤的局部对象是栈帧创建时,空间就开好了),⽽是对象实例化时初始化对象。构造函数的本质是要替代我们以前Stack和Date类中写的Init函数的功能,构造函数⾃动调⽤的特点就完美的替代的了Init。

1. 函数名与类名相同。
2. ⽆返回值。(返回值啥都不需要给,也不需要写void,C++规定如此)
3. 对象实例化时系统会⾃动调⽤对应的构造函数。
4. 构造函数可以重载。
5. 如果类中没有显式定义构造函数,则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数,⼀旦用户显式定义编译器将不再⽣成。
6. ⽆参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数,都叫做默认构造函数。但是这三个函数有且只有⼀个存在,不能同时存在。零参构造(不用传参数的构造函数) == 默认构造

class Date
{
public:
	// 1.⽆参构造函数(是默认构造)
	/*Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}*/
	// 2.带参构造函数(不是默认构造)
	/*Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}*/
	// 3.全缺省构造函数(是默认构造)
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

7. 我们不写,编译器默认⽣成的构造,对内置类型成员变量(int double char……)的初始化没有要求,也就是说是是否初始化是不确定的,看编译器。对于⾃定义类型成员变量(class struct……) ,要求调⽤这个成员变量的默认构造函数初始化。如果这个成员变量没有默认构造函数,那么就会报错。这时我们要初始化这个成员变量,需要⽤初始化列表才能解决。

typedef int STDataType;
class Stack
{
public :
	Stack(int n = 4)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败");
			return;
		} _
			capacity = n;
		_top = 0;
	}
private:
	STDataType * _a;
	size_t _capacity;
	size_t _top;
};
// 两个Stack实现队列
class MyQueue
{
public :
	//编译器默认⽣成MyQueue的构造函数调⽤了Stack的构造,完成了两个成员的初始化
private:
	Stack pushst;
	Stack popst;
};

析构函数

析构函数与构造函数功能相反,析构函数不是完成对对象本⾝的销毁,⽐如局部对象是存在栈帧的,函数结束栈帧销毁,他就释放了,不需要我们管,C++规定对象在销毁时会⾃动调⽤析构函数,完成对象中资源的清理释放⼯作。析构函数的功能类⽐我们之前Stack实现的Destroy功能,⽽像Date没有Destroy,其实就是没有资源需要释放,所以严格说Date是不需要析构函数的。

1. 析构函数名是在类名前加上字符~。
2. ⽆参数⽆返回值。(这⾥跟构造类似,也不需要加void)
3. ⼀个类只能有⼀个析构函数。若未显式定义,系统会⾃动⽣成默认的析构函数。
4. 对象⽣命周期结束时,系统会⾃动调⽤析构函数。
5. 和构造函数类似,我们不写编译器⾃动⽣成的析构函数对内置类型成员不做处理,⾃定类型成员会调⽤他的析构函数。
6. 还需要注意的是我们显示写析构函数,对于⾃定义类型成员也会调⽤他的析构,也就是说⾃定义类型成员⽆论什么情况都会⾃动调⽤析构函数。
7. 如果类中没有申请资源(malloc)时,析构函数可以不写,直接使⽤编译器⽣成的默认析构函数,如Date;如果默认⽣成的析构就可以⽤,也就不需要显⽰写析构,如MyQueue(调用了Stack的析构函数);但是有资源申请时,⼀定要⾃⼰写析构,否则会造成资源(内存)泄漏,如Stack。
8. ⼀个局部域的多个对象,C++规定后定义的先析构。

	~Stack()
	{
		free(_a);
		_a = nullptr;
		_capacity = _top = 0;
	}

拷贝复制

拷贝构造函数

如果⼀个构造函数的第⼀个参数是⾃⾝类类型的引⽤,且任何额外的参数都有默认值,则此构造函数也叫做拷⻉构造函数,也就是说拷⻉构造是⼀个特殊的构造函数。

1. 拷⻉构造函数是构造函数的⼀个重载
2. 拷⻉构造函数的参数只有⼀个且必须是类类型对象的引⽤,使⽤传值⽅式编译器直接报错,因为语法逻辑上会引发⽆穷递归调⽤。
3. C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造,所以这⾥⾃定义类型传值传参和传值返回都会调⽤拷⻉构造完成,所以这里会引发无穷递归。

	date(date d)//E0408	类 "date" 的复制构造函数不能带有 "date" 类型的参数
	{	
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

4. 若未显式定义拷⻉构造,编译器会⽣成⾃动⽣成拷⻉构造函数。⾃动⽣成的拷⻉构造对内置类型成员变量会完成值拷⻉/浅拷⻉(⼀个字节⼀个字节的拷⻉),对⾃定义类型成员变量会调⽤他的拷⻉构造。
5. 像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器⾃动⽣成的拷⻉构造就可以完成需要的拷⻉,所以不需要我们显⽰实现拷⻉构造。像Stack这样的类,虽然也都是内置类型,但是_a指向了资源,编译器⾃动⽣成的拷⻉构造完成的值拷⻉/浅拷⻉不符合我们的需求,所以需要我们⾃⼰实现深拷⻉(对指向的资源也进⾏拷⻉)。像MyQueue这样的类型内部主要是⾃定义类型Stack成员,编译器⾃动⽣成的拷⻉构造会调⽤Stack的拷⻉构造,也不需要我们显⽰实现
MyQueue的拷⻉构造。

	Stack(const Stack& s)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * s._capacity);
		if (_a != nullptr)
		{
			perror("maollc is fail");
			return;
		}
		memcpy(_a, s._a, sizeof(STDataType) * s._top);
		_capacity = s._capacity;
		_top = s._top;
	}

6. 传值返回会产⽣⼀个临时对象调⽤拷⻉构造,传值引⽤返回,返回的是返回对象的别名(引⽤),没有产⽣拷⻉。但是如果返回对象是⼀个当前函数局部域的局部对象,函数结束就销毁了,那么使⽤引⽤返回是有问题的,这时的引⽤相当于⼀个野引⽤,类似⼀个野指针⼀样。传引⽤返回可以减少拷⻉,但是⼀定要确保返回对象,在当前函数结束后还在,才能⽤引⽤返回。

赋值重载

运算符重载

• 当运算符被⽤于类类型的对象时,C++语⾔允许我们通过运算符重载的形式指定新的含义。C++规定类类型对象使⽤运算符时,必须转换成调⽤对应运算符重载,若没有对应的运算符重载,则会编译报错。
• 运算符重载是具有特名字的函数,他的名字是由operator和后⾯要定义的运算符共同构成。和其他函数⼀样,它也具有其返回类型和参数列表以及函数体。
• 重载运算符函数的参数个数和该运算符作⽤的运算对象数量⼀样多。⼀元运算符有⼀个参数,⼆元运算符有两个参数,⼆元运算符的左侧运算对象传给第⼀个参数,右侧运算对象传给第⼆个参数。
• 如果⼀个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数⽐运算对象少⼀个。
• 运算符重载以后,其优先级和结合性与对应的内置类型运算符保持⼀致。
• 不能通过连接语法中没有的符号来创建新的操作符:⽐如operator@。
.*     ::     sizeof    ?:(三目)   .    注意以上5个运算符不能重载。

class A
{ 
public :
void func()
{
    cout << "A::func()" << endl;
}
};
typedef void(A::* PF)(); //成员函数指针类型
int main()
{
	// C++规定成员函数要加&才能取到函数指针
	PF pf = &A::func;
	A obj;//定义A类对象obj
	// 对象调⽤成员函数指针时,使⽤.*运算符
	(obj.*pf)();
	return 0;
}


• 重载操作符⾄少有⼀个类类型参数,不能通过运算符重载改变内置类型对象的含义,如:

	int operator+(int x, int y)
	{
		return x - y;
	}

• ⼀个类需要重载哪些运算符,是看哪些运算符重载后有意义,⽐如Date类重载operator-(const date& d1,const date& d2)就有意义,但是重载operator+(const date& d1,const date& d2)就没有意义,重载operator+(const date& d1,int x)也是有意义的。
• 重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,⽆法很好的区分。
C++规定,后置++重载时,增加⼀个int形参(不传参数只作为区分),跟前置++构成函数重载,⽅便区分。

date& operator++()
{
    cout << "前置++" << endl;
    //...
    return *this;
}
date operator++(int)
{
    Date tmp;
    cout << "后置++" << endl;
    //...
    return tmp;
}

• 重载<<和>>时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第⼀个形参位
置,第⼀个形参位置是左侧运算对象,调⽤时就变成了对象<<cout,不符合使⽤习惯和可读性。
重载为全局函数把ostream/istream放到第⼀个形参位置就可以了,第⼆个形参位置当类类型对
象。

class date
{
	friend ostream& operator<<(ostream& out, const date& d);
private:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& out,const date& d)
{
	out << d._year << " " << d._month << " " << d._day << endl;
	return out;
}
赋值运算符重载

赋值运算符重载是⼀个默认成员函数,⽤于完成两个已经存在的对象直接的拷⻉赋值,这⾥要注意跟拷⻉构造区分,拷⻉构造⽤于⼀个对象拷⻉初始化给另⼀个要创建的对象。

1. 赋值运算符重载是⼀个运算符重载,规定必须重载为成员函数。赋值运算重载的参数建议写成
const当前类类型引⽤,否则会传值传参会有拷⻉
2. 有返回值,且建议写成当前类类型引⽤,引⽤返回可以提⾼效率,有返回值⽬的是为了⽀持连续赋值场景。
3. 没有显式实现时,编译器会⾃动⽣成⼀个默认赋值运算符重载,默认赋值运算符重载⾏为跟默认构造函数类似,对内置类型成员变量会完成值拷⻉/浅拷⻉(⼀个字节⼀个字节的拷⻉),对⾃定义类型成员变量会调⽤他的拷⻉构造。

	date& operator=(const date& d)
	{
		if (&d != this)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}

4. 像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器⾃动⽣成的赋值运算符重载就可以完成需要的拷⻉,所以不需要我们显⽰实现赋值运算符重载。像Stack这样的类,虽然也都是内置类型,但是_a指向了资源,编译器⾃动⽣成的赋值运算符重载完成的值拷⻉/浅拷⻉不符合我们的需求,所以需要我们⾃⼰实现深拷⻉(对指向的资源也进⾏拷⻉)。像MyQueue这样的类型内部主要是⾃定义类型Stack成员,编译器⾃动⽣成的赋值运算符重载会调⽤Stack的赋值运算符重载,也不需要我们显⽰实现MyQueue的赋值运算符重载。

	Stack& operator=(const Stack& s)
	{
		if (this != &s)
		{
			memcpy(_a, s._a, sizeof(STDataType) * s._top);
			_capacity = s._capacity;
			_top = s._top;
		}
		return *this;
	}

取地址重载

const成员函数

• 将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后
⾯。
const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进⾏修改
const修饰Date类的Print成员函数,Print隐含的this指针由 Date* const this 变为 const Date* const this    C语言指针从入门到基础详解(指针常量&常量指针)

//void Print(const Date* const this) const
void Print() const
{
    cout << _year << "-" << _month << "-" << _day << endl;
}

取地址运算符重载

取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器⾃动⽣成的就可以够我们⽤了,不需要去显⽰实现。除⾮⼀些很特殊的场景,⽐如我们不想让别⼈取到当前类对象的地址,就可以⾃⼰实现⼀份,胡乱返回⼀个地址。

class Date
{
public :
	Date * operator&()
	{
		 return this;
		// return nullptr;
		// return (Date*)00267011;
	}
	const Date * operator&() const
	{
		 return this;
		// return nullptr;
		// return (Date*)00267011;
	}
private:
	int _year; // 年
	int _month; // ⽉
	int _day; // ⽇
};

 本期博客到这里就结束了,如果有什么错误,欢迎指出,如果对你有帮助,请点个赞,谢谢!

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

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

相关文章

报文对比工具

如果有报文对比需求&#xff0c;可以通过以下步骤实现&#xff1a; ①通过在线 XML排序、压缩、格式化 网站 排序后格式化数据 http://www.bejson.com/otherformat/xmlsort/ 访问速度快&#xff0c;操作直观&#xff0c; 1.原始xml数据 2.排序 3.格式化 ②N比对数据是否一…

哥德巴赫猜想c++

方法一 #include<bits/stdc.h> using namespace std; //定义函数&#xff0c;判断素数 bool sushu(int n){bool rtrue;//先假设是素数&#xff0c;即真//循环因子范围&#xff0c;找到一个因子就不是素数for(int i2;i<sqrt(n);i){//判断2~n的根号是否素数if(n%i0){//…

翁恺-C语言程序设计-05-3. 求a的连续和

05-3. 求a的连续和 输入两个整数a和n&#xff0c;a的范围是[0,9]&#xff0c;n的范围是[1,8]&#xff0c;求数列之和S aaaaaa…aaa…a&#xff08;n个a&#xff09;。如a为2、n为8时输出的是222222…22222222的和。 输入格式&#xff1a; 输入在一行中给出两个整数&#xf…

Hdfs3.x新特性详解

作者&#xff1a;九月 HDFS Disk Balancer(磁盘均衡器) HDFS Disk Balancer与HDFS Balancer的区别 两者都是实现负载均衡功能。 HDFS Balancer是之前Hadoop2.x中本身存在的&#xff0c;主要是多个DataNode节点之间的数据的平衡。 HDFS Disk Balancer是Hadoop3中新出现的&…

融云:换头像=换人设?社交应用中隐秘而重要的「用户信息管理」

当代年轻人失眠三大原因&#xff0c;最近新上的《喜人奇妙夜》帮你找到了—— 基金绿了、吵架输了、前任头像换了。 当你半夜翻看前任的社交账号&#xff0c;一场盛大的失眠就开始了&#xff0c;就算古希腊掌柜睡眠的神躺你旁边也不好使。即便 Ta 没有更新内容&#xff0c;昵…

Linux RTL8111/RTL8168 不能联网 / 最新版驱动下载安装

注&#xff1a; 机翻&#xff0c;未校对。 如何让 Realtek RTL8111/RTL8168 在 Linux 下工作 这篇文章于 2016 年 8 月在我原来的博客上发布。尽管如今 Linux 下的 RTL8111/RTL8168 网络接口的情况变得越来越稳定&#xff0c;但它们仍然会导致数据包丢失或网络连接不稳定等问题…

C1W1.Assignment: Logistic Regression

理论课&#xff1a;C1W1.Sentiment Analysis with Logistic Regression 文章目录 前期准备导入包导入数据处理推文文本 Part 1: Logistic regressionPart 1.1: Sigmoid实现 sigmoid 函数Logistic regression: regression and a sigmoid Part 1.2 Cost function and GradientUp…

自动驾驶-端到端分割任务

上采样 bed of nails interpolation transposed convolutions 1. 上采样 (Upsampling) 上采样是一种技术&#xff0c;用于增加数据集中的样本数量或是提高信号的分辨率。在图像处理中&#xff0c;上采样通常指的是增加图像的像素数量&#xff0c;从而使图像变得更大。这可…

【Android安全】Ubuntu 下载、编译 、刷入Android-8.1.0_r1

0. 环境准备 Ubuntu 16.04 LTS&#xff08;预留至少95GB磁盘空间&#xff0c;实测占94.2GB&#xff09; Pixel 2 XL 要买欧版的&#xff0c;不要美版的。 欧版能解锁BootLoader、能刷机。 美版IMEI里一般带“v”或者"version"&#xff0c;这样不能解锁BootLoader、…

Android之间互传消息之ServerSocket,Android服务端接收Socket发送的TCP

Android之间在在局域网下互传消息&#xff0c;咱就不用走云服务器了吧&#xff0c;让俩安卓设备&#xff0c;自己传呗 方式1 通过在安卓设备上搭建Web服务器接收数据&#xff0c;可参考 Android使用AndServer在安卓设备上搭建服务端(Java)(Kotlin)两种写法 方式2 本文章&…

空安全编程的典范:Java 8中的安全应用指南

文章目录 一、Base64 编码解码1.1 基本的编码和解码1.2 URL 和文件名安全的编码解码器1.3 MIME Base64编码和解码 二、Optional类三、Nashorn JavaScript 一、Base64 编码解码 1.1 基本的编码和解码 Base64 编码&#xff1a; 使用 Base64.getEncoder().encodeToString(origin…

【STM32嵌入式系统设计与开发---拓展】——1_8_寄存器的理解

1、寄存器的理解 &#xff08;1&#xff09;MOS管 MOS管是一种场效应晶体管&#xff0c;通过控制栅极电压来调节漏极和源极之间的电流&#xff0c;常用于电子开关和放大器电路中。 MOS管就像是电子开关&#xff0c;可以通过控制一个小电压来打开或关闭一个大电流&#xff0c;常…

小程序-1(项目结构+代码结构+宿主环境+组件)

目录 1.小程序简介 2.小程序的项目结构 小程序的基本组成结构 小程序的页面组成部分 json配置文件的作用 app.json文件 project.config.json文件 sitemap.json文件 页面的.json文件 新建小程序页面 修改项目首页 3.小程序的代码结构 wxml和html的区别 wxss和css的…

数据结构(Java):LinkedList集合Stack集合

1、集合类LinkedList 1.1 什么是LinkedList LinkedList的底层是一个双向链表的结构&#xff08;故不支持随机访问&#xff09;&#xff1a; 在LinkedList中&#xff0c;定义了first和last&#xff0c;分别指向链表的首节点和尾结点。 每个节点中有一个成员用来存储数据&…

postgresql简单导出数据与手动本地恢复(小型数据库)

问题 需要每天手动备份postgresql。 步骤 导出数据 /opt/homebrew/opt/postgresql16/bin/pg_dump --file/Users/zhangyalin/backup_sql/<IP地址>_pg-2024_07_15_17_30_15-dump.sql --dbname<数据库名> --username<用户名> --host<IP地址> --port54…

Python array的特点及使用

1、Python array的特点及使用 1.1、python array为什么只能接收指定类型数据 array 模块提供了一种叫做 array 的数据结构&#xff0c;它表示一块连续的内存空间&#xff0c;所有的元素必须是相同的类型。这是因为在内存中&#xff0c;数组元素存储在连续的位置上&#xff0c…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 游乐园门票 (200分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; 最新华为O…

数据结构java笔记

线性表 数组 物理地址连续、逻辑地址连续。数组长度是固定的&#xff0c;不能动态增长或缩小&#xff0c;数组中元素的类型相同&#xff08;适合用于元素个数固定&#xff0c;且快速用下标访问&#xff09; ArrayList&#xff08;动态数组&#xff09; 物理地址连续、逻辑地…

【C++练级之路】【Lv.26】类型转换

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《算法神殿》《数据结构世界》《进击的C》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 一、C风格类型转换1.1 隐式类型转换1.2 显式类型转换 二、C风格类型转换2.1 static_cast2.2 dynamic_cast2.3…

【C++】—— 初识C++

【C】—— 初识C 一、什么是 C二、C 的发展历史三、C 版本更新四、C 的重要性五、C 在工作领域中的运用六、C 书籍推荐&#xff1a; 一、什么是 C C语言 是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对于复杂的问题&#xff0c;规模较大的程序&#xff0c;需要…