网站内链是什么 怎么做,湛江论坛,甘肃省建设厅查行网站,广州网站制作设计公司目录 一、类的6个默认成员函数
二、构造函数
2.1概念
2.2七大特性
三、析构函数
3.1概念
3.2特性
四、拷贝构造函数 4.1概念
4.2特性
五、赋值运算符重载
5.1运算符重载
5.2赋值运算符重载 5.3前置和后置重载
六、const成员函数
七、取地址及const取地址操作符重…目录 一、类的6个默认成员函数
二、构造函数
2.1概念
2.2七大特性
三、析构函数
3.1概念
3.2特性
四、拷贝构造函数 4.1概念
4.2特性
五、赋值运算符重载
5.1运算符重载
5.2赋值运算符重载 5.3前置和后置重载
六、const成员函数
七、取地址及const取地址操作符重载 一、类的6个默认成员函数 对于日常认知空类好像没有作用但其实他也有实际的意义任何类在什么都不写时编译器会自动生成6个默认成员函数 默认成员函数用户没有显示的去实现该函数而是编译器会生成的成员函数称为默认成员函数。其实就是祖师爷给编译器设定好的默认成员函数我们自己不去定义这些函数时编译器会自动帮我们实现。 接下来详细介绍他们~
二、构造函数
2.1概念
先来回顾一下日期类的代码
#include iostream
using namespace std;class Date
{
public:void Init(int year, int month, int day){_year year;_month month;_day day;}void Print(){cout this-_year - this-_month - this-_day endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Init(2022, 1, 12);d1.Print();Date d2;d2.Init(2023, 11, 24);d2.Print();return 0;
}
在上述Date类中需要通过Init方法给对象设置日期但是每次创建对象时都需要调用该方法才能设置好日期,有点烦。那么能不能在创建对象时直接就设置好日期呢 而构造函数就解决了这一问题。构造函数是一个特殊的成员函数名字与类名相同创建类类型对象时由编译器自动调用以保证每个数据成员都有一个合适的初始值并且在对象整个生命周期内只调用一次 那么接下来讲讲它的特点以及如何使用的。
2.2七大特性
构造函数虽然名称叫做构造但其主要任务并不是开空间创建对象而是初始化对象即你在创建构造函数时可以为对象初始化。
其特征如下
1.函数名与类名相同
2.无返回值
3.对象实例化时编译器自动调用对应的构造函数
4.构造函数可以重载 通过代码来演示
#include iostream
using namespace std;class Date
{
public://1.无参构造函数Date()//函数名与类名相同,无返回值{cout 2023-11-24 endl;}//1.带参构造函数Date(int year, int month, int day)//函数名与类名相同,无返回值{_year year;_month month;_day day;cout this-_year - this-_month - this-_day endl;}private:int _year;int _month;int _day;
};int main()
{Date d1;//调用无参构造函数没有任何参数但是编译器依然会自动调用构造函数//只需要在构造函数中设置输出不需要额外写一个打印函数然后还要调用打印函数。在运行时依然会有打印结果//就可验证编译器会自动调用构造函数Date d2(2023, 11, 24);//调用带参的构造函数,创建对象时直接设置日期//且编译器会自动调用构造函数return 0;
}
没有设置打印的方法但是在构造函数中设置了输出创建对象后也不需要去调用该构造函数然而在运行时依然会有结果说明编译器自动调用了该构造函数 且该无参构造与有参构造也构成了重载函数。
运行结果 注意通过无参构造函数创建对象后对象后面不用跟括号否则就成了函数声明。
5.如果类中没有显示定义构造函数则c编译器会自动生成一个无参的默认构造函数一旦用户显示定义构造函数编译器则不会自动生成
举例
#include iostream
using namespace std;class Date//类中没有构造函数
{};int main()
{Date d1;//创建对象时编译器会自动生成一个无参的默认构造函数return 0;
} 此运行没有任何问题。
#include iostream
using namespace std;class Date
{
public://1.用户显示定义了构造函数编译器则不会生成默认构造函数Date(int year, int month, int day){_year year;_month month;_day day;cout this-_year - this-_month - this-_day endl;}private:int _year;int _month;int _day;
};int main()
{Date d1;//d1对象只能调用无参构造函数而却又显示定义了构造函数运行时则会报错return 0;
} 运行结果 总结一下无参构造函数可以自己构造也可由编译器自动生成但是在创建无参对象时需要调用无参构造函数可以调用自己构建的无参构造函数且当自己构造了无参构造函数也可以构造有参构造函数因为构造函数可以重载若自己没有构造无参构造函数那么就不能显示构造构造函数即不能构造有参构造函数而是由编译器默认构造。 想必有疑问的是对于编译器生成的默认构造参数它到底有啥作用呢 见下
6.c把类型分为内置类型基本类型和自定义类型。内置类型就是语言提供的数据类型如int/char/double...,自定义类型就是自己定义的类型如class/struct/union等。在这里编译器生成的默认构造函数会对自定义类型成员调用它的默认成员函数。
那我们用显示定义的构造函数来演示其对自定义类型成员的默认成员函数的调用更加的形象
#include iostream
using namespace std;class Time
{
public:Time()//构造函数{cout Time() endl;}};
class Date
{
public:Date()//显示定义构造函数{cout Date() endl;}private://自定义类型Time _t;//内置类型int _year 1970;int _month 1;int _day 1;};int main()
{Date d1;//会调用自定义类型成员的默认成员函数return 0;
}
运行结果 当然在该自定义类型中依然不能显示定义有参构造函数 。通过结果发现先调用了_t对象的构造函数再调用d1对象的构造函数符合会调用自定义类型成员的默认成员函数。若没有显示定义d1对象的构造函数则编译器会生成默认构造函数该默认构造函数同样也会调用其自定义类型成员的默认成员函数。 注意在VS2013下默认构造函数不会对内置类型成员做处理只对自定义类型处理而在一些高版本下当只有内置类型成员时默认构造函数不会对其做处理当既存在内置类型成员又存在自定义类型成员时默认构造函数就会对他们都做处理。
VS2022下对上述代码进行调试 发现内置类型都被初始化了0。一方面本来设定内置类型不用初始化另一方面默认构造函数又给内置类型初始化了那么这样默认构造函数就能够随意去改变内置类型的值吗早在2011年为了解决该问题c11中针对内置类型成员不初始化的缺陷又打了补丁即内置类型成员变量在类中声明时可以给默认值。 对于缺省函数如果给了默认值优先使用否则使用缺省值。
#include iostream
using namespace std;class Date
{
public:Date(){cout _year - _month - _day endl;}
private://内置类型int _year 1970;int _month 1;int _day 1;};int main()
{Date d1;return 0;
}
运行结果 7. 无参的构造函数和全缺省的构造函数、我们没写编译器默认生成的构造函数都可以称为默认构造函数并且默认构造函数只能有一个。 #include iostream
using namespace std;class Date
{
public:Date()//无参构造函数{cout _year - _month - _day endl;}Date(int year 1900, int month 1, int day 1)//全缺省的构造函数{_year year;_month month;_day day;}
private://内置类型int _year 1970;int _month 1;int _day 1;};int main()
{Date d1;return 0;
}
运行结果 三、析构函数
3.1概念
前面已经讲解了如何实例化对象那么对象是如何销毁的呢其成员是如何做处理的对于其被初始化的成员以及所开的空间又是如何做处理的。
析构函数正是对对象销毁前做处理工作其功能与构造函数相反完成的是对对象中资源的清理工作对象在销毁时会自动调用析构函数。即当对象的生命周期要结束时会调用析构函数对对象中的成员做处理清理成员的值清理所开的空间。
3.2特性
析构函数同样也是特殊的成员函数其特性如下
1.构造函数名与类名相同而析构函数名与构造函数名相比在构造函数名前多了一个字符~。
2.无参数无返回值类型
3.一个类只能有一个析构函数。若未显示定义系统会自动生成默认的析构函数。由此可知析构函数不能重载。
4.对象生命周期结束时c编译系统会自动调用析构函数。
且看例子
#include iostream
using namespace std;typedef int DataType;
class stack
{
public:stack(DataType capacity 3)//缺省构造函数{_array (DataType*)malloc(sizeof(DataType) * capacity);if (nullptr _array){perror(malloc fail);exit(-1);}}//压栈void push(DataType data){_array[_size] data;_size;}//其他方法...~stack()//显示定义析构函数{if (_array){free(_array);_array nullptr;_capacity 0;_size 0;}}
private://内置类型DataType* _array;int _capacity 0;int _size 0;};int main()
{stack s;s.push(1);s.push(2);return 0;
}
调试结果 进行调试阶段当压了两次栈后_size 的值为2。 当执行完析构函数后_size的值为0. 至于_capacity 的值为什么是0因为最开始给了默认值为0然后没有写扩容的检查方法但也不影响演示。 如果将显示定义的析构函数去掉编译器也会自动生成默认的析构函数只不过这个过程演示不出来。 5.与构造函数一样编译器生成的默认析构函数对自定义类型成员调用它的析构函数
这里同样用显示定义的析构函数来演示调用其自定义类型成员的析构函数更形象
class Time
{
public:~Time()//显示定义析构函数{cout ~Time() endl;}};
class Date
{
public:~Date()//显示定义析构函数{cout ~Date() endl;}private://内置类型int _year 1970;int _month 1;int _day 1;//自定义类型Time _t;};int main()
{Date d1;//会调用自定义类型成员的默认成员函数return 0;
} 运行结果 通过运行结果与构造函数的调用顺序不一样先调用了d1对象的析构函数再调用了d1对象中_t对象中的析构函数 ,那是因为main函数会先识别在它的作用域中的对象在main函数中只有d1对象所以在d1对象销毁时会先调用d1对象的析构函数,但是发现在调用析构函数时d1对象中还有一个Time类的_t对象然后才会调用_t对象的析构函数。 6.如果类中没有申请资源时析构函数可以不写直接使用编译器生成的默认析构函数比如上面Date类的析构函数可以不写但是有资源申请时一定要显示定义否则会造成资源泄露。因为默认析构函数只对普通成员做清理工作而对于有成员以realloc、malloc形式自己申请的资源默认析构函数不会做处理像stack类就需要自己定义析构函数去做清理工作。
四、拷贝构造函数 4.1概念
只有单个形参该形参是对类类型对象的引用(一般用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。即已经创建了一个类类型的对象d1再用d1对象拷贝给同类类型的对象从而出创建出新对象d2这时会调用一个拷贝构造函数该拷贝构造函数形参是对同类类型的引用
4.2特性
拷贝构造函数也是特殊的成员函数其特征如下
1.拷贝构造函数是构造函数的一个重载形式。
2.拷贝构造函数的参数只有一个且必须是类类型对象的引用使用传值方式编译器直接报错因为会引发无穷的递归调用。
3.若未显示定义编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝这种拷贝叫做浅拷贝或者值拷贝。
4.跟构造、析构一样默认拷贝构造会对其自定义类型成员调用它的默认成员函数。
先看正确的代码
#include iostream
using namespace std;class Date
{
public:Date(int year 2023, int month 11, int day 26)//全缺省构造函数{_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);//将d1对象拷贝给d2对象会调用拷贝构造函数return 0;
}
若为传值方式则会发生以下递归 当自定义类型进行传值拷贝时则会引发对象的拷贝上图d1对象拷贝给d2对象时会调用拷贝构造函数接着d1要传值拷贝给dd此时则会引发对象的拷贝d1对象拷贝给d对象(名字随便取),则又会调用拷贝构造函数接着d1又要传值给dd对象此时又会引发对象的拷贝d1对象又拷贝给d对象以此无穷递归。而对于传引用只是d1对象的别名不会引发对象的拷贝。
总而言之通过拷贝对象来创建新对象会调用拷贝构造函数而传值时拷贝会创建新对象。
为什么要存在拷贝构造函数
先给代码再看图
#include iostream
using namespace std;typedef int DataType;
class stack
{
public:stack(DataType capacity 3)//缺省构造函数{cout stack(DataType capacity 3) endl;_array (DataType*)malloc(sizeof(DataType) * capacity);if (nullptr _array){perror(malloc fail);exit(-1);}}stack(const stack d)//拷贝构造函数{cout stack(const stack d) endl;_array (DataType*)malloc(sizeof(DataType) * d._capacity);if (nullptr _array){perror(malloc fail);exit(-1);}memcpy(_array, d._array, sizeof(DataType) * d._size);_capacity d._capacity;_size d._size;}//压栈void push(DataType data){_array[_size] data;_size;}//其他方法...~stack()//显示定义析构函数{//cout ~stack() endl;if (_array){free(_array);_array nullptr;_capacity 0;_size 0;}}
private://内置类型DataType* _array;int _capacity 0;int _size 0;};class MyQueue
{
public:MyQueue(){cout MyQueue() endl;}MyQueue(const MyQueue dd) : _pushst(dd._pushst)//显示定义拷贝构造函数,将传入的对象dd的成员变量_pushst拷贝到当前对象的成员变量_pushst中。//意思先大概了解用法后面章节会讲解{cout MyQueue(const stack dd) endl;}//自定义类型成员stack _pushst;int _size 0;
};int main()
{stack s1;stack s2(s1);MyQueue q1;MyQueue q2(q1);return 0;
} 无拷贝构造函数的示意图 对于上述图若没有拷贝构造函数将s1对象拷贝给s2对象s1对象中的_array指向malloc的空间那么s2对象中的_array也会指向malloc的空间那么在对象s1销毁时会调用析构函数那么_array指向的空间就会释放然后s2对象销毁时同样会调用析构函数也会对_array进行free这就导致了对已释放的空间再释放就会报错。其实拷贝构造函数主要就是解决这一问题而存在
有拷贝构造函数的示意图 对于有拷贝构造函数而言则就不会存在重复空间的释放由代码也可以看出d1对象拷贝给d2对象d2对象会重新malloc一个空间但里面的内容和d1对象的一样当他们销毁时调用各自的析构函数不会影响彼此。
跟构造、析构一样默认拷贝构造会对其自定义类型成员调用它的默认成员函数
上述代码中依然显示的定义了拷贝构造函数来进行演示会更加形象MyQueue q1;会调用MyQueue构造函数又因MyQueue类中含有自定义类型成员所以在调用MyQueue构造函数的时候先去调用自定义类型成员的构造函数了。MyQueue q2(q1);q1对象拷贝给q2对象会调用MyQueue拷贝构造函数又因MyQueue类中含有自定义类型成员所以在调用MyQueue拷贝构造函数的时候先去调用自定义类型成员的拷贝构造函数了。
验证输出结果 总结一下 1. 像Date类我们可以称之为浅拷贝可写拷贝构造函数也可不写由编译器默认生成只适用于内置类型成员像stack类我们称之为深拷贝就是自己用realloc、malloc申请的资源需要自己写拷贝构造函数2.拷贝时不能传值可以传指针或者引用但对于拷贝构造函数的定义而言必须传引用3.默认拷贝构造会对其自定义类型成员调用它的默认成员函数。 5.拷贝构造函数典型调用场景
使用已存在对象创建新对象函数参数类型为类类型对象函数返回值类型为类类型对象
#include iostream
using namespace std;class Date
{
public:Date(int year, int month, int day)//全缺省构造函数{cout Date(int,int,int) endl;}//拷贝构造函数Date(const Date dd) //传引用采用传值会报错{cout Date(const Date dd) endl;}~Date(){cout ~Date() endl;}private://内置类型int _year;int _month;int _day;};Date Test(Date d)//函数参数类型为类类型对象
{Date temp(d);//使用已存在对象创建新对象return temp;//函数返回值类型为类类型对象
}int main()
{Date d1(2023, 11, 26);Test(d1);//函数参数类型为类类型对象return 0;
}
运行结果及其如何调用 创建对象d1调用构造函数。 将对象d1传值拷贝给d对象引发新对象创建会调用拷贝构造函数。 再将对象拷贝给temp对象又引发新对象创建会调用拷贝构造函数。 其实在传值返回temp对象时会创建临时对象temp对象会传值拷贝给临时对象会引发拷贝构造函数调用但是却没有该输出结果原因是编译器在这里做了返回值优化给干掉了。 接着temp对象销毁调用析构函数。 d对象销毁调用析构函数。 最后d1对象销毁调用析构函数 由于以上代码并没有传引用导致不断的调用成员函数所以为了提高效率一般对象传参时尽量使用引用类型返回时根据实际场景能用引用尽量使用引用。 五、赋值运算符重载
5.1运算符重载 对于内置类型int、double等的成员可以使用运算符进行彼此间的操作这是语言本身支持的他们的行为很简单编译器会自动识别所以在编译时也会转换成汇编指令而对于自定义类型的成员语言本身不支持他们进行运算符的运算因为编译器不能够识别他们的行为要表达什么对象中的行为是如何定义的编译器不能够识别。祖师爷早就意识到这一方面的问题所以C为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数也具有返回值类型函数名字以及参数列表。 运算符重载的原型使用
函数原型返回值类型 operator操作符(参数列表)例如Date operator(Date x, Date y);
函数名operator操作符
运算符重载表达的意思
先拿简单的运算符代码分析
#include iostream
using namespace std;class Date
{
public:Date(int year 1900, int month 1, int day 1)//全缺省构造函数{_year year;_month month;_day day;}//private://内置类型int _year;int _month;int _day;};bool operator(const Date x, const Date y)
{return x._year y._year x._month y._month x._day y._day;
}int main()
{Date d1(2023, 11, 26);Date d2(2023, 11, 26);cout operator(d1, d2) endl;cout (d1 d2) endl;return 0;
}
运行结果 1.我们要表达两个对象是否相等一方面operator加运算符是不是解决了编译器本身不能识别自定义类型运算符的操作。另一方面operator加运算符是不是很明显要表达什么意思在这里表达是否相等的意思 2.结果用operator(d1,d2)的返回值来判断d1,d2是否相等然而祖师爷觉得这样还是欠缺点可读性于是便直接使用d1d2来判断d1,d2是否相等更加直观--符合我们的直观感受但两者都可以使用其次d1 d2需要其中需要加括号因为流插入的运算符优先级比的高不然就会先把d1对象流入到控制台中留下一对孤魂野鬼在外面而不是将d1d2的返回值流入到控制台 3.operator在这里定义的是一个全局函数当通过外部对象去访问class类的私有成员时是无法访问的所以在这里就将其成员改为了公有的但是这一问题导致了class类的封装性下降了。 那么为了针对这一封装性问题operator可以写到类中去这样就可以访问类中私有成员还有几点运算符重载的注意事项
不能通过连接其他符号来创建新的操作符比如operator重载操作符必须有一个类类型参数就是传参时必须有一个类类型的对象或者隐藏的对象传过去由this指针接收否则会报错。用于内置类型的运算符其含义不能改变例如内置的整形就是相加的意思不能改变其含义不要与运算符重载混淆作为类成员函数重载时其形参看起来比操作数数目少1因为成员函数的第一个参数为隐藏的this.* :: sizeof ?: . 注意以上5个运算符不能重载在选择题中也经常出现。
拿代码分析运算符重载作为类成员函数
#include iostream
using namespace std;class Date
{
public:Date(int year 1900, int month 1, int day 1)//全缺省构造函数{_year year;_month month;_day day;}bool operator(const Date y)//隐藏Date* const this指针{return _year y._year _month y._month _day y._day;}private://内置类型int _year;int _month;int _day;};int main()
{Date d1(2023, 11, 26);Date d2(2023, 11, 26);cout d1.operator(d2) endl;cout (d1 d2) endl;return 0;
}
运行结果 也没多大变化形参少了一个隐藏了一个指向d1的this指针有一个点注意的是此时不能直接使用operator(d1, d2)作为返回值判断是否相等了正因为operator函数中隐藏了一个this指针d1显示传过去并不会传给隐藏的this指针而operator中没有多余的参数来接受d1就会引发参数不匹配所以必须用实例化的对象来调用类中的成员函数即d1.operator(d2)这样d1就会隐藏的传给this指针。 上面举了简单的运算符来简介了一下运算符重载存在的理由及基本事项接下来就是关于赋值运算符的分析
5.2赋值运算符重载
1.赋值运算符重载格式
参数类型const 类类型传引用防止调用拷贝构造返回值类型类类型若返回对象所在的作用域销毁时对象还存在为了防止调用拷贝构造就用传引用返回。检测是否自己给自己赋值返回*this要复合连续赋值的含义 ok直接代码分析
#include iostream
using namespace std;class Date
{
public:Date(int year 1900, int month 1, int day 1)//全缺省构造函数{_year year;_month month;_day day;}Date(const Date dd){_year dd._year;_month dd._month;_day dd._day;}Date operator(const Date dd)//返回值传引用{if (dd ! this)//防止给自己赋值{_year dd._year;_month dd._month;_day dd._day;}return *this;}private://内置类型int _year;int _month;int _day;};int main()
{Date d1;Date d2(2023, 11, 26);d1.operator(d2);d1 d2;return 0;
}
调试结果
赋值前 赋值后 2.在进行自定类型赋值时用户没有显示实现重载时编译器会生成一个默认赋值运算符重载以值的方式逐字节拷贝且默认规定赋值运算符重载是成员函数只能重载成类的成员函数不能重载成全局函数 若在全局区定义就会与默认生成的赋值运算符重载冲突从而报错。
在Date类中这里依然显示定义了赋值运算符重载来演示更加形象
#include iostream
using namespace std;class Time
{
public:Time(){_hour 1;_minute 1;_second 1;}Time operator(const Time dd)//返回值传引用{if (dd ! this)//防止给自己赋值{_hour dd._hour;_minute dd._minute;_second dd._second;}return *this;}private://内置类型int _hour;int _minute;int _second;};
class Date
{
public:Date operator(const Date t)//显示定义赋值运算符重载否则默认生成会调用自定义类型的成员函数{cout Date operator(const Date t) endl;_t t._t; // 调用Time类的赋值运算符重载return *this;}
private:int _year 2023;int _month 11;int _day 27;//自定义类型Time _t;
};int main()
{Date d1;Date d2;//d1.operator(d2);d1 d2;return 0;
}
运行及调试结果 这里由于_t t._t;这条语句在显示定义的赋值运算符重载内部所以会先调用该赋值运算符重载然后再去调用自定义类型的成员函数。如果没有显示写该赋值运算符重载编译器会在Date类中默认生成一个赋值运算符重载然后去调用自定义类型的成员函数。 3.跟拷贝构造析构一样如果类中有涉及到深拷贝的资源管理就必须要自己去写赋值运算符重载这里就不在过多演示了。 5.3前置和后置重载 前置原型 返回值类型 operator();
#include iostream
using namespace std;class Date
{
public:Date operator(){_day 1;return *this;//this指针指向的是d1对象该函数结束时该对象不会销毁因为该对象在main函数中//为了防止调用拷贝构造提高效率就传引用返回}
private:int _year 2023;int _month 11;int _day 27;};int main()
{Date d1;Date d2;d1 d1;return 0;
}
调试结果 前置实现了对d1对象中的天数进行前置重载只有一个隐藏的this指针参数返回值就是this指针指向的对象。问题来了那么对于后置该怎么写然道也和前置的原型一样吗那怎么做区分我们规定运算符重载的原型是返回值类型 operator运算符所以后置重载的原型离不开这个规则但是又为了和前置做区分规定后置重载时在参数列表中多增加了一个int类型参数但调用函数时该参数不需要我们传值给它而由编译器自动传递。
后置的原型为返回值类型 operator(int);
#include iostream
using namespace std;class Date
{
public://Date operator()//{// _day 1;// return *this;//this指针指向的是d1对象该函数结束时该对象不会销毁因为该对象在main函数中// //为了防止调用拷贝构造提高效率就传引用返回//}Date operator(int){//因为后置是先使用再所以得另外创建一个对象该对象保存原来对象的值返回值也是该对象Date temp(*this);//将d1对象初始化temp对象编译器会默认生成拷贝构造并调用。this-_day 1;//这里才是d1对像的成员要1的地方return temp;//temp对象在该函数中创建函数结束该对象会销毁所以得传值返回}
private:int _year 2023;int _month 11;int _day 27;};int main()
{Date d1;Date d2;//d1 d1;d1 d1;//返回的temp对象给d1实质返回的是原来没有进行的d1对象赋值完后再进行则是改变d1对象成员的值return 0;
}
调试结果 六、const成员函数
拿代码分析来引出const成员函数
#include iostream
using namespace std;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;};int main()
{Date d1(2023, 11, 28);d1.print();return 0;
}
正常运行结果 对于以上代码是正常运行但是我再创建d2对象时加上const修饰即const Date d2(2023,11,28); const Date d2(2023, 11, 28);d2.print();
则编译结果 意思就是d2现在是一个const Date类型的对象而函数print();中的this指针类型是Date* const类型那么将d2的地址传给this指针时就是要将一个const Date*类型指向对象的值不能修改传给Date* const类型指向对象的值可以修改就是我们所说的权限放大了那么怎样才能将const Date类型对象传给this指针呢这里就引出了const修饰成员函数。
看代码
#include iostream
using namespace std;class Date
{
public:Date(int year, int month, int day){_year year;_month month;_day day;}void print(){cout print() endl;cout _year - _month - _day endl;}void print() const{cout print() const endl;cout _year - _month - _day endl;}
private:int _year;int _month;int _day;};int main()
{Date d1(2023, 11, 28);d1.print();const Date d2(2023, 11, 28);d2.print();return 0;
} 运行结果 结果可以发现d1对象调用了print();函数而d2对象调用的时print() const函数。
这里给出const成员的定义将const修饰的成员函数称之为const成员函数const修饰类成员函数实际修饰该成员函数隐含的this指针表明在该成员函数中不能对类的任何成员进行修改也也就是说被const修饰的成员函数this指针的类型变为了 const Date* const this。
const修饰成员函数原型返回值类型 函数名(参数) const 例如void print() const;
所以d2对象可以传给被const修饰的成员函数这算是权限的平移我们知道权限可以平移也可以缩小但是不能放大那么非const对象可以调用const成员函数就是权限的缩小。 所以在这里给出结论 1.能定义成const成员函数都应该定义这样const对象和非const对象都可以调用。 2.要修改成员变量的成员函数不能定义成const成员函数。 七、取地址及const取地址操作符重载 这两个运算符一般不需要重载编译器会默认生成取地址的重载只有特殊情况才需要重载比如想让别人获取指定的内容。
原型返回值类型 operator() 与 返回值类型 operator() const;
#include iostream
using namespace std;class Date
{
public:Date()//自定义构造函数{}Date* operator(){return this;}const Date* operator()const{return this;}
private:int _year;int _month;int _day;};int main()
{Date d1;const Date d2;cout d1 endl;cout d2 endl;return 0;
}
对于以上代码d2对象是一个const类型的对象而编译器默认生成的构造函数其中this指针默认是非常量类型指针所以得自己定义一个构造函数在自定义构造函数中this指针的类型取决于正在被构造的对象的类型。