C++从零到满绩——类和对象(中)

目录

1>>前言

2>>构造函数(我称之为初始化函数)

3>>析构函数(我称之为销毁函数)

4>>拷贝构造函数(我称之为复制函数)

5>>运算符重载

5.2>>赋值运算符重载

​编辑

6>>结语

1>>前言

上节课学习了基础篇(包括函数重载、引用、inline、nullptr)and类和对象(上)(包括类的定义、实例化、this指针),忘记的宝子们可以去复习一下~~~

枫の大一-CSDN博客C++从零到满绩------入门基础and类和对象(上)-CSDN博客 跳转上篇

枫の大一-CSDN博客 跳转个人主页查看

今天我们来类和对象(中)(包括:构造函数、析构函数、拷贝构造函数、赋值运算符重载)。需要注意的是今天我们学习的四个内容 始终 带着两个疑问:

第一:我们不写时,编译器默认生成的函数行为是什么,是否满足我们的需求?

第二:编译器默认生成的函数不满足我们的需求,我们需要自己实现,那么如何自己实现?

ps:如果有觉得小编哪里需要改进的欢迎指出,做出完美的C++篇章肯定少不了各位精神股东的支持啦,谢谢大家~

2>>构造函数(我称之为初始化函数)

构造函数虽然有构造二字,但它并非开空间造对象,而是在对象实例化时初始化对象。它的本质就是替代之前写的init初始化函数,并且它还能自动调用,简直perfect!

构造函数的特点:

1.函数名域类名一样。

2.无返回值。没有void!!!

3.对象实例化时会自动调用。

4.构造函数可重载。

5.如果类中没有显式定义(我们自己写的)构造函数,编译器会自动生成无参默认构造函数。

6.默认构造包括:无参构造函数、全缺省构造函数、编译器默认生成的构造函数。简单说就是不需要我们传实参就是默认构造。

7.编译器默认生成的构造函数,对于内置类型初始化不确定,看编译器;对于自定义类型成员变量,要求调用这个成员变量的默认构造函数进行初始化。类似于高数的链式求导,y不能对x求导,可以对t求导,t在对x求导。

cpp 复制代码
#include<iostream>
using namespace std;
class Date {
public://公共
	void print() {//打印函数
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private://私有
	int _year;
	int _month;
	int _day;
};
int main() {
	Date d1;
	d1.print();
	return 0;
}

不写构造函数运行结果为随机值

cpp 复制代码
#include<iostream>
using namespace std;
class Date {
public://公共
	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(2024, 11, 24);
	d1.print();
	return 0;
}

写了构造函数对变量d1进行了初始化操作,还用到了上节课的缺省参数

d1这里后面什么都不写

cpp 复制代码
Date d1;

运行结果为1/1/1,因为缺省参数帮助初始化了。

可以发现我们没有进行调用,而是在实质化对象的时候自动调用,这就是它的好用之处。这就是构造函数的使用。

3>>析构函数(我称之为销毁函数)

析构函数功能与构造函数相反,听名字可以理解为解析构造,将构造函数一步步剖析。它的工作是完成对象中资源的清理释放工作。类似与之前栈使用的Destroy功能。

析构函数的特点:

1.析构函数名是在类名前加上字符~。~本身也是按位取反的意思,这里代表相反。

2.无参数无返回值。不需要void!!!

3.一个类只能有一个析构函数。

4.对象生命周期结束时,系统会自动调用析构函数。

