大家好啊,这里是c++之旅第五弹,跟随我的步伐来开始这一篇的学习吧!
如果有知识性错误,欢迎各位指正!!一起加油!!
创作不易,希望大家多多支持哦!
一,构造函数:
1.作用:
它是一种特殊的函数,主要用来在创建类对象时初始化对象,即为对象的成员变量赋初始值。(在构造函数中对类中的数据成员赋值 )
2.构造函数的定义:
(1)、构造函数名和类名相同
(2)、构造函数没有返回值类型和返回值
(3)、构造函数可以重载,需要满足函数重载的条件
3.构造函数的调用:
在定义一个类的对象时自动调用,也就是伴随对象产生同时调用做初始化操作
4.构造函数写法:
//不带参构造函数就是没有参数列表,函数体内操作和带参构造基本一致,只不过定义变量赋值时赋的是构造函数定义时写的默认值
类名(参数列表)//这里是带参构造{函数体;//做在定义对象的时候想要做的事情,包括对数据成员赋值,当然构造函数除了对创建的对象进行初始化数据之外,还可以往里面写入类的成员函数的调用,用于在对象创建使就自动执行这个函数}
具体写法示例:
class student{//属性举例:int age;char *pname;public:student(){}// 无参 ( 默认 )构造student(int a){}// 有参 ( 带参 )构造}//调用构造函数:student stu1;// 调用默认构造student stu2(1);// 调用带参构造
5.构造函数特点:
(1)、如果一个类中没有显式的给出构造函数,系统会自动地给出一个缺省的 ( 隐式 )什么都不干的构造函数
(2)、如果类中有多个构造函数,那么通常会有不同的参数列表和函数体(构造函数可以重载 )
(3)、如果用户提供了无参 ( 有参 ) 构造,那么系统就不再提供默认构造
(4) 、类中如果只有有参构造,没有无参构造,那么就不能调用默认构造的方式初始化对象,想用这种方式初始化对象那么就提供无参构造//基于第三点
(5)、构造函数需要写在公有访问属性下面,以便类外访问
6.构造函数注意点:
构造函数用来给类中成员数据赋初值,为什么不使用和结构体类似的直接赋值操作呢,这样只能在没有书写构造函数及所有数据是公有属性的情况下才能像结构变量一样直接赋初值。因为类中成员数据的属性默认是私有的(封装性),而如果通过这种方式来进行初始化就必须将成员数据属性改为public公有属性,所以一般不用这种直接初始化的操作而是使用构造函数来初始化类对象中的成员数据。
7.实际代码片段分析:
MyStu *pMs = new MyStu;//这是使用指针开辟空间时调用了无参构造函数的情况delete pMs;pMs = NULL;MyStu *pMs2 = new MyStu(1245);//这是使用指针开辟空间时调用了有参构造函数的情况delete pMs2;pMs2= NULL;MyStu mArr[2];//创建类对象数组,但是如果只在类中写有带参构造就不可使用
二,拷贝构造:
1.作用:
拷贝构造函数,它是一种特殊的构造函数,是在定义一个新的对象的时候用自身这种类型的对象来构造自身
2.拷贝构造函数的定义:
(1)、拷贝构造函数名和类名相同
(2)、拷贝构造函数没有返回值类型,和返回值
3.拷贝构造本质:
拷贝构造是有参构造的特殊形式,int b=20在C语言中是赋值,在c++中是拷贝构造(定义一个新的对象的时候用自身这种类型的对象来构造初始化自身)
4.拷贝构造注意事项:
(1)在C++里面结构和类没有太多区别,那么结构怎么算内存,类就怎么算(内存对齐)
(2)如果没有去书写拷贝构造,系统提供一个隐式的拷贝构造,可以理解为浅拷贝,浅拷贝只拷贝物理内存,不会拷贝逻辑内存,也就 是不会拷贝类中指针开辟的堆内存 如果类中的属性有指针,且会动态开辟,必须重写拷贝构造,去书写深拷贝(也就是自定义拷贝构造)
5.函数使用时本质上涉及到的拷贝:
函数调用过程可能发生两次拷贝,一次是实参到形参,一次是返回值时进行了拷贝,值是算出来的,但是返回时返回的是备份,备份值可以来到函数外面,原来算出的值直接在函数结束后丢弃
6.实际代码分析:
(1):
class CMyStu{ int id; char *pName;public: CMyStu() { id = 0; pName = NULL; } CMyStu(int i,char *n) { id = i; int len = strlen(n); pName = new char[len + 1]; strcpy(pName, n); } //自定义拷贝构造 CMyStu(CMyStu const& other)//CMyStu(CMyStu m)会造成死循环 { id = other.id;4 pName = NULL; if (other.pName) { int len = strlen(other.pName); pName = new char[len + 1]; strcpy(pName, other.pName); } } ~CMyStu() { if (pName) delete[] pName; pName = NULL; }};
const作用:在C++中,拷贝构造函数用于创建一个对象的副本。通常情况下,拷贝构造函数的参数是一个引用类型,用于接收要拷贝的对象。在拷贝构造函数的参数引用前加上const
关键字的作用是为了避免对被拷贝对象进行修改。 具体来说,将参数引用声明为const
类型有以下几个原因:
避免修改被拷贝对象:通过将参数引用声明为const
类型,可以确保在拷贝构造函数中不会对被拷贝对象进行任何修改。这是因为const
修饰的引用只能读取对象的值,而不能修改它。
支持常量对象的拷贝:如果被拷贝对象是一个常量对象(const
对象),则只能使用const
引用来接收它,否则会导致编译错误。因此,将拷贝构造函数的参数引用声明为const
类型,可以支持常量对象的拷贝。
(2):
CMyStu ms1 = ms;//拷贝构造的隐式调用(ms为已经定义好的类对象)ms.~CMyStu();//析构函数是可以主动调用的,但是其清理效果只是相当于将类中的代码段复制过来,也就是只会实现清理开辟的堆内存,类的对象的其他项比如变量属性是不会清理掉的,所以一般不主动使用析构函数,而是在类对象生命周期到的时候自动调用CMyStu ms2(ms);//拷贝构造的显式调用(相当于int a(10)这个意思)CMyStu *pMs = new CMyStu(ms);//拷贝构造的显式调用(开辟一个堆内存时将内存赋为类对象ms)delete pMs;pMs=NULL;CMyStu ms3;ms3 = ms;//这个不是拷贝构造,是赋值,因为构造是对象产生内存之后立马给值,不是立马给值的就是赋值操作
二,析构函数:析构函数和构造函数一样,也是一种特殊的函数,主要的作用是在对象结束生命周期时,系统自动调用析构函数,来做一些清理工作
1.析构函数的定义:
(1)、函数名与类名相同,在前面加上一个~
~类名(){}
(2)、没有返回值类型和返回值,也没有参数
(3)、如果类中没有自己写析构函数,那么系统将会给出一个隐式什么都不干的析构函数
2.析构函数的调用:
(1)、通过类对象主动调用(和类中普通函数的使用方式一致,也就是用对象加‘.'使用的,这和结构体是一致的)
(2)、对象死亡时,析构函数被自动调用
3.析构函数的特点:
(1)、析构函数是对对象做一些清理工作,主动调用析构函数,不意味对象死亡
(2)、一个类只有一个析构函数
(3)、析构函数需要写在公有下面
(4) 、析构函数里面的代码可以理解为是当对象死亡时要执行的代码
4.注意事项:
(1)如果一个类中有了指针来指向堆内存,那么析构函数就必须要自己重写,不能不写来使用默认的什么都不干的析构函数,否则会造成内存泄漏,积累下去会导致堆区内存越来越小
(2)由于拷贝构造和析构函数的特点,我们在对象有开辟堆内存的时候才需要手写拷贝构造和析构函数
三,this指针:系统自动生成且隐藏的指针,只能在类中使用,该指针总是指向调用者对象
1.理解方式:this指针理解为一个只能在类中的指针,指向当前对象的指针。在类中:this->成员;(*this).成员
2.实际代码分析:
#include <string.h>class CMyStu{ int id; char *pName;public: CMyStu() { id = 0; pName = NULL; } CMyStu(int id, char *pName)//this指针可以区分同名 { this->id = id; int len = strlen(pName); this->pName = new char[len + 1]; strcpy(this->pName, pName); } ~CMyStu() { if (pName) delete[]pName; pName = NULL; } CMyStu& operator =(CMyStu const& destStu) //返回值为什么使用的是引用类型,因为是返回自己,所以没有必要再将自己拷贝一份给到返回值,使用引用类型直接返回自己本身 { id = destStu.id; pName = NULL; if (destStu.pName) { int len = strlen(destStu.pName); pName = new char[len + 1]; strcpy(pName, destStu.pName); } return *this;//在调用者自己的成员函数中返回调用者自己 }};
用于区分同名不常用,一般是用于返回对象的,原理类似于赋值表达式的值是被赋值变量的值,eg:if(a=0),里面表达式值就是0