近日写了一道题,遇到了新bug,使博主对C++的逻辑严密性有了更深的体会,特写此博客。本文标题是对两个bug的总结,即本文内容分为两部分。

题目如下:

设计一个CShape抽象类,类中包含纯虚函数

        从CShape类派生出三角形类CTriangle、矩形类CRectangle和椭圆类CEllipse

        使用一个公共接口计算三角形对象、矩形对象和椭圆对象的面积

        重载运算符>用于判断两个形状面积的大小,返回true或false

阅读完题目,显然CShape类中至少要包含计算面积的纯虚函数,再在派生出的形状类中重写计算面积的函数即可。题目最后一个要求是重载比较运算符>来实现判断两个形状面积的大小,由于形状具有随机性,且C++中允许类型兼容(即子类对象可以当做父类使用,子类可以赋给父类引用或父类指针),所以重载该运算符应该在基类CShape中进行,这样就可以实现对任意两个派生类对象进行比较。最后,每个派生类中都要添加足够描述形状特征的数据成员,只要有几何基础知识就知道该怎么做,这里就不赘述。根据以上分析,我初步写的代码如下:

#include<iostream>
#include<math.h>using namespace std;#define Pi 3.14159class CShape//抽象基类
{
public:virtual double area()=0;friend bool operator>(const CShape &a,const CShape &b){return a.area()>b.area();}
};class CTriangle:public CShape//三角形类
{
private://三条边长,半周长double a,b,c,p;
public:CTriangle(double m,double n,double l){a=m;b=n;c=l;p=(a+b+c)/2;}double area(){return sqrt(p*(p-a)*(p-b)*(p-c));}
};class CRectangle:public CShape//矩形类
{
private://长,宽double a,b;
public:CRectangle(double m,double n){a=m;b=n;}double area(){return a*b;}
};class CEllipse:public CShape//椭圆形类
{
private://半长轴,半短轴double a,b;
public:CEllipse(double m,double n){a=m;b=n;}double area(){return Pi*a*b;}
};int main()
{//构造测试用例CTriangle a(3,4,5);CRectangle b(3,3);//结果应为falsecout<< boolalpha << (a>b) <<endl;return 0;
}

看起来没有毛病,但是编译器居然报错了,报错信息为:对象含有与成员函数CShape::area不兼容的类型限定符,对象类型是const CShape

乍一看不明白这句话的意思,但是很显然问题出在area函数和重载运算符的参数类型上。强调对象类型是const CShape是有什么用意呢?我们知道,只要加上const关键字,就不能进行修改值(内存内容)的相关操作。那么传入重载运算符的两个常对象,都不能在重载运算符函数体内进行修改自身属性的操作。但是我写的area并没有修改对象属性啊,怎么会这样呢?后来我才知道,原来C++为了提高代码安全性,以及更好地预防误操作修改不允许修改的量,规定了这么一条规则:常对象只能调用常成员函数。常成员函数和一般的成员函数有什么区别呢?常成员函数的写法是"返回值类型 函数名(函数参数)const",与一般的成员函数相比末尾多了const,这个const的意思就是在函数体内不允许有修改对象属性的操作,也是一种保证,保证这个成员函数不会修改对象属性。于是bug如何解决就豁然开朗了,在所有定义area函数的代码行末尾加const就行了。修改后代码如下:

#include<iostream>
#include<math.h>using namespace std;#define Pi 3.14159class CShape
{
private:
public:virtual double area()const=0;//修改1friend bool operator>(const CShape &a,const CShape &b){return a.area()>b.area();}
};class CTriangle:public CShape
{
private:double a,b,c,p;
public:CTriangle(double m,double n,double l){a=m;b=n;c=l;p=(a+b+c)/2;}double area()const//修改2{return sqrt(p*(p-a)*(p-b)*(p-c));}
};class CRectangle:public CShape
{
private:double a,b;
public:CRectangle(double m,double n){a=m;b=n;}double area()const//修改3{return a*b;}
};class CEllipse:public CShape
{
private:double a,b;
public:CEllipse(double m,double n){a=m;b=n;}double area()const//修改4{return Pi*a*b;}
};int main()
{CTriangle a(3,4,5);CRectangle b(3,3);cout<< boolalpha << (a>b) <<endl;return 0;
}

代码正常运行,bug1解决。然后我又想,能不能把比较运算符重载为成员函数而不是友元函数。于是我对代码再次做改动。此处只附上做了改动的代码:

class CShape
{
private:
public:virtual double area()const=0;bool operator>(const CShape &a,const CShape &b){return a.area()>b.area();}
};

编译器又报了错,报错信息为:此运算符函数的参数太多

乍一看我又愣住了,参数何以太多?比较运算符要比较两个对象,必然需要两个参数啊。然后我意识到一件事:我已经把这个运算符重载为成员函数。何谓成员函数?就是只有属于成员函数所在的类的对象才能调用的函数。——就是说,我必须要用本类对象才能调用成员函数。举个例子,当我们重载运算符>为成员函数,执行a>b这个语句时,实际情况是对象a在调用这个函数。所以,运算符>前面的对象是不用传参的,学术一点、专业一点的说法就是第一个参数通过this指针隐式传递!那么这个bug如何解决就显而易见,删去参数列表第一个参数,并把函数体内第一个参数出现的地方改为this指针就行了。修改后代码如下:

class CShape
{
private:
public:virtual double area()const=0;bool operator>(const CShape &b){//两种写法都可以//return (*this).area()>b.area();return this->area()>b.area();}
};

代码正常运行,bug2解决。

最后,再总结一下重载运算符为友元函数和成员函数的区别:前者的本质是一个全局函数,后者的本质是只允许本类对象调用的成员函数;前者没有this指针,所以必须要传递所有参数,后者是成员函数,有this指针,不用也不应该传递第一个参数,同时要保证调用时第一个参数是本类对象;如果重载双目运算符,当运算符第一个参数不是本类对象时,只能重载为友元函数,如果希望实现双目运算符第一个参数既可以是本类对象,也可以不是本类对象,需要重载该运算符两次,一次重载为成员函数,一次重载为友元函数。比如:

//A泛指某个类,x泛指A类中某个数据成员
A operator+(float b)//实现支持a+1.2之类操作,且仅支持此类操作
{A a;a.x=(*this).x+b;return a;
}
//实现支持1.2+a之类操作,且仅支持此类操作
friend A operator+(float b,A a)
{A c;c.x=b+a.x;return c;
}

C++之常引用对象只能调用常成员函数、重载为成员函数和友元函数的区别相关推荐

  1. 常对象只能调用常函数成员 c++

    关于 常对象只能调用常函数成员,不能调用非const 函数成员问题,我有点想法 就是我想到的一个角度来解释为什么. 你看,常函数成员的特性就是要在函数后面加一个const 修饰关键字 以  int g ...

  2. const对象只能调用const成员函数、不能调用非const成员函数;非const对象可以调用const成员函数

    引发原因: 由调用成员函数时隐式传入的当前对象的this指针引起. 1. 非const成员函数中的隐式参数:classA* this 2. const成员函数中的隐式参数:const classA* ...

  3. C++中的常引用、常对象、常函数、常数据成员

    1.常引用 用const声明的引用就是常引用.常引用所引用的对象不能被更改.经常见到的是常引用作为函数的形参,这样不会发生对实参的误修改.常引用的声明形式为:const 类型说明符 &引用名. ...

  4. 【C++---16】常指针和常引用

    Coordinate coor1(3,5); //定义一个普通对象const Coordinate &coor2 = coor1; //coor2是对象的常引用const Coordinate ...

  5. C++基础知识 —— 内存分区模型、引用、函数重载、类和继承、this指针、友元、多态、文件操作

       参考 黑马程序员 C++课程笔记,个人理解及整理  可以使用 在线编译c++代码 来简单验证.学习代码 目录 C++核心编程 1. 内存分区模型 1.1 程序运行前 1.2 程序运行后 1.3 ...

  6. c++从入门到精通——面向对象初探以及友元函数、对象

    面向对象 每个对象内存地址独一无二,空对象分配一个字节空间 #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespa ...

  7. 常引用、常对象和对象的常成员

    常引用 用const声明的引用就是常引用.常引用所引用的对象不能被更改.我们经常见到的是常引用作为函数的形参,这样不会发生对实参的误修改. 常引用的声明形式为:const 类型说明符 &引用名 ...

  8. C++中的常对象与常引用

    目录 一.常对象 二.用const修饰的类对象 1.常成员函数 声明 注意 三.常引用 1.声明 2.应该在何时使用: 1.在函数中无需改变其值的参数,应该使用常引用. 2.复制构造函数的参数一般也宜 ...

  9. 【C++基础知识】常成员函数,常引用

    文章目录 const修饰类的成员函数--常成员函数 常引用 常成员函数和普通成员函数的访问特性比较 const修饰类的成员函数–常成员函数 将const修饰的类成员函数称之为const成员函数,con ...

最新文章

  1. axure怎么做5秒倒计时_如何用Axure做倒计时,由分到秒?
  2. 自动化专业是不是计算机专业,自动化专业算不算计算机专业?
  3. 理解深度学习中的学习率及多种选择策略
  4. 18年石油大学c语言网考答案,石油大学华东C语言2018在线考试.doc
  5. python程序中怎样数个数_python3中的代码行数是怎么计算的?
  6. pooled-jms_Hibernate隐藏的宝石:pooled-lo优化器
  7. c语言error和,C语言ERROR精选.doc
  8. python3 一些常用的数学函数
  9. JQuery LazyLoad实现图片延迟加载-探究
  10. CNN反向传播算法过程
  11. Python 3.x中reduce()函数完整用法
  12. iOS开发之控制器的创建
  13. 安卓3.0之后的网络访问问题
  14. UnityShader13:渐变与遮罩
  15. [Flink]Flink DataStream API 概览
  16. 等差素数列 蓝桥杯 python
  17. python股票交易时间_Python量化交易,轻松获取股市每日龙虎榜详情数据
  18. 2017公共DNS服务器评估报告——公共DNS推荐(摘录)
  19. 与老师连这得计算机如何上网,观潮的老师教案
  20. 计算机病毒无法破坏压缩文件,求解压缩文件拒绝访问是怎么回事?

热门文章

  1. nnUnet 在windows 10 上使用
  2. ajax轮询是什么?怎么实现?
  3. matlab2014 图标,Ubuntu14.04 Matlab 2014b启动器(使用matlab自带图标)
  4. ark服务器公告消息,方舟生存进化新服开启公告 免费PVX服务器来临
  5. nginx-nginx详解
  6. Linux 文件重命名
  7. 98 Three.js 通过设置纹理属性来修改纹理贴图的位置和大小
  8. 2022前端相关面试题整理
  9. 阿里国际站的评论可以删除吗?要如何运营?
  10. 【PyTorch】PyTorch基础知识——自动求导