  1. 自定义类型的成员变量都会调用析构函数(不管是写的还是默认生成的)。

6.若类无申请资源,析构可以不写,如Date;若类都是自定义成员变量,它们都有自己的析构,也可以不写,如用两个栈实现队列的MyQueue;若有申请资源,一定要写,如用两个栈实现队列的Stack。

7.若有多个对象,后定义的先析构。

cpp 复制代码
#include<iostream>
using namespace std;
typedef int StDataType;
class Stack {
public://公共
	Stack(int n=4) {//构造函数
		_arr = (StDataType*)malloc(sizeof(StDataType) * n);
		if (nullptr == _arr)
		{
			perror("malloc获取失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}
	
	~Stack() {//析构函数
		free(_arr);
		_arr = nullptr;
		_capacity = _top = 0;
	}
private://私有
	StDataType* _arr;
	int _capacity;
	int _top;
};
int main() {
	Stack st;
	return 0;
}

4>>拷贝构造函数(我称之为复制函数)

拷贝构造函数第一个参数时自身类型的引用,其他参数都是缺省参数。

拷贝构造的特点:

1.拷贝构造函数是构造函数的重载。

2.拷贝构造参数第一个必须是自身类型的引用。

3.自定义类型对象进行拷贝时必须调用拷贝构造,因此传值传参和传值返回的行为都会调用拷贝构造。

4.若未自己写,编译器会自动生成。自动生成的拷贝构造是值拷贝/浅拷贝(一个字节一个字节拷贝),自定义成员变量会调用它的拷贝构造。

5.需要我们写析构的也要写拷贝构造,例如Stack。因为它有申请资源,浅拷贝会导致两块st1和st2都指向同一块空间,因此要进行深拷贝,所以要自己写。

6.传值返回会调用拷贝构造,传值"引用"返回没产生拷贝,因此不需要调用。但是如果返回的引用时一个局部对象,在函数结束时销毁,那么引用会变成野引用,那就不行。传引用返回前提是函数结束后还在。

cpp 复制代码
#include<iostream>
using namespace std;
class Date {
public://公共
	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(2024,11,24);
	Date d2(d1);
	d1.print();
	d2.print();
	return 0;
}

用栈的拷贝构造:

cpp 复制代码
//拷贝构造
#include<iostream>
using namespace std;
typedef int StDataType;
class Stack {
public://公共
	Stack(int n = 4) {//构造函数
		_arr = (StDataType*)malloc(sizeof(StDataType) * n);
		if (nullptr == _arr)
		{
			perror("malloc获取失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}

	Stack(const Stack& source) {//拷贝构造函数
		//需要再开辟一个同样大小空间
		_arr = (StDataType*)malloc(sizeof(StDataType) * source._capacity);//乘上之前空间大小
		if (nullptr == _arr)
		{
			perror("malloc获取失败");
			return;
		}
		memcpy(_arr, source._arr, sizeof(StDataType) * source._top);//复制有效数据
		_capacity = source._capacity;
		_top = source._top;
	}

	~Stack() {//析构函数
		free(_arr);
		_arr = nullptr;
		_capacity = _top = 0;
	}
private://私有
	StDataType* _arr;
	int _capacity;
	int _top;
};
int main() {
	Stack st;
	Stack st2(st);
	return 0;
}

5>>运算符重载

1.这里重载和之前的函数重载区分开,并不是一个意思。

2.当两个类类型对象进行运算符操作时,不能直接操作,需要我们写一个对应的运算符重载函数才可以。

3.它由operator和定义的运算符构成。

4.它的参数个数和运算对象一样多。

5.如果一个重载运算符函数是成员函数,那它少一个参数,因为有隐藏的this指针。

6.特别注意5个运算符不能重载:(.* :: sizeof :? .)

7.至少有一个类类型参数。

cpp 复制代码
//运算符重载
#include<iostream>
using namespace std;
class Date {
public://公共
	Date(int year = 1, int month = 1, int day = 1) {//构造函数
		_year = year;
		_month = month;
		_day = day;
	}

	bool operator==(const Date& d2) {//重载格式:operator符号
		return _year == d2._year
			&& _month == d2._month
			&& _day == d2._day;
	}
	void print() {//打印函数
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private://私有
	int _year;
	int _month;
	int _day;
};
int main() {
	Date d1(2024,11,24);
	Date d2(d1);
	int r= d1 == d2;
	cout << r;
	return 0;
}

5.2>>赋值运算符重载

跟拷贝构造区分开,它是两个已经存在的对象进行赋值。

赋值运算符重载特点:

1.必须重载为成员函数,建议带上const。

2.有返回值,写当前类类型的引用,提高效率。

cpp 复制代码
//赋值运算符重载
#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& operator=(const Date& d2) {//重载格式:operator符号
		//跳过自己给自己赋值
		if (this != &d2) {
			_year = d2._year;
			_month = d2._month;
			_day = d2._day;
		}
		//因为d1=d2返回的是d1,而*this也是d1,所以返回*this
		return *this;
	}
	void print() {//打印函数
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private://私有
	int _year;
	int _month;
	int _day;
};
int main() {
	Date d1(2024, 11, 24);
	Date d2(2024, 11, 299);
	d1 = d2;
	d1.print();
	d2.print();
	return 0;
}

6>>结语

今日份C++入门就先到这里啦,来总结一下:主要讲了类和对象(中)(包括:构造函数、析构函数、拷贝构造函数、赋值运算符重载),感兴趣的宝子们欢迎持续订阅小编,小编在这里谢谢宝子们啦~C++的学习很陡,时而巨难时而巨简单,希望宝子和小编一起坚持下去~你们的三连是我的动力,感谢支持~

相关推荐
techdashen11 分钟前
Go与黑客(第四部分)
开发语言·后端·golang
宇宙大豹发17 分钟前
【Python】爬虫实战:高效爬取电影网站信息指南(涵盖了诸多学习内容)
开发语言·爬虫·python·学习·python爬虫·python代码·python使用
蓝桉柒723 分钟前
web前端开发--动画效果
开发语言·前端·css
芥末虾25 分钟前
【优选算法】KMP模式匹配算法 {算法介绍;算法原理:核心原理,如何求next数组;代码实现}
c语言·c++·算法·kmp·字符串模式匹配
薔薇十字31 分钟前
【代码随想录day36】【C++复健】1049. 最后一块石头的重量 II ; 494. 目标和 ;474.一和零
c++·算法·leetcode
hummhumm33 分钟前
第 32 章 - Go语言 部署与运维
java·运维·开发语言·后端·python·sql·golang
techdashen34 分钟前
Go与黑客(第二部分)
开发语言·后端·golang
阿阿越39 分钟前
数据结构进阶(C++) -- AVL树的实现
数据结构·c++
LightOfNight40 分钟前
一文学会Golang里拼接字符串的6种方式(性能对比)
开发语言·golang
2401_8784673243 分钟前
大连环保公益管理系统|Java|SSM|Vue| 前后端分离
java·开发语言·学习·tomcat·maven