当前位置: 首页 > article >正文

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求导。

#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;
}

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

#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这里后面什么都不写

Date d1;

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

 

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

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

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

析构函数的特点:

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

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

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

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

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

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

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

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

#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;
}

 

 用栈的拷贝构造:

//拷贝构造
#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.至少有一个类类型参数。

//运算符重载
#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.有返回值,写当前类类型的引用,提高效率。

 

//赋值运算符重载
#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++的学习很陡,时而巨难时而巨简单,希望宝子和小编一起坚持下去~你们的三连是我的动力,感谢支持~


http://www.kler.cn/a/408559.html

相关文章:

  • 【贪心算法第二弹——2208.将数组和减半的最小操作数】
  • 小柴冲刺软考中级嵌入式系统设计师系列二、嵌入式系统硬件基础知识(7)嵌入式Soc
  • VSCode 汉化教程【简洁易懂】
  • 网络爬虫——常见问题与调试技巧
  • C语言中const char *字符进行切割实现
  • git使用(二)
  • Tkinter置顶弹窗提示操作成功
  • rabbitmq结合springboot配置发送消息和消费消息
  • 监控报警系统的指标、规则与执行闭环
  • `--version` 选项在 Java 8 中是不被支持的。Java 8 使用的是 `-version` 选项而不是 `--version`
  • Go语言获取客户端真实IP
  • PowerMILL 客制化宏 - 概念
  • 功能强大的stringstream类
  • Kotlin Multiplatform 未来将采用基于 JetBrains Fleet 定制的独立 IDE
  • STM32定时器原理及应用
  • Spring 框架环境搭建
  • 改错题总结
  • 6-自定义fprint函数 --github_com_fatih_color测试
  • 【初阶数据结构与算法】线性表之队列的定义与实现
  • HarmonyOS:使用ArkWeb构建页面
  • 手搓《unordered_map unordered_set》
  • 《第十部分》1.STM32之通信接口《精讲》之IIC通信---介绍
  • 用 BlockingQueue 打造轻量级消息队列服务:从原理到实现
  • [Docker-显示所有容器IP] 显示docker-compose.yml中所有容器IP的方法
  • 本地推流,服务器拉流全流程
  • SCP文件传输命令解析