电商网站做互联网金融,滕州网站建设企业,内蒙建设厅网站,成都网站建设yingrihe文章目录 前言类的六个默认成员函数#xff1a;1. 构造函数概念特性做了什么#xff1f;易错注意#xff1a;显式定义和默认构造函数 2. 析构函数概念特征做了什么?注意事项#xff1a; 3.拷贝构造函数概念特征做了什么#xff1f;注意事项#xff1a; 4.赋值运算符重载… 文章目录 前言类的六个默认成员函数1. 构造函数概念特性做了什么易错注意显式定义和默认构造函数 2. 析构函数概念特征做了什么?注意事项 3.拷贝构造函数概念特征做了什么注意事项 4.赋值运算符重载运算符重载赋值运算符的重载注意事项 5.取地址及const取地址操作符重载 总结C语言系列学习目录 前言
本节是要学习六个默认成员函数。主要是从四个方面讲解 1什么是该默认成员函数 2默认成员函数做了什么 3一些易错的注意事项 4什么时候用默认成员函数什么时候显式实现 本篇用 日期类Date、栈Stack) 、队列Queue三种类来举例 类的六个默认成员函数
如果一个类中什么成员都没有简称为空类。 空类中真的什么都没有吗并不是任何类在什么都不写时编译器会自动生成以下6个默认成员函数。 默认成员函数用户没有显式实现编译器会生成的成员函数称为默认成员函数。
1. 构造函数
构造函数就与我们所写的Init()方法一样用于类对象属性的初始化。但这个构造函数不用用户调用而是在类对象实例化时自动调用。
概念
构造函数是一个特殊的成员函数名字与类名相同,创建类类型对象时由编译器自动调用以保证每个数据成员都有 一个合适的初始值并且在对象整个生命周期内只调用一次。
特性
我们围绕第一个问题展开什么是构造函数 其有如下特征
函数名与类名相同无返回值
class Date
{
public://函数名和类名相同无返回值Date(int year, int month, int day){_year year;_month month;_day day;}void Print(){cout _year - _month - _day endl;}
private:int _year;int _month;int _day;
};函数可以重载
class Date
{
public://无参数Date(){_year year;_month month;_day day;}/*//全缺省:注意全缺省和无参数不能同时存在他们实例化方式可以相同编译器无法辨别Date(int year2024,int month4,int day27){_year year;_month month;_day day;}*///半缺省Date(int year,int month4,int day27){_year year;_month month;_day day;}//不缺省Date(int year, int month, int day){_year year;_month month;_day day;}
private:int _year;int _month;int _day;
};对象实例化时编译器自动调用如果类中没有显式定义构造函数则C编译器会自动生成一个无参的默认构造函数一旦用户显式定义编译器将不再生成。后面实例化对象按照显式定义的函数调用举例看下面代码注释
Date类举例
class Date
{
public:/*// 5.如果用户显式定义了构造函数编译器将不再生成默认构造函数后面也不能用Date d1;这样实例化而是采用Date d1(2024,4,27);这样来实例化对象Date(int year, int month, int day){_year year;_month month;_day day;}*/void Print(){cout _year - _month - _day endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;d1.Print();return 0;
}这里我们用Date实例化了个对象d1调用Print函数打印日期发现个问题如下图 我们发现我们的实例化对象d1并没有初始化啊那默认构造函数到底干了什么呢
做了什么
C把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型如int/char…自定义类型就是我们使用class/struct/union等自己定义的类型。默认生成的构造函数对两者处理不同 1内置类型不做处理 2自定义类型调用它自己的默认构造函数
易错注意
实例化对象错误
int main()
{Date d1; //表示实例化一个Date类对象Date d1(); //表示一个返回值为Date的d1函数方法return 0;
}无参数构造函数、全缺省构造函数、默认构造函数三种都可以当做默认构造函数只能存在其中一个不然会发生实例化时编译器不知道调用哪一个构造函数的错误。因为都可以用Date d1来实例化对象。C11 中针对内置类型成员不初始化的缺陷又打了补丁即内置类型成员变量在类中声明时可以给默认值。
class Date
{
public:void Print(){cout _year - _month - _day endl;}
private://可以在这里给缺省值int _year2024;int _month4;int _day27;
};
int main()
{Date d1;d1.Print();return 0;
}显式定义和默认构造函数
那么什么时候我们采用显式定义什么时候采用默认构造函数呢 我给出的答案是一般都自己显式定义比较好。 默认构造函数在以下几种情况下可以使用
内置类型成员都具有缺省值默认值类中全是自定义类型如Queue
2. 析构函数
析构函数是一个特殊的成员函数其名称与类名相同前面加上波浪号~作为前缀。析构函数的主要作用是执行对象生命周期结束时的清理工作。当一个对象的生命周期结束时无论是因为超出作用域、被显式删除还是因为其所在的动态内存分配被释放析构函数都会被自动调用。
概念
析构函数与构造函数功能相反析构函数不是完成对对象本身的销毁局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数完成对象中资源的清理工作。
特征
那么什么是析构函数呢 它有以下特征
函数名就是在类名前加~无参数、无返回类型一个类只能由一个析构函数。若没有显示定义系统自动调用默认析构函数。注意析构函数不能够重载对象生命周期结束时C编译系统系统自动调用析构函数。
Stack类 举例
class Stack {public://构造函数Stack(int capacity4) {_a (int*)malloc(sizeof(int) * capacity);if (NULL _a){perror(malloc fail);return;}_capacity capacity;_size 0;}//析构函数显式定义~Stack(){//释放动态调用的空间资源free(_a);_a NULL;}private:int* _a;int _size;int _capacity;
};int main()
{{Stack s1(10);}//s1作用域结束自动调用 ~Stack();//不论显示定义还是默认析构函数都不需要显示调用return 0;
}做了什么?
默认析构函数做了什么
内置类型不做处理自定义类型调用它的析构函数
内置类型成员销毁时不需要资源清理最后系统直接将其内存回收即可
注意事项
如果类中没有申请资源时析构函数可以不写直接使用编译器生成的默认析构函数比如Date类有资源申请时一定要写否则会造成资源泄漏比如Stack类。Queue类成员全是自定义类型会调用成员各自的析构函数。所以不用调用析构函数。只有堆上的资源需要手动释放。
3.拷贝构造函数
拷贝构造函数是C中的一种特殊的构造函数用于创建一个新对象该对象是已存在对象的副本。拷贝构造函数在多种情况下会自动被调用。
概念
概念拷贝构造函数只有单个形参该形参是对本类类型对象的引用(一般常用const修饰)在用已存在的类类型对象创建新对象时由编译器自动调用。
特征
拷贝构造函数有以下特征
拷贝构造函数是构造函数的重载形式。拷贝构造函数的参数只有一个且必须是类类型对象的引用。若未显式定义编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝这种拷贝叫做浅拷贝或者值拷贝。
Date类 举例
class Date
{
public:Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}//拷贝构造函数 一个参数必须是对象引用哦!Date(const Date d){_year d._year;_month d._month;_day d._day;}
private:int _year;int _month;int _day;
};int main()
{Date d1;//拷贝构造函数Date d2(d1);return 0;
}做了什么
默认拷贝构造依旧是一样的对内置类型进行浅拷贝自定义类型调用它们自己的拷贝构造。不需要显示定义的类比如Date、Queue。需要显式定义的类比如Stack。因为有动态空间的开辟所以需要深拷贝。
注意事项
这个比较容易错大家请注意
默认拷贝构造只是浅拷贝需要深拷贝的对象需要显示定义拷贝构造函数。 举个Stack的例子我们如果只是浅拷贝来处理Stack会出现错误。
比如Stack s2(s1);
如果只是浅拷贝 s2._as1._a; 只是这种两个指针指向同一个开辟的空间问这种会出现怎样的问题呢 答在函数结束的时候s1调用一次析构函数把空间释放了s2也会在调用一次此时原本空间已经被释放了无法再次释放会报错。
强调显示定义拷贝构造函数只能传一个参数并且必须为该类的引用。不能传值。 原因如果进行传值传参会在过程中调用拷贝构造去拷贝一个data临时对象用于传值。如下图便无限的调用下去没有结束点进入死循环。所以发生报错。 4.赋值运算符重载
运算符重载
C为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数也具有其返回值类型函数名字以及参数列表其返回值类型与参数列表与普通的函数类似。 函数名字为关键字 operator后面接需要重载的运算符符号。 函数原型返回值类型 operator操作符(参数列表) 注意
不能新增加运算符保持运算符具有原有语意不改变运算符原有操作数个数(比如就只能两个对象进行作为类成员函数重载时其形参看起来比操作数数目少1因为成员函数的第一个参数为隐藏的this.* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
赋值运算符的重载
1. 赋值运算符重载格式
参数类型const T传递引用可以提高传参效率
返回值类型T返回引用可以提高返回的效率有返回值目的是为了支持连续赋值
检测是否自己给自己赋值
返回*this 要复合连续赋值的含义Date类 举例
class Date
{
public:Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}//拷贝构造函数Date(const Date d){_year d._year;_month d._month;_day d._day;}//赋值运算符重载Date operator(const Date d){if (this ! d){_year d._year;_month d._month;_day d._day;}return *this;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024,5,5);//拷贝构造函数Date d2(2024,5,6);Date d3;d3 d1 d2;return 0;
}注意事项
赋值运算符重载是默认构造函数不写会进行简单的赋值和浅拷贝类似。通常需要为包含动态分配内存的类重载赋值运算符以执行深拷贝操作。深拷贝意味着为新对象分配新的内存并复制原对象所指向的内存内容从而确保两个对象独立拥有自己的内存资源。注意区分什么是赋值什么时候是构造
Date d1(2024,5,5); //构造
Date d2 d1; //构造
Date d3;
d3d1; //赋值
辨别方法已存在的对象初始化另一个对象叫构造两个都存在的对象则是赋值。
//小技巧看前面有没有Date 类5.取地址及const取地址操作符重载
这两个默认成员函数一般不用重新定义 编译器默认会生成。
class Date
{
public :
Date* operator()
{
return this ;
}
const Date* operator()const
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};这两个运算符一般不需要重载使用编译器生成的默认取地址的重载即可只有特殊情况才需要重载比如想让别人获取到指定的内容 总结
本章节介绍了四个主要的默认成员函数还有两个不常用就没有过多介绍。 C语言系列学习目录 提示这里可以添加系列文章的所有文章的目录目录需要自己手动添加添加超链接