-
C++中的常成员函数以及const用于函数重载
2019-12-27 18:00:471.什么是常成员函数? 在类中可以含有普通成员函数和静态成员函数,而使用 const修饰的普通成员函数称为 常成员函数 const修饰的是普通成员函数中的this指针指向的内存空间 类的普通成员函数中都存在隐藏的 this...1.什么是常成员函数?
在类中可以含有普通成员函数和静态成员函数,而使用 const修饰的普通成员函数称为 常成员函数
const修饰的是普通成员函数中的this指针指向的内存空间
类的普通成员函数中都存在隐藏的 this指针,比如:
class Test { private: int a; int b; public: Test(int a,int b) { this->a = a; this->b = b; } void printT() { cout << "a = " << this->a << ",b = " << this->b << endl; } } //在C++编译器底层,void printT()函数是这样实现的 void printT(Test *const this) //是一个const型的指针,一旦实参传过来后,指针本身指向就不可以被修改 { cout << "a = " << this->a << ",b = " << this->b << endl; //类中的成员变量相当于结构体中的属性,用类定义对象T,相当于用 //结构体类型定义变量T, 然后对象T调用方法时 相当于结构体变量T调用方法,T此时是一个栈空间变量 //调用printT()函数方法时,T会将自己的变量内存空间首地址传给函数printT(),所以对象TT调用方法时相当于把自身的地址也传入了函数中作为一个参数,但C++编译器给我们隐藏了而已 }
这是C++底层绑定成员的方法,而常成员函数是指在此基础上再加一个 const 进行修饰,修饰 this指针指向的内存空间
void printT() const { cout << "a = " << this->a << ",b = " << this->b << endl; }
此时const修饰的是this指针所指向的内存空间,即指向的内存空间的量不可改变,映射到底层表现为
void printT(const Test *const this)
{
cout << "a = " << this->a << ",b = " << this->b << endl;
}绿色const是普通成员函数和常成员函数均有的,表示的是 指针本身的指向一旦确定不可修改,即 指针常量
而红色const是在普通成员函数括号后面编程 自己添加的,表示的是 指针指向的内存空间的值不可被修改,是一个常量指针,从而this指针是一个指向常量的常量指针
此时该成员函数称为 常成员函数2.常成员函数的特点:
由于常成员函数中 this指针指向常量,所以常成员函数不能够完成修改 类属性的值的操作,比如:
class Change: { private: int a; public: void changeA(int A) // #1 { this.a = A; } void changeA(int A) const // #2 { this.a = A; } }
在上方中,#2 的操作是错误的
同时还要注意:
3.
常成员函数 不能调用 普通成员函数(因为普通成员函数中可能出现更新成员属性值的操作),可以调用其他常成员函数
而普通成员函数是可以调用常成员函数的
4.const关键字来进行函数的重载,还是在上方change类中:
int getval(int val) { return val+3; } int getval(int val) const { return val - 3; }
而两者互为重载
规则是 常对象(实例化时必须用const进行修饰)调用 常成员函数, 普通对象调用 普通成员函数
5.其他关于const位置的小结
还是对于函数,const来修饰函数的返回值
const来修饰函数的返回值时 分为 指针返回,值返回
5.1当函数是指针返回时,该返回值只能返回给 用 const修饰的同类型的指针
const int* getval() { int* tmp = new int(10); return tmp; }
由于函数返回指针所指向的值不能被修改,所以
int* a = getval(); //编译错误
const int* b = getval(); //正确
5.2当函数是值返回时,由于返回的值要赋值给另外一个内存空间,所以此时加不加const影响不大
6.const 修饰成员变量
int const a;
const int a;
两者等价,定义常量a
const int* a;
int const* a;
两者等价,均为定义了 常量指针,即指针指向的值不可改变
int * const a;
定义了一个 指针常量,即 指针的指向不可改变
7.const修饰函数参数
const修饰函数参数时,就带来了一个问题。是否可以 根据 函数参数中的 const来进行函数重载,答案是可以的
主要看的就是 函数中的 参数是否 等价于一样,如果等价于一样,则编译器提示错误,函数重定义
如果两者不等价,则根据参数类型发生函数重载
比如:
#include<iostream> using namespace std; void fun(const int i) { cout << "fun(const int) called "; } void fun(int i) { cout << "fun(int ) called " ; } int main() { const int i = 10; fun(i); return 0; }
这样编译则会出错,提示重定义,而不是发生重载
原因在于 函数调用中存在实参和形参的结合,我们的实参 i=10; 不管有没有 const,都不会改变实参的值
再如:
#include<iostream> using namespace std; void fun(char *a) { cout << "non-const fun() " << a; } void fun(const char *a) { cout << "const fun() " << a; } int main() { const char *ptr = "hello world"; fun(ptr); return 0; }
其中, char* a 指向的是 字符串变量, const char * a指向的是 字符串常量,两者对于实参是不一样的,前者可以改变实参指向的值,而后者无法改变
但是对于下面的:
#include<iostream> using namespace std; void fun(char *a) { cout << "non-const fun() " << a; } void fun(char * const a) { cout << "const fun() " << a; } int main() { char ptr[] = "hello world"; fun(ptr); return 0; }
char* a 与 char* const a 前者是 指针变量,后者是指针常量, 但是由于 函数参数的 实参与形参结合, 两者对于实参来讲是一样的,并不会改变实参的值
所以,综上,函数参数中的 const是可以看做函数重载的标志,但是要视具体情况而言
此外,上面已经知道 带const的常成员函数和普通成员函数也是可以发生重载的
-
重载成员函数的常成员函数版本
2013-11-12 22:58:55(3)常成员函数不能更新类的成员变量,也不能调用该类中没有用const修饰的成员函数,只能调用常成员函数和常数据成员。 重点是第二条,const可以用来重载函数 它的效果很简单,如果类的实例是const的,就一定首先,复习一下const的用法
(1)const是函数类型的一部分,在实现部分也要带该关键字。
(2)const关键字可以用于对重载函数的区分。
(3)常成员函数不能更新类的成员变量,也不能调用该类中没有用const修饰的成员函数,只能调用常成员函数和常数据成员。
重点是第二条,const可以用来重载函数它的效果很简单,如果类的实例是const的,就一定要为要调用的函数重载相应的const版本
否则
就会破坏实例的const性质(普通成员函数可以改变对象里的成员)
而且const实例里面的this指针也是const的,在调用了一般的函数之后返回的是一个普通的this指针,相当于要把原来的
const this改成非const的
编译器肯定不会同意的。。。
测试代码:
#include<iostream> using namespace std; class Test { public: int& fun1(int &a) { cout<<"fun1"<<endl; return a; } //const int& fun1(int &a)const //{ // cout<<"fun1 const"<<endl; // return a; //} }; int main() { Test t; Test const t1; int a = 10; t.fun1(a); t1.fun1(a);//这里会报错 :error C2662: “Test::fun1”: 不能将“this”指针从“const Test”转换为“Test &” return 0;
-
C++之常引用对象只能调用常成员函数、重载为成员函数和友元函数的区别
2020-05-16 18:18:30设计一个CShape抽象类,类中包含纯虚函数 从CShape类派生出三角形类CTriangle、矩形类CRectangle和椭圆类CEllipse 使用一个公共接口计算三角形对象、矩形对象和椭圆对象的面积 重载运算符>用于判断两个...近日写了一道题,遇到了新bug,使博主对C++的逻辑严密性有了更深的体会,特写此博客。本文标题是对两个bug的总结,即本文内容分为两部分。
题目如下:
设计一个CShape抽象类,类中包含纯虚函数
从CShape类派生出三角形类CTriangle、矩形类CRectangle和椭圆类CEllipse
使用一个公共接口计算三角形对象、矩形对象和椭圆对象的面积
重载运算符>用于判断两个形状面积的大小,返回true或false
阅读完题目,显然CShape类中至少要包含计算面积的纯虚函数,再在派生出的形状类中重写计算面积的函数即可。题目最后一个要求是重载比较运算符>来实现判断两个形状面积的大小,由于形状具有随机性,且C++中允许类型兼容(即子类对象可以当做父类使用,子类可以赋给父类引用或父类指针),所以重载该运算符应该在基类CShape中进行,这样就可以实现对任意两个派生类对象进行比较。最后,每个派生类中都要添加足够描述形状特征的数据成员,只要有几何基础知识就知道该怎么做,这里就不赘述。根据以上分析,我初步写的代码如下:
#include<iostream> #include<math.h> using namespace std; #define Pi 3.14159 class 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); //结果应为false cout<< 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.14159 class CShape { private: public: virtual double area()const=0;//修改1 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()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; }
-
常成员函数和静态成员函数
2018-03-12 19:35:51常成员函数具有以下特殊性质 不能更新成员变量的值(静态成员变量除外)不能调用除了常成员函数以外的成员函数可以用来区分函数重载类的常对象只能访问类的常成员函数 class A{ private: int w, h; publi...常成员函数,声明方式:类型标识符 函数名(形参列表)const
类的成员函数的一种类型,由于const是函数类型,故在函数实现的时候也应带上const关键字。常成员函数具有以下特殊性质- 不能更新成员变量的值(静态成员变量除外)
- 不能调用除了常成员函数以外的成员函数
- 可以用来区分函数重载
- 类的常对象只能访问类的常成员函数
class A{
private: int w, h; public: int getValue()const; int getValue(); A(int x, int y):w(x), h(y){} A(){} ~A(){} }; int A::getValue()const{ return w*h; } int A::getValue(){ return w+h; } int main() { const A a(1, 2); A c(1,2); cout << a.getValue() << endl; //调用const成员函数 cout << c.getValue() << endl; //调用非const成员函数 return 0; }
静态成员函数 ,声明方式:static 类型标识符 函数名(形参列表)
静态成员函数只能被定义一次,由类的全体对象共用,属于类的行为,与具体对象无关,而本身也具有如下的特殊性质- 不可以直接访问类中的非静态成员变量和非静态成员函数,只能通过形参传入的对象名来进行访问。
- 不可以 在函数实现时加static关键词,否则会出错
- 静态成员函数不含this指针,可以在类外通过“类名::函数名”的方式直接调用。也可以通过对象调用
- class B{
- private:
- int x;
- int y;
- static int count;
- public:
- B():x(0), y(0){
- count++;
- }
- B(int xx, int yy):x(xx), y(yy){
- count++;
- }
- static int getObjCount();
- };
- int B::count = 0;
- int B::getObjCount(){
- return count;
- }
- int main()
- {
- cout << B::getObjCount() << endl;
- B b1;
- B b2(10, 20);
- cout << b1.getObjCount() << endl;
- cout << b2.getObjCount() << endl;
- cout << B::getObjCount() << endl;
- return 0;
- }
-
C++:常对象成员和常成员函数
2020-03-03 13:07:045-1 常对象成员和常成员函数 例子: 常对象成员: 常成员函数: 上图:常成员函数中为什么不能改变数据成员的值? 上图:编译后this指针是用const指针修饰的,成了一个常指针,通过常指针改变指针指向的... -
C++类的常成员函数以及静态成员函数
2016-02-15 11:37:111 常成员函数 1.1声明:函数名(形参列表)const; 1.2说明: 1)const是函数类型的一部分,在实现部分也要带上该关键字; 2)const关键字可以用于对重载函数的区分; 3)常成员函数不能更新类的成员变量,也不... -
C++常成员函数
2018-09-28 20:09:13(3)常成员函数不能更新类的成员变量,也不能调用该类中没有用const修饰的成员函数,只能调用常成员函数。 A、通过例子来理解const是函数类型的一部分,在实现部分也要带该关键字。 #include ... -
C++ const常成员函数
2019-12-30 15:04:25C++ const常成员函数 使用const关键字修饰的函数为常成员函数,常见声明如下: 类型说明符 函数名(参数表) const; 注意 const关键字可以用于对重载函数的区分。例如在类中这样声明: void print(); void ... -
c++类中常成员与常成员函数
2017-12-27 22:30:36常成员函数: 常成员函数中不能改变数据成员的值! 因为: void changeX(const Coordinate *this) this作为常指针不能改变 xxx const 与 xxx 互为重载 如何调用常成员函数: 通过const实例化对象,叫做常... -
C++远征之封装篇——常对象成员、常成员函数
2017-04-21 23:33:24一、常对象成员 二、常成员函数 ...(1)常成员函数中不能修改数据成员的值 ...每个函数都默认带了一个this指针的,该指针指向该类的实例。当使用const来修饰时,其实是const ...(3)常成员函数和普通成员函数重载的 -
常成员函数
2013-06-16 02:59:38使用const的函数 形式:返回值类型 函数名(参数表) const 作用: 1、const是函数的一部分,在函数的实现部分也要带上 2、const可以用来区分函数重载。即:int add(int a,int b);... 3、常成员函数不 -
C++如何显式调用常成员函数
2019-10-03 20:04:51C++的常成员函数与同名成员函数重载时,该如何显式调用常成员函数? 具体的一个小例子: #include <iostream> using namespace std; class C1 { public: void fun() { cout<<1<&... -
14.0 C++远征:常对象成员和常成员函数
2016-11-18 11:15:005-1常对象成员和常成员函数 1.const修饰的常量需要初始化列表来初始化 2.const除可修饰普通的数据成员外,还可以修饰对象成员和成员函数 3.const成员函数中不能修改数据成员的值,因为该函数的隐藏指针*this也被... -
C++常成员函数 - const 关键字
2015-06-12 22:58:35C++常成员函数 - const 关键字 一、常成员函数详解 声明:函数名(参数表)const; 说明: (1)const是函数类型的一部分,在实现部分也要带该关键字。 (2)const关键字可以用于对重载函数的区分... -
C++点滴----关于类常成员函数
2018-06-23 22:11:22函数名称(参数表) const一些说明:1、const是函数声明的一部分,在函数的实现部分也需要加上const2、const关键字可以重载函数名相同但是未加const关键字的函数3、常成员函数不能用来更新类的成员变量,也不能调用类... -
C++常成员函数和常对象、对象指针和对象引用
2017-09-28 23:45:45常成员函数 注意: 常成员函数不允许调用普通成员函数 普通的数据成员,但是没有改数据的权限 为什么常成员函数不允许赋值操作? 其可以使用 m iX = 10; X" width="600" style="height:auto"> 从上述例子可以看出,... -
常成员函数与常对象的使用
2012-04-12 12:46:51#include #include using namespace std; class person { private: int age; char *name; public : person(int ,char *);...//重载函数,用于输出的普通成员函数 void print() const;//重载函数,用于输出的 -
c++函数重载详解
2019-06-12 12:54:24先是简单理解下函数重载的概念:同一域内的同名函数在函数参数类型,参数个数,参数顺序上具有不同,则互为重载。 ps:函数重载不包括函数返回值... 常成员函数的重载: class Test{ //成员函数func void fun... -
C++中const用于函数重载的示例代码
2021-01-20 06:35:25常成员函数和非常成员函数之间的重载 首先先回忆一下常成员函数 声明:<类型标志符>函数名(参数表)const; 说明: (1)const是函数类型的一部分,在实现部分也要带该关键字。 (2)const关键字可以用于对重载... -
C++中const用于函数重载的用法
2020-09-28 11:26:27(3)常成员函数不能更新类的成员变量,也不能调用该类中没有用const修饰的成员函数,只能调用常成员函数。 (4)非常量对象也可以调用常成员函数,但是如果有重载的非常成员函数则会调用非常成员函数 例子1,... -
C++中const用于函数重载
2017-09-29 09:22:00C++中const用于函数重载 常成员函数和非常成员函数之间的重载 首先先回忆一下常成员函数 ...(3)常成员函数不能更新类的成员变量,也不能调用该类中没有用const修饰的成员函数,只能调用常成员函数。 ... -
C++函数重载详解及实例代码
2020-12-31 04:26:40常函数和普通成员函数之间构成重载,重载时常对象调用常成员函数,一般对象调用一般成员函数 class A{ … public: void getVal()const{…} void getVal(){…} }; int main(){ const A a; A b; a.getVal(); ... -
C++函数重载中的const
2019-09-02 20:45:01常成员函数和非常成员函数之间的重载 首先先回忆一下常成员函数 声明:<类型标志符>函数名(参数表)const; 说明: (1)const是函数类型的一部分,在实现部分也要带该关键字。 (2)const关键字可以用于对...
-
数据清洗
-
Mycat 实现 MySQL的分库分表、读写分离、主从切换
-
鲇鱼2000-2500.rar yolo数据集
-
中国区块链企业发展普查报告 2020.pdf
-
华莱士树的一种实现方式(做项目要用,不骗积分).rar
-
phpStudy所需运行库.zip
-
2021 年该学的 CSS 框架 Tailwind CSS 实战视频
-
MySQL 备份与恢复详解(高低版本 迁移;不同字符集 相互转换;表
-
PPT大神之路高清教程
-
JDK源码解析 迭代器模式在JAVA的很多集合类中被广泛应用,接下来看看JAVA源码中是如何使用迭代器模式的。
-
MoneyTreeTM中国TMT报告 2021年一季度二季度.pdf
-
7.简单加减乘除
-
RSA与AES的加解密(Java)
-
opengles4android绘制点、线、三角形、立方体,相机实时预览等等实践
-
ELF视频教程
-
python——列表相关(倒序遍历list+删除列表中指定值)
-
wiif+bt模块esp32学习
-
国家商用加密算法 SMx(SM2,SM3,SM4)
-
JSP技术(上)
-
妙用linux strings命令