[toc]
0x0100 const含义
const类型是指使用类型修饰符 const 说明的类型,const类型的变量或对象的值是不能被更新的。
约定:
常量指针: 指向的变量不可被修改的指针; 类似于: const char * pr;
指针常量: 指针在初始化之后不可被修改; 类似于: char * const pr;
记忆方式:
关注const的位置即可>
常量指针、指针常量 <---> const char *、 char * const
0x0200 const作用
- 定义常量
const int a=100;
-
类型检查
- const 编译器可以进行安全检查;#define 只是简单的字符串替换,没有进行类型安全检查;
-
防止修改,起保护作用,增加程序健壮性
void f(const int i){
i++; //error!
}
0x0300 const对象默认为文件局部变量
注意:非const变量默认为extern。要使const变量能够在其他文件中访问,必须在文件中显式地指定它为extern。
// file1.cpp
int ext
extern const int ext1=12;
// file2.cpp
#include<iostream>
extern int ext;
extern const int ext1;
int main(){
std::cout<<(ext+10)<<std::endl;
std::cout<<ext1<<std::endl;
}
小结:
可以发现未被const修饰的变量不需要extern显式声明!而const常量需要显式声明extern,并且需要做初始化!因为常量在定义后就不能被修改,所以定义时必须初始化。
0x0300 const基本使用
const int b = 10;
b = 0; // error: assignment of read-only variable ‘b’
const string s = "helloworld";
const int i,j=0 // error: uninitialized const ‘i’
上述有两个错误:
+ b 为常量,不可更改!
+ i 为常量,必须进行初始化!(因为常量在定义后就不能被修改,所以定义时必须初始化。)
0x0400 指针与const
与指针相关的const有四种:
// 第一类方式
const char * a;
char const * a;
// 第二类方式
char * const a;
const char * const a;
小结:
const 在 * 左侧, 表示该指针指向的值不可以通过该指针进行修改 && 该指针可以指向其它可以指向的值;
const 在 * 右侧, 表示该指针指向的值可以通过该指针修改(也就是说,指向的值不可以被const修饰) && 该指针不可以指向其它值了(初始化之后)
具体使用如下:
- 常量指针
int num = 10;
const int *ptr;
ptr = #
*ptr = 20; //error
const 限定符在* 的左侧表示, ptr 不可修改指向的值;
小结:
允许把非const对象的地址赋给指向const对象的指针
const对象的指针不允许修改指向的内容
- 指针常量
const指针必须进行初始化,且const指针的值不能修改。
例子1:
#include<iostream>
using namespace std;
int main(){
int num=0;
int * const ptr=# //const指针必须初始化!且const指针的值不能修改
int * t = #
*t = 1;
cout<<*ptr<<endl;
}
例子二:
#include<iostream>
using namespace std;
int main(){
const int num=0;
int * const ptr=# //error! const int* -> int*
cout<<*ptr<<endl;
}
小结:
指针常量 必须被初始化, 这也决定了它不可再指向其它的变量了.
指针常量 指向的是一个变量, 不可以被const修饰.
常量指针常量
const int p = 3;
const int * const ptr = &p;
ptr是一个const指针,然后指向了一个int 类型的const对象。
0x0500 函数中使用const
0x0501 const修饰函数返回值
这个跟const修饰普通变量以及指针的含义基本相同:
- const int
const int func1();
这个本身无意义,因为参数返回本身就是赋值给其他的变量!
2. const int*
const int* func2();
指针指向的内容不变。
3. int *const
int *const func2();
指针本身不可变。
0x0502 const修饰函数参数
例子1:
void func(const int var); // 传递过来的参数不可变
void func(int *const var); // 指针本身不可变
例子2:
void StringCopy(char *dst, const char *src);
例子3:
void func(const A &a)
对于非内部数据类型的参数而言,象void func(A a) 这样声明的函数注定效率比较低。因为函数体内将产生A 类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。
为了提高效率,可以将函数声明改为void func(A &a),因为“引用传递”仅借用一下参数的别名而已,不需要产生临时对象。
但是函数void func(A &a) 存在一个缺点:
引用传递”有可能改变参数a,这是我们不期望的。解决这个问题很容易,加const修饰即可,因此函数最终成为:
void func(const A &a)。
以此类推,是否应将void func(int x) 改写为void func(const int &x),以便提高效率?完全没有必要,因为内部数
据类型的参数不存在构造、析构的过程,而复制也非常快,“值传递”和“引用传递”的效率几乎相当。
小结:
对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。例如将void func(A a) 改为void func(const A &a)。
对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void func(int x) 不应该改为void func(const int &x)。
以上解决了两个问题:
- 如果函数需要传入一个指针,是否需要为该指针加上const,把const加在指针不同的位置有什么区别;
- 如果写的函数需要传入的参数是一个复杂类型的实例,传入值参数或者引用参数有什么区别,什么时候需要为传入的引用参数加上const。
0x0600.类中使用const
在一个类中,任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改 数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。
file1.cpp
class Apple {
public:
Apple(int i);
const int MAX_APPLE_NUM;
int cur_apple_num;
void take(int num) const;
void add(int num);
void add(int num) const;
int getCount() const;
};
Apple::Apple(int i) :MAX_APPLE_NUM(i),cur_apple_num(0)
{
}
void Apple::add(int num) {
cur_apple_num += num;
cout << "this is not const add " << endl;
take(num);// 非const函数调用了const函数, 这种情况是被允许的
}
void Apple::add(int num) const {
//cur_apple_num += num; // error 这里不可修改成员的值
cout << "this is const add " << endl;
take(num);
}
void Apple::take(int num) const {
cout << "take func " << num << endl;
}
// const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数.
// const函数 只可以访问类里面的const 函数, 但是可以访问类中的非const成员变量,但是不可修改它
int Apple::getCount() const
{
take(1);
//add(1); // 访问的是 add const函数, 如果不存在add const函数, 则报错
return cur_apple_num;
}
file2.cpp
// 普通对象
Apple a(2);
a.cur_apple_num += 1;
cout << a.MAX_APPLE_NUM << endl;
cout << a.getCount() << endl;
a.add(10);// 默认调用的是是非const函数, 如果不存在非const函数, 则会调用const函数
// const对象
const Apple b(3);
//b.cur_apple_num += 1; // error!
cout << b.MAX_APPLE_NUM << endl;
b.add(100);// 调用的是const函数, 如果不存在const函数, 则调用失败
return 0;
小结:
访问类中的字段:
const对象对类中的非const字段只具有"读”权限,不具有"写"权限
非const对象对类中的非const字段具有"读写"权限 访问类中的函数:
const对象只能访问类中的const修饰的成员函数
非const对象优先访问类中非const函数, 若非const函数不存在, 则访问const函数
最后:
被const修饰的函数, 只对本类中的成员具有"读"权限, 不具有"写"权限