c++中const关键字的使用完整攻略
c++中const关键字,将一个变量声明为常量,在这个基础上进行编程,通过这个关键字可以区分两个成员函数,限制成员函数的行为,并且由于定义的不同位置,而又不尽相同的意义。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 using namespace std; 3 class Base{ 4 public: 5 Base(const int a):elem(a){ 6 cout<<"base const"<<endl; 7 } 8 void test(){} 9 int mul(const int a)const 10 { 11 cout<<"base int mul(const int a)const "; 12 return a*elem; 13 } 14 private: 15 int elem; 16 }; 17 18 int main(){ 19 int te = 2; 20 Base *p = new Base(te); 21 int temp = 2; 22 const int a = temp; 23 temp = p->mul(a); 24 cout<<temp<<endl; 25 return 0; 26 }
在Base的构造方法中,形参表的成员以const修饰,这就说明这个函数不会修改传入的实参。如果在调用的时候出现non-const的形参,也会调用这个方法;如果构造方法不加const修饰形参,而使用时以const实参加载,也会正常调用这个方法。
这是一个简单的应用,在一个成员函数的尾部加上const关键字,意味着定义了一个const成员函数,const成员函数不能修改任何数据成员,也不能调用任何non-const成员函数。
如果在const函数中调用non-const成员函数,或是修改成员变量,编译器会主动提示错误。
vs2008中提示的错误就是 "左值指定 const 对象"。
在本例当中如果mul方法调用test()方法,或是修改成员变量elem的值,编译器就会主动提示错误。
程序执行结果:
base const
base int mul(const int a)const 4
请按任意键继续. . .
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 using namespace std; 3 class Base{ 4 public: 5 Base(const int a):elem(a){ 6 cout<<"base const"<<endl; 7 } 8 void test(){cout<<"test";} 9 int mul(const int a)const 10 { 11 cout<<"base int mul(const int a)const "; 12 //elem = 3; 13 //test(); 14 return a*elem; 15 } 16 /* 17 int mul(const int a) 18 { 19 cout<<"base int mul(const int a)const "; 20 elem = 3; 21 test(); 22 return a*elem; 23 } 24 */ 25 int mul(int &a) 26 { 27 test(); 28 cout<<"base int mul(int &a) "; 29 return elem = a*elem; 30 } 31 private: 32 int elem; 33 }; 34 int main(){ 35 Base *p = new Base(2); 36 int temp = 2; 37 const int a = temp; 38 temp = p->mul(a); 39 cout<<temp<<endl; 40 int &b = temp; 41 temp = p->mul(b); 42 cout<<temp<<endl; 43 return 0; 44 }
在一个类中,定义多个函数,有相同的函数名,以及不尽相同的形参表,在这种情况下,函数调用总是选择最为接近的那个。——这是函数重载的调用规则。
但是对于const关键字并不适用,const关键字对于函数调用采取的是不同的调用策略。
同时定义const形参方法和non-const形参方法
1 void print(int &a,int &b,int &c,int &d){ 2 cout<<"non-const"<<a<<"non-const"<<b<<"non-const"<<c<<"non-const"<<d<<endl; 3 } 4 void print(const int &a,const int &b,const int &c,const int &d)const 5 { 6 cout<<"const"<<a<<"const"<<b<<"const"<<c<<"const"<<d<<endl; 7 }
如果我们定义4个const int引用和4个non-const int引用,然后调用这两个函数,产生的结果就完全不是以重载考虑问题所想象的 结果了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 using namespace std; 3 class Base{ 4 public: 5 Base(const int a):elem(a){ 6 cout<<"base const"<<endl; 7 } 8 void print(int &a,int &b,int &c,int &d){ 9 cout<<"non-const"<<a<<"non-const"<<b<<"non-const"<<c<<"non-const"<<d<<endl; 10 } 11 void print(int &a,int &b,const int &c,const int &d)const 12 { 13 cout<<"non-const"<<a<<"non-const"<<b<<"const"<<c<<"const"<<d<<endl; 14 } 15 void print(const int &a,const int &b,const int &c,const int &d)const 16 { 17 cout<<"const"<<a<<"const"<<b<<"const"<<c<<"const"<<d<<endl; 18 } 19 private: 20 int elem; 21 }; 22 int main(){ 23 Base *p = new Base(0); 24 int nconst_a=1,nconst_b=2,nconst_c=3,nconst_d=4; 25 const int &const_a=1,&const_b=2,&const_c=3,&const_d=4; 26 p->print(nconst_a,nconst_b,nconst_c,nconst_d);//4-0 27 p->print(nconst_a,nconst_b,nconst_c,const_d);//3-1 28 p->print(nconst_a,nconst_b,const_c,const_d);//2-2 29 p->print(nconst_a,const_b,const_c,const_d);//1-3 30 p->print(const_a,const_b,const_c,const_d);//0-4 31 return 0; 32 }
上述代码在主函数中调用这两个成员函数,在这里分别加载0-4个const成员变量,如果按照重载的定义规则,这上下各有两个调用相似类型较多的函数,中间的函数会出现一个不确定操作进而编译器不让我们用这种东西折磨他。。。
不过很奇怪的是,如果把这个代码提交编译,编译器都是十分乐意地完成了compile和link,然后毫不客气的给出了与我们预期完全不同的运行结果。
程序运行结果:
base const
non-const1non-const2non-const3non-const4
non-const1non-const2const3const4
non-const1non-const2const3const4
const1const2const3const4
const1const2const3const4
请按任意键继续. . .
只要有const成员出现,就要调用调用满足const成员的最接近方法。
不过,如果没有函数能够提供针对这么多的const 实参的成员函数,这时候编译器就会突然决定:
你玩蛋去吧!!!
1>Const.cpp
1>d:\backup\我的文档\visual studio 2008\projects\test_const\test_const\const.cpp(25) : error C2664: “void Base::print(int &,int &,int &,int &)”: 不能将参数 2 从“const int”转换为“int &”
1> 转换丢失限定符
1>d:\backup\我的文档\visual studio 2008\projects\test_const\test_const\const.cpp(26) : error C2664: “void Base::print(int &,int &,int &,int &)”: 不能将参数 1 从“const int”转换为“int &”
1> 转换丢失限定符
所以const不是重载,const成员函数如果不会改变一个实参,就尽可能将其设为const,毕竟在形参表中定义的const关键字,意味着对于这个位置的这个类型的数据,朕老少通吃。
c++作为一个强大的语言,意味着他不单单能够吃饭睡觉挣钱,还能找妹子生孩子,而且和Java的计划生育政策不同的是,c++只要你能够优生,是不会禁止你生多少个孩子,养多少个孙子的。。。
所以在面向对象的时候,子类对父类的操作,对父类方法的继承和重写,完全遵循c++面向对象的规定。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 using namespace std; 3 class Base{ 4 public: 5 Base(const int a):elem(a){ 6 cout<<"base const"<<endl; 7 } 8 void test(){cout<<"test ";} 9 virtual int mul(const int &a) 10 { 11 test(); 12 cout<<"base int mul(const int &a) "; 13 return elem = a*elem; 14 } 15 virtual int mul(int &a) 16 { 17 test(); 18 cout<<"base int mul(int &a) "; 19 return elem = a*elem; 20 } 21 private: 22 int elem; 23 }; 24 class Derived:public Base{ 25 public: 26 Derived(const int a):Base(a){ 27 cout<<"derived const"<<endl; 28 } 29 int mul(const int &a) 30 { 31 test(); 32 cout<<"derived int mul(const int &a) "; 33 return a; 34 } 35 int mul(int &a) 36 { 37 test(); 38 cout<<"derived int mul(int &a) "; 39 return a; 40 } 41 }; 42 int main(){ 43 Base *p = new Base(2); 44 Base *q = new Derived(3); 45 int temp = 2; 46 const int a = temp; 47 temp = p->mul(a); 48 cout<<temp<<endl; 49 temp = p->mul(temp); 50 cout<<temp<<endl; 51 cout<<q->mul(temp)<<endl; 52 cout<<q->mul(a)<<endl; 53 return EXIT_SUCCESS; 54 }
程序的执行结果毫不出乎我们的意料之外,完全按照继承派生——找妹子生孩子这个套路进行函数调用,父类对象调用父类方法,子类对象使用父类指针范围,但是照样使用子类的方法。
程序运行结果:
base const
base const
derived const
test base int mul(const int &a) 4
test base int mul(int &a) 16
test derived int mul(int &a) 16
test derived int mul(const int &a) 2
请按任意键继续. . .
如果基类的成员函数被继承类重写了一个父类方法,然后没有重写另一个方法,这时候调用就会显得十分有趣而符合逻辑。
c++继承的过程相当于子类隐式增加了父类的方法,如果子类不重写这个方法,这个方法就是实际存在的而且与子类的其他方法完全相同。
父类有const版本和non-const版本的成员函数,如果子类重新non-const版本,则在调用const版本的时候就必须调用父类的const版本的成员函数——因为如果调用成员函数时候传入const对象,意味着程序员知道这个对象是有const版本的成员函数的,无论他在子类还是父类。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 using namespace std; 3 class Base{ 4 public: 5 Base(const int a):elem(a){ 6 cout<<"base const"<<endl; 7 } 8 void test(){cout<<"test ";} 9 virtual int mul(const int &a) 10 { 11 test(); 12 cout<<"base int mul(const int &a) "; 13 return elem = a*elem; 14 } 15 virtual int mul(int &a) 16 { 17 test(); 18 cout<<"base int mul(int &a) "; 19 return elem = a*elem; 20 } 21 private: 22 int elem; 23 }; 24 class Derived:public Base{ 25 public: 26 Derived(const int a):Base(a){ 27 cout<<"derived const"<<endl; 28 } 29 int mul(int &a) 30 { 31 test(); 32 cout<<"derived int mul(int &a) "; 33 return a; 34 } 35 }; 36 int main(){ 37 Base *p = new Base(2); 38 Base *q = new Derived(3); 39 int temp = 2; 40 const int a = temp; 41 temp = p->mul(a); 42 cout<<temp<<endl; 43 temp = p->mul(temp); 44 cout<<temp<<endl; 45 cout<<q->mul(temp)<<endl; 46 cout<<q->mul(a)<<endl; 47 return EXIT_SUCCESS; 48 }
程序运行结果:
base const
base const
derived const
test base int mul(const int &a) 4
test base int mul(int &a) 16
test derived int mul(int &a) 16
test base int mul(const int &a) 6
请按任意键继续. . .
问题出现在子类重写const版本的成员函数的时候,如果子类不知道父类有哪些方法,自己写了一个const版本的方法,然后就期待着这个方法老少通吃,这时候在子类调用non-const版本的方法的时候,理所当然的会调用隐式存在的non-const版本的方法,这时候对于构建子类的程序员就出现了一个无法理解的现象,只定义了一个const版本的成员函数,由于父类中存在non-const版本的函数,所以无法访问自己希望访问的方法。如果重新一个子类的non-const版本完全相同的成员函数,我们就相当于做了一个复制粘贴的工作,增加了代码的维护代价。
在这里就给出一个建议:如果一个类有可能被继承,则这个父类如果并无必要使用一个non-const版本的成员函数,就尽量不定义这个函数,或是定义一个const版本的,子类需要自定义的const版本的时候,就可以重写;如果子类需要自定义non-const版本的时候,就可以重载。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 using namespace std; 3 class Base{ 4 public: 5 Base(const int a):elem(a){ 6 cout<<"base const"<<endl; 7 } 8 void test(){cout<<"test ";} 9 virtual int mul(const int &a) 10 { 11 test(); 12 cout<<"base int mul(const int &a) "; 13 return elem = a*elem; 14 } 15 virtual int mul(int &a) 16 { 17 test(); 18 cout<<"base int mul(int &a) "; 19 return elem = a*elem; 20 } 21 private: 22 int elem; 23 }; 24 class Derived:public Base{ 25 public: 26 Derived(const int a):Base(a){ 27 cout<<"derived const"<<endl; 28 } 29 int mul(const int &a) 30 { 31 test(); 32 cout<<"derived int mul(const int &a) "; 33 return a; 34 } 35 }; 36 int main(){ 37 Base *p = new Base(2); 38 Base *q = new Derived(3); 39 int temp = 2; 40 const int a = temp; 41 temp = p->mul(a); 42 cout<<temp<<endl; 43 temp = p->mul(temp); 44 cout<<temp<<endl; 45 cout<<q->mul(temp)<<endl; 46 cout<<q->mul(a)<<endl; 47 return EXIT_SUCCESS; 48 }
程序运行结果:
base const
base const
derived const
test base int mul(const int &a) 4
test base int mul(int &a) 16
test base int mul(int &a) 48
test derived int mul(const int &a) 2
请按任意键继续. . .
而const 对象调用不同版本的成员函数时候,是通过成员函数的修饰符来决定调用方式的。
1 #include <iostream> 2 using namespace std; 3 class Base{ 4 public: 5 Base():size(0),info(NULL){} 6 Base(int s,int* p):size(s),info(p){} 7 Base(const Base &obj){ 8 size = obj.size; 9 info = obj.info; 10 } 11 Base& operator = (const Base &obj){ 12 size = obj.size; 13 info = obj.info; 14 return *this; 15 } 16 bool operator == (const Base &obj){ 17 cout<<"bool operator == (const Base &obj)"<<endl; 18 if(size != obj.size) 19 return false; 20 if(*info != *obj.info) 21 return false; 22 return true; 23 } 24 bool operator == (const Base &obj) const { 25 cout<<"bool operator == (const Base &obj) const "<<endl; 26 if(size != obj.size) 27 return false; 28 if(*info != *obj.info) 29 return false; 30 return true; 31 } 32 void print() const; 33 private: 34 int size; 35 int* info; 36 }; 37 void Base::print() const { 38 int index = 0; 39 for(;index<size;index++){ 40 cout<<*(info+index)<<" "; 41 } 42 cout<<endl; 43 } 44 45 int main(){ 46 int *p = new int(3); 47 Base a(1,p); 48 const Base c(a); 49 const Base b(2,p); 50 cout<<(a==b)<<endl; 51 cout<<(c==b)<<endl; 52 return EXIT_SUCCESS; 53 }
主函数中调用==的两个重载版本,而const和non-const对象调用的是不同的版本。
bool operator == (const Base &obj)
0
bool operator == (const Base &obj) const
0
请按任意键继续. . .