c++ const 限定符
来源:互联网 发布:拼接控制器软件 编辑:程序博客网 时间:2024/06/05 12:48
c++ const 限定符
1.const引用与非const引用
const引用的含义是指向const对象的引用,非const引用表示指向非const类型的引用。
非const引用不可以绑定到const对象,例如:
const int iSize = 1024;
int &iRef = iSize;//error
将提示错误:
prog4.cpp(8) : error C2440: “初始化”: 无法从“const int”转换为“int &”
转换丢失限定符
非const引用只能绑定到同类型的非const对象(因为非const引用可以修改绑定的对象,如果绑定到const对象,那么又不能通过这个引用改变这个对象,这不是自相矛盾,所以不能绑定到const对象),const引用则可以绑定到不同但相关的类型的对象或者右值。
绑定到不同但相关类型对象,例如:
#include <iostream>//const引用绑定到不同但相关类型的对象int main(){double dVal = 3.14;const int &ri = dVal;//int &ref = dVal;//error,无法从“double”转换为“int &”std::cout<<"ri:"<<ri<<std::endl;//print 3std::cout<<"dVal:"<<dVal<<std::endl;//print 3.14return 0;}
这里ri绑定到dVal并不影响dVal本身,只是编译器构造了临时变量,即实际效果等价于代码:
Int tmp = dVal;//cast to int
const int &ri = tmp;//bind to temporary variable
2.指针和const限定符
1)指向const对象的指针——不能用来修改其指向的对象的值
一般使用的指针,允许我们通过指针修改其指向对象的值。如果指针指向的是const对象,则不允许用指针来修改其所指对象的值,这就需要将指针定义为指向const对象的指针。指向const对象的指针确保不能用来修改基础对象。
注意,指向const对象的指针,不允许修改其指向的对象的值,但是允许给指针重新赋值从而指向另一个const对象。同时注意,不能使用void*保存const对象的地址,必须使用const void*保存。
在实际应用中,指向const的指针常用作函数的形参,以确保传递给函数的实际对象在函数中不因为形参而被修改。
2)const指针——本身的值不可修改
const指针的本身的值不可修改,但是并没有说明能否使用该指针修改它指向对象的值。指针所指对象的值能否修改完全取决于该对象的类型。
3)指向const对象的const指针——本身和所指向对象的值均不能改变
这种类型的指针,一经定义就不能做任何更改,既不能改变指向,也不能改变其指向对象的值。例如:
const double pi = 3.14;
const double* const pi_ptr = π
从右至左阅读,解释为pi_ptr首先是一个const指针,指向double类型的const对象。当然,这里确实非常混淆。
const限定符和指针,可参见下例:
#include <iostream>//指向const对象的指针和const指针int main(){ const double pi = 3.14;double exp = 2.718;const double *cptr;//指向const对象的指针,在定义时不强制初始化cptr = πstd::cout<<*cptr<<std::endl;cptr = &exp;//允许重新绑定到另一个const对象std::cout<<*cptr<<std::endl;//double *ptr = π//错误,无法从“const double *”转换为“double *”//void *pv = π//错误,无法从“const double *”转换为“void *”const void *cpv = π//使用const void*保存const对象的地址std::cout<<*((double*)cpv)<<std::endl; double *const cpd =&exp;//const指针 *cpd = 2.7;//通过cosnt指针修改原对象的值std::cout<<exp<<std::endl;//cpd = &exp;//错误,不能给常量赋值const double *const cptr_cd = π//指向const对象的const指针 std::cout<<*cptr_cd<<std::endl; return 0;}
输出:
3.14
2.718
3.14
2.7
3.14
3.函数参数和const
1) const引用形参。
如果使用引用形参的唯一目的在于避免复制实参,即不修改相应的实参,则应该将其定义为const引用,否则const实参、字面值常量将无法调用此函数,因为非const引用形参只能与完全同类型的非const对象关联(若与const对象关联,那么通过非const引用形参去修改实参时会发生错误)。
例如:
#include <iostream>#include <string>using std::cout;using std::endl;using std::string;//const引用形参举例//非const引用形参只能与完全同类型的非const对象关联std::size_t find_char(string &s,char c){ string::size_type i = 0; while(i != s.size() && s[i] != c) ++i; if(i == s.size()) return string::npos; else return i;}int main(int argc, char *argv[]){ //字面值常量为const对象,调用出错if(find_char("Hello, world.",'.') != string::npos){cout<<"a sentence."<<endl;}return 0;}
提示错误:
error C2664: “find_char”: 不能将参数 1 从“const char [14]”转换为“std::string &
应该将finc_char函数形参定义为const引用。
2) 函数返回const引用。
返回引用的函数返回左值,这样的函数可以用于任何要求使用左值的地方,这一点确实令人迷惑,需要注意,例如下面的代码可能违背你的原意:
#include <iostream>#include <string>using std::cout;using std::endl;using std::string;//返回引用的函数返回左值需要注意class CStu{ public:CStu(){}CStu(string sname,int iage):name(sname),age(iage){} string & getName(){return name;}//应声明返回const引用private:string name;int age;};int main(int argc, char *argv[]){CStu stu("Tom",22);cout<<"name: "<<stu.getName()<<endl; stu.getName() = "Jack";//修改了私有成员,违背原意cout<<"name: "<<stu.getName()<<endl;return 0;}
使用getName函数返回的引用竟然修改了私有成员,这里违背了封装性设计原则。如果不希望引用返回值被修改,应该将其声明为const引用。这里需要将getName()函数返回值声明为const引用,需要修改其name时通过setName()修改。
3) 重载和const形参
仅当形参是引用或者指针时,形参是否为const才能实现函数的重载。可以基于指针指向const对象还是非const对象实现函数重载,编译器可以判断,如果实参是const对象,则调用带有const*类型形参的函数,否则如果实参不是const对象则调用带有普通指针形参的函数。注意,不能基于指针本身是否为const来实现函数的重载。
4.类的const成员函数
关于const成员函数的要点有以下要点:
Point 1: const成员函数的作用在于约定,这个成员函数不会去修改调用它的对象.const成员函数,不能修改非静态的成员变量,也不能调用任何非const成员函数.如果一个函数声明为const而更改了成员,则编译器会报错.
例如下面的例子(来自:Constant Member Functions):
class Date{public: Date( int mn, int dy, int yr ); int getMonth() const; // 只读成员方法 void setMonth( int mn ); // 写入函数,不能为constprivate: int month;};int Date::getMonth() const{ return month; // 不会修改任何内容}void Date::setMonth( int mn ){ month = mn; // 修改成员变量}int main(){ Date MyDate( 7, 4, 1998 ); const Date BirthDate( 1, 18, 1953 ); MyDate.setMonth( 4 ); // Okay BirthDate.getMonth(); // Okay BirthDate.setMonth( 4 ); // C2662 Error}
Point 2 : 在非静态成员函数中,隐含传递的this指针是一个常量指针,它保存调用这个函数的当前对象的地址.
对于一个类X来说,其非const成员中,隐含的this指针类型为'X* const',而const成员函数中,this类型为:'const X *const'.
所以在const成员函数中,无法通过this指针修改对象的成员变量和调用非const成员函数.
Point 3: const对象,指向const对象的指针或者引用只能用于调用其const成员函数,如果尝试用它们来调用非const成员函数则是错误的.C++ 允许基于成员函数是否为const进行重载.const对象调用const成员函数版本,而非const对象调用非const版本.
例如(来自: Function overloading and const keyword):
#include<iostream>using namespace std;class Test{protected: int x;public: Test (int i):x(i) { } void fun() const { cout << "fun() const called " << endl; } void fun() { cout << "fun() called " << endl; }};int main(){ Test t1 (10); const Test t2 (20); t1.fun(); // call non-const version t2.fun(); // call const version return 0;}
程序输出:
fun() called
fun() const called
5.const成员初始化
初始化const类型的成员必须在构造函数初始化列表中进行初始化。例如如下代码:
//const成员初始化class ConstInit {public:ConstInit(int i,int j){ ival = i; cival = j; rival = ival;}private:int ival; const int cival;int &rival;};int main(int argc, char *argv[]){ConstInit ci;}
将提示错误:
error C2758: “ConstInit::cival”: 必须在构造函数基/成员初始值设定项列表中初始化
prog28.cpp(12) : 参见“ConstInit::cival”的声明
解决办法就是在初始化列表中初始化特殊类成员,如下所示代码:
//const成员初始化#include <iostream>using std::cout;class ConstInit {public:ConstInit(int i=0):ival(i),cival(i),rival(i){}private:int ival; const int cival;int &rival;//只要初始化表达式是一个常量,可以再定义体中进行初始化 static const int period = 30;public:static const unsigned int ARRAY[3];//静态常量数组};const unsigned int ConstInit::ARRAY[3] = {1,3,5};int main(int argc, char *argv[]){ConstInit ci;cout<<ConstInit::ARRAY[1];}
- c++-const限定符
- C++const限定符
- C++const限定符
- C++primer :const限定符
- 2.C++--const限定符
- C/C++中的const限定修饰符
- const限定符声明 - C语言
- C 类型限定符const 和volatile
- 【C++】【基础】const限定符的使用
- C语言中的const限定符
- C++:const限定符基本解析
- c++primer读书笔记:2.4 const 限定符
- c++primer笔记--2.4const限定符
- 关于C/C++的限定符const
- C++----声明、定义、const限定符
- C语言的const类型限定符
- const限定修饰符
- 限定符const
- 2049 不容易系列之(4)——考新郎
- JavaWeb学习笔记 第七记
- 最小生成树之克鲁斯卡尔算法
- 2050 折线分割平面
- .Net下的MSMQ(微软消息队列)的同步异步调用
- c++ const 限定符
- Notepad++ 删除空行
- 6.1-6.30推荐文章汇总
- 字符设备编程基本格式
- baidumap 判断是否安装百度地图客户端
- 几款代码比较工具
- dbms_scheduler
- 2051 Bitset
- Android的Manifest配置文件介绍