临沂网站搜索排名,页面设计多少钱,长春网站建设推荐网诚传媒,网站图片用什么做的C入门 六个默认成员函数1 构造函数语法特性 2 析构函数语法特性 3 拷贝构造函数特性 4 赋值运算符重载运算符重载赋值运算符重载特例#xff1a;前置 与 后置前置#xff1a;返回1之后的结果后置#xff1a; Thanks♪(#xff65;ω#xff65;)#xff89;谢谢阅读… C入门 六个默认成员函数1 构造函数语法特性 2 析构函数语法特性 3 拷贝构造函数特性 4 赋值运算符重载运算符重载赋值运算符重载特例前置 与 后置前置返回1之后的结果后置 Thanks♪(ω)谢谢阅读下一篇文章见 六个默认成员函数
如果一个类中什么成员都没有简称为空类。 空类中真的什么都没有吗 并不是 任何类在什么都不写时编译器会自动生成以下6个默认成员函数。 默认成员函数用户没有显式实现编译器会生成的成员函数称为默认成员函数 我们实现了编译器就不会生成了
1 构造函数
构造函数是一个特殊的成员函数名字与类名相同,创建类类型对象时由编译器自动调用以保证 每个数据成员都有 一个合适的初始值并且在对象整个生命周期内只调用一次
语法
函数名与类名相同。无返回值对象实例化时编译器自动调用对应的构造函数构造函数可以重载最好实现一个全缺省的构造函数
class Date {
public:Date(int year 2024, int month 1, int day 1) {_year year;_month month;_day day;}void show() {cout this-_year - this-_month - this-_day endl;}
private:int _year;int _month;int _day;
};int main() {Date today(2024, 2, 18);//默认构造Date yesterday(2024, 2, 17);//默认构造today.show();yesterday.show();return 0;
}来看效果
特性 如果类中没有显示定义构造函数C编译器会自动生成一个无参的默认构造函数一旦用户显示定义编译器将不在生成。 关于编译器生成的默认成员函数有个疑惑不实现构造函数的情况下编译器会生成 默认的构造函数。但是看起来默认构造函数又没什么用Date对象调用了编译器生成的默认构造函数但是Date对象_year / _month / _day依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用 解答C把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型如int/char…自定义类型就是我们使用class/struct/union等自己定义的类型看看下面的程序就会发现编译器生成默认的构造函数会对自定类型成员 _t 调用的它的默认成员函数。
class Time
{
public:Time(){cout Time() endl;_hour 0;_minute 0;_second 0;}
private:int _hour;int _minute;int _second;
};
class Date
{private:// 基本类型(内置类型)int _year;int _month;int _day;// 自定义类型Time _t;
};
int main()
{Date d;return 0;
}上面程序会对 _t 进行默认构造由于我们编写了构造函数_t 中 的内容成功初始化但是_year / _month / _day缺依然是随机值。
无参的构造函数和全缺省的构造函数都称为默认构造函数并且默认构造函数只能有一个。注意无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数都可以认为是默认构造函数
2 析构函数
析构函数与构造函数功能相反析构函数不是完成对对象本身的销毁局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数完成对象中资源的清理工作。
语法
析构函数名是在类名前加上字符 ~。无参数无返回值类型。一个类只能有一个析构函数。若未显式定义系统会自动生成默认的析构函数。注意析构函数不能重载对象生命周期结束时C编译系统系统自动调用析构函数。 下面的栈可以帮助我们理解析构函数的作用
typedef int DataType;
class Stack
{
public:Stack(size_t capacity 3){_array (DataType*)malloc(sizeof(DataType) * capacity);if (NULL _array){perror(malloc申请空间失败!!!);return;}_capacity capacity;_size 0;}void Push(DataType data){// CheckCapacity();_array[_size] data;_size;}// 析构函数~Stack(){ //如果 不为空if (_array){ // 释放指针free(_array);//初始化_array NULL;_capacity 0;_size 0;}}
private:DataType* _array;int _capacity;int _size;
};
void TestStack()
{Stack s;s.Push(1);s.Push(2);//生命周期结束自动析构清理
}特性
关于编译器自动生成的析构函数是否会完成一些事情呢下面的程序我们会看到编译器生成的默认析构函数对自定类型成员调用它的析构函数。
class Time
{
public:~Time(){cout ~Time() endl;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本类型(内置类型)int _year 1970;int _month 1;int _day 1;// 自定义类型Time _t;
};
int main()
{Date d;return 0;
}调用自定义类型 time 的析构函数 注意 析构函数的调用顺序先创建先销毁先销毁局部变量在销毁全局变量。 如果类中没有申请资源时析构函数可以不写直接使用编译器生成的默认析构函数比如Date类有资源申请时一定要写否则会造成资源泄漏比如Stack类
3 拷贝构造函数
拷贝构造函数只有单个形参该形参是对本类类型对象的引用(一般常用const修饰)在用已存在的类类型对象创建新对象时由编译器自动调用
特性
拷贝构造是构造函数的一个重载形式。拷贝构造函数的参数只有一个且必须是类类型对象的引用使用直接的传值编译器会直接报错因为会引发无穷递归调用。 原因传值拷贝时 第一步开辟一个临时空间 第二步由于临时空间是需要构造的重新调用拷贝构造函数无穷递归形成… 若未显式定义编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝这种拷贝叫做浅拷贝或者值拷贝。浅拷贝不能实现复杂的类拷贝涉及指针等内容会拷贝失败。拷贝构造函数典型调用场景 使用已存在对象创建新对象 函数参数类型为类类型对象 函数返回值类型为类类型对象
4 赋值运算符重载
运算符重载
C为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数也具有其返回值类型函数名字以及参数列表其返回值类型与参数列表与普通的函数类似。 函数名字为关键字operator后面接需要重载的运算符符号。 函数原型返回值类型 operator操作符 (参数列表)
注意
不能通过连接其他符号来创建新的操作符比如operator重载操作符必须有一个类类型参数用于内置类型的运算符其含义不能改变例如内置的整型不 能改变其含义作为类成员函数重载时其形参看起来比操作数数目少1因为成员函数的第一个参数为隐藏的this .* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
来看样例
class Date {
public:Date(int year 2024, int month 1, int day 1) {_year year;_month month;_day day;}bool operator( const Date d2) {return _year d2._year _month d2._month _day d2._day;}void show() {cout _year - this-_month - this-_day endl;}
private:int _year;int _month;int _day;
};int main() {Date today(2024, 2, 18);Date yesterday(2024, 2, 17);today.show();yesterday.show();cout operator()\n (today yesterday) endl;return 0;
}不相等返回假。
赋值运算符重载
赋值运算符重载格式 参数类型const T传递引用可以提高传参效率 返回值类型T返回引用可以提高返回的效率有返回值目的是为了支持连续赋值 检测是否自己给自己赋值 返回*this 要复合连续赋值的含义
class Date {
public:Date(int year 2024, int month 1, int day 1) {_year year;_month month;_day day;}//运算符重载bool operator( const Date d2) {return _year d2._year _month d2._month _day d2._day;}//赋值运算符重载Date operator(const Date d){if(this ! d){_year d._year;_month d._month;_day d._day;}return *this;}void show() {cout _year - this-_month - this-_day endl;}
private:int _year;int _month;int _day;
};int main() {Date today(2024, 2, 18);Date day today;today.show();day.show();return 0;
} 可以成功赋值。
赋值运算符只能重载成类的成员函数不能重载成全局函数 原因赋值运算符如果不显式实现编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载就和编译器在类中生成的默认赋值运算符重载冲突了故赋值运算符重载只能是类的成员函数用户没有显式实现时编译器会生成一个默认赋值运算符重载以值的方式逐字节拷贝。注意内置类型成员变量是直接赋值的而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了 还需要自己实现吗当然像日期类这样的类是没必要的。那么下面的类呢验证一下试试
typedef int DataType;
class Stack
{
public:Stack(size_t capacity 10){_array (DataType*)malloc(capacity * sizeof(DataType));if (nullptr _array){perror(malloc申请空间失败);return;}_size 0;_capacity capacity;}void Push(const DataType data){// CheckCapacity();_array[_size] data;_size;}~Stack(){if (_array){free(_array);_array nullptr;_capacity 0;_size 0;}}
private:DataType *_array;size_t _size;size_t _capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2;s2 s1;return 0;
} 这里会发现下面的程序会崩溃掉这里就需要我们以后讲的深拷贝去解决。 注意如果类中未涉及到资源管理赋值运算符是否实现都可以一旦涉及到资源管理则必须要实现
特例前置 与 后置
前置返回1之后的结果 // 注意this指向的对象函数结束后不会销毁故以引用方式返回提高效率
Date operator()
{_day 1;return *this;
}后置
前置和后置都是一元运算符为了让前置与后置形成能正确重载 C规定后置重载时多增加一个int类型的参数但调用函数时该参数不用传递编译器自动传递 注意 后置是 先使用后1因此需要返回1之前的旧值故需在实现时需要先将this保存 一份然后给this1, Date operator(int)
{Date temp(*this);_day 1;// temp是临时对象因此只能以值的方式返回不能返回引用return temp;
}Thanks♪(ω)谢谢阅读
下一篇文章见