- const 修饰普通变量
- const修饰指针变量
- const修饰普通函数的值传递参数、引用&指针传递参数、返回值
- const修饰类成员函数
一、const的作用
1.定义const常量
const int Max=100;
2.便于检查类型
const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误。
void f(const int i)
{
.........
}
//对传入的参数进行类型检查,不匹配进行提示
3.保护被从const修饰的变量或对象
防止意外的修改,增强程序的健壮性。
void f(const int i)
{
i=10;//error!
}
//如果在函数体内修改了i,编译器就会报错
4.可以很方便地进行参数的调整和修改
同宏定义一样,可以做到不变则已,一变都变。
5.为函数重载提供了一个参考
class A
{
public:
void f(int i){......} //一个函数
void f(int i) const {......} //上一个函数的重载
};
6.可以节省空间,避免不必要的内存分配
const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝
#define Pi 3.14159 //常量宏
double a=pi; //编译期间进行宏替换,分配内存
double b=Pi; //再进行宏替换,又一次分配内存!
......
const doulbe Pi=3.14159; //此时并未将Pi放入ROM中
double a=Pi; //此时为Pi分配内存,以后不再分配!
double b=Pi; //没有内存分配
7.提高了效率
编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
二、const修饰普通类型的变量
const修饰普通类型的变量被赋值后,其值不允许被修改。
const int a = 7;
a = 8; // 错误,a 被编译器认为是一个常量,其值不允许修改。
三、const修饰指针变量
1.如果const位于 * 的左侧,const 修饰指针指向的内容,则内容为不可变量,简称左定值;
int a = 7;
int b = 8;
const int* c = &a;
*c = b; //错误:指针指向的内容不可修改
c = &b;//正确:指针可以修改
2.如果const位于*的右侧,const 修饰指针,则指针为不可变量,简称右定向;
int a = 7;
int b = 8;
int* const c = &a;
*c = b; //正确:指针指向的内容可修改
c = &b;//错误:指针为不可变量
3.const 修饰指针和指针指向的内容,则指针和指针指向的内容都为不可变量。
int a = 7;
int b = 8;
const int* const c = &a;
*c = b;//错误:指针指向的内容不可修改
c = &b;//错误:指针为不可变量
“左定值,右定向,const修饰不变量”。
四、const参数传递和函数返回值。
1、const传递参数
- 值传递的 const 修饰传递,传递过来的参数在函数内不可以改变。
void func(const int p)
{
cout << "p = " << p << endl;
++p;//错误:p值不能修改
}
int main()
{
int a = 10;
int b = 20;
func(a);
func(b);
return 0;
}
- 当 const 参数为指针时,可以防止指针被意外篡改。
#include<iostream>
using namespace std;
int b = 30;
void func(int* const p)
{
cout << "*p = " << *p << endl;
++*p;//正确,p指向的值可以修改
p = &b;//错误:p不能被修改
}
int main()
{
int a = 10;
func(&a);
cout << "a = " << a << endl;
return 0;
}
- 自定义类型的参数传递,需要临时对象复制参数,对于临时对象的构造,需要调用构造函数,比较浪费时间,因此我们采取 const 外加引用传递的方法。并且对于一般的 int、double 等内置类型,我们不采用引用的传递方式。同时,传递的对象不能被修改。
/*定义一个学生类,获取学号*/
class Student
{
public:
Student(){}//默认构造函数
Student(int num):number(num){}//初始化列表
int get_number() const //const修饰成员函数,不可修改被调用对象的值
{
return number;
}
int set_number(int num);
private:
int number;
};
//普通函数
void get_student_number(const Student& stu)//避免了临时对象构造
{
cout<<stu.get_number()<<endl;
stu.set_number(3333);//错误!对象stu不能被修改
}
int Student::set_number(int num)
{
this->number = num;
return this->number;
}
int main(void)
{
Student RF(1001);
get_student_number(RF);
int rf_num = RF.set_number(2222);
cout << "rf_num = " << rf_num << endl;
return 0;
}
2、const 修饰函数的返回值【用的少,有机会用到再补充】
const 修饰自定义类型的作为返回值,此时返回的值不能作为左值使用,既不能被赋值,也不能被修改。
const 修饰返回的指针或者引用,是否返回一个指向 const 的指针,取决于我们想让用户干什么。
五、const修饰类成员函数。
- 面向对象程序设计中,为了体现封装性,通常不允许直接修改类对象的数据成员。
- 若要修改类对象,应调用公有成员函数来完成。为了保证const对象的常量性,编译器须区分不安全与安全的成员函数(即区分试图修改类对象与不修改类对象的函数)。例如:
const Screen blankScreen;
blankScreen.display(); // 对象的读操作
blankScreen.set(‘*’); // 错误:const类对象不允许修改
- 在C++中,只有被声明为const的成员函数才能被一个const类对象调用。
- 要声明一个const类型的类成员函数,只需要在成员函数参数列表后加上关键字const,例如
class Screen {
public:
char get() const;
};
- 在类体之外定义const成员函数时,还必须加上const关键字,例如
char Screen::get() const {
return _screen[_cursor];
}
- 若将成员成员函数声明为const,则该函数不允许修改类的数据成员。例如
class Screen {
public:
int ok() const {return _cursor; }//读可以
int error(intival) const { _cursor = ival; }//修改不行!
};
- 值得注意的是,把一个成员函数声明为const可以保证这个成员函数不修改数据成员,但是,如果数据成员是指针,则const成员函数则可以修改指针指向的对象,编译器不会把这种修改检测为错误。例如
class Name {
public:
void setName(const string &s) const;
private:
char *m_sName;
};
void setName(const string &s) const {
m_sName = s.c_str(); // 错误!不能修改m_sName;
for (int i = 0; i < s.size(); ++i)
m_sName[i] = s[i]; // 不好的风格,但不是错误的
//虽然 m_Name 不能被修改,但 m_sName 是 char * 类型,const 成员函数可以修改其所指向的字符。
}
- const成员函数可以被具有相同参数列表的非const成员函数重载,例如,
class Screen {
public:
char get(int x,int y);
char get(int x,int y) const;
};
//在这种情况下,类对象的常量性决定调用哪个函数。
const Screen cs;
Screen cc2;
char ch = cs.get(0, 0); // 调用const成员函数
ch = cs2.get(0, 0); // 调用非const成员函数
小结:
- const成员函数可以访问非const对象的非const数据成员、const数据成员,也可以访问const对象内的所有数据成员;
- 非const成员函数可以访问非const对象的非const数据成员、const数据成员,但不可以访问const对象的任意数据成员;
- 作为一种良好的编程风格,在声明一个成员函数时,若该成员函数并不对数据成员进行修改操作,应尽可能将该成员函数声明为const 成员函数。