ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C++基础篇 —— 类和对象(三)默认成员函数详解

C++基础篇 —— 类和对象(三)默认成员函数详解 文章目录1 类的默认成员函数1.1 赋值运算符重载1.1.1 运算符重载1.1.1.1 运算符重载形式1.1.1.2 为什么要有运算符重载1.1.1.3 不能重载的运算符1.1.1.4 运算符重载的注意点1.1.1.5 运算符中的冲突点1.1.1.6 要设计为全局函数的流1.1.2 赋值运算符重载1.1.2.1 赋值操作符重载的形式1.1.2.2 赋值运算符重载的作用1.1.2.3 编译默认生成的赋值运算符重载1.1.2.4 要写赋值运算符重载的情况1.2 取地址运算符重载1.2.1 const成员函数1.2.2 取地址运算符重载这里是think的博客希望可以一起交流知识一起think今天我们来学习C类和对象三一起来think1 类的默认成员函数六个默认成员函数中的另外三个赋值操作符重载取地址操作符重载两个。1.1 赋值运算符重载1.1.1 运算符重载1.1.1.1 运算符重载形式usingnamespacestd;#includeiostreamclassDate{public://private:int_year;int_month;int_day;};booloperator(constDated1,constDated2){returnd1._yeard2._yeard1._monthd2._monthd1._dayd2._day;}intmain(){Date d1;Date d2;if(d1d2){couttrueendl;}return0;}重载了运算符运算符重载形式和普通函数差不多一样区别在于运算符重载函数的函数名是operator运算符。1.1.1.2 为什么要有运算符重载运算符重载的出现是因为类类型也是需要±*/等等运算的操作的但是内置的运算符大多只适用于内置类型所以需要我们自己重载运算符以供类使用。1.1.1.3 不能重载的运算符那么什么运算符都可以重载吗自然不是成员访问操作符.域作用限定符::sizeof三目操作符成员指针访问操作符.*。其中只有成员指针访问操作符需要了解一下。usingnamespacestd;#includeiostreamclassA{public:voidfunc(){coutA::func()endl;}int_a;};typedefvoid(A::*PF)();//成员函数指针类型intmain(){//例1//C规定成员函数要加才能取到函数指针PF pfA::func;A obj;// 对象调用成员函数指针时使用.*运算符(obj.*pf)();return0;}普通函数名天然代表一个独立函数可以自动转换成地址而成员函数名 A::func 只是表示类中的成员函数为了避免“函数”和“函数指针”混淆有类的加入使转换变得麻烦并解决重载歧义重载使转换变得更加麻烦C要求必须显式写 A::func。这里的.*是一个二元操作符好像有点像先*再.的可以这么想如果只*的话这里是有问题的因为成员函数是不可以单独存在的要对象去调用所以.*必须在一起才可以调用。1.1.1.4 运算符重载的注意点运算符重载后优先级和结合性和内置的一样。重载运算符函数的参数个数和该运算符作用的运算对象数量⼀样多调用时有两个参数时左侧运算符传给第一个参数右侧第二个运算符重载作为成员函数的情况下比全局函数少一个参数全局两个参数成员就一个参数因为有一个this指针是编译器传的我们不用传。注意一个类不是所有操作符都要重载重载有意义的即可比如日期类中日期-日期有意义但是日期加日期没有。不可以重载操作符中没有的符号比如。不可以把有的重载运算符在内置类型不能调用的地方调用比如a内部实现的是后置逻辑这样是不行的因为内置类型的只有ab和a两种用法1.1.1.5 运算符中的冲突点运算符中分为前置和后置那么重载的时候如何区分由于前置在语义上拷贝比后置要少而内置类型的这点拷贝不算什么但是类类型可就大多了所以前置是正常形态而后置要加一个int参数。Dateoperator();//前置Dateoperator(int);//后置1.1.1.6 要设计为全局函数的流运算符重载最好设计为成员函数但是流设计为成员函数会不符号习惯。以流插入举例正常来说内置类型流插入是int a 1; couta;,其中cout是左参数而其中int类型是右参数而int变为Date类型呢是不是就是说cout要重载的话Date类型必须成为第二个参数而成员函数中this指针是默认的第一个参数所以为了保证使用习惯应该将cout设计为全局函数。ostreamoperator(ostreamout,constDated);istreamoperator(istreamin,Dated);还有一个问题就是如果流cout和cin成为了全局函数那么就不可以使用成员变量了方法有私有放开破坏了封装性提供Get函数麻烦但是可以让它们成员类的friend函数最方便的声明处前面加上friend表示是类的朋友函数可以访问对象中的私有成员。friendostreamoperator(ostreamout,constDated);friendistreamoperator(istreamin,Dated);1.1.2 赋值运算符重载1.1.2.1 赋值操作符重载的形式usingnamespacestd;#includeiostreamclassDate{public:Date(intyear1,intmonth1,intday1){_yearyear;_monthmonth;_dayday;}Date(constDated){cout Date(const Date d)endl;_yeard._year;_monthd._month;_dayd._day;}// 传引用返回减少拷⻉//const 减少拷贝Dateoperator(constDated){if(this!d){_yeard._year;_monthd._month;_dayd._day;}return*this;}voidPrint(){cout_year-_month-_dayendl;}private:int_year;int_month;int_day;};intmain(){Dated1(2024,7,5);Dated2(d1);Dated3(2024,7,6);d1d3;Date d4d1;return0;}赋值运算符重载的参数是const 是为了减少拷贝而赋值操作符的逻辑中不需要改变对象的值加上const以保证安全。赋值运算符重载的返回值是引用返回有返回值是因为赋值操作符重载要实现连续赋值传引用返回为了减少拷贝。1.1.2.2 赋值运算符重载的作用赋值运算符和拷贝构造相比功能差不了多少都是可以自己实现深拷贝解决C语言中只有浅拷贝的问题。区别在于拷贝构造是已经创建的对象拷贝初始化另外一个将要创建的对象。而赋值重载是一个已经创建的对象拷贝赋值给另外一个已经创建的对象。1.1.2.3 编译默认生成的赋值运算符重载没有显示实现就会默认生成默认生成的赋值运算符重载和拷贝构造一样都是内置类型浅拷贝对于自定义类型调用它的赋值重载函数。1.1.2.4 要写赋值运算符重载的情况和拷贝构造一样分三种情况。没有资源的类只有间接资源的类有直接资源的类只有3是需要写的比如Stack2是Myqueue1是Date这两个不用自己写赋值运算符重载。1.2 取地址运算符重载1.2.1 const成员函数const对象是无法调用普通成员函数的因为普通成员函数的第一个隐藏参数是Date* const this以日期类举例而const对象的类型是const Date* this如果const对象传递给第一个参数那么就权限放大导致报错。所以应该是const 对象应该传递的参数要是const Date* const this但是this指针又不能我们主动传递怎么办呢解决方法就是再参数列表的后面加上一个const即让编译器将第一个形参位置类型变为const Date* const thisvoid Print() const;其中Date* const this中的const是编译器在处理成员函数形参时加上的为了保证成员函数内不会有人改变this指针的指向导致报错。一般情况下如果成员函数内部不修改对象情况下最好加上const保证const对象和普通对象都可以调用。1.2.2 取地址运算符重载classDate{public:Date*operator(){returnthis;// return nullptr;}constDate*operator()const{returnthis;// return nullptr;}private:int_year;// 年int_month;// ⽉int_day;// ⽇};这两个一般不用写的如果不写上面那一个编译器不生成的话下面那个可以被普通对象和const对象调用但是实际上不写第一个编译器会默认生成 所以普通对象调用不到第二个因为有最合适的调用最合适的。
返回列表