C++杂记001:const详解

阅读量: 鲁文奎 2021-04-22 19:14:19
Categories: Tags:

[toc]

0x0100 const含义

const类型是指使用类型修饰符 const 说明的类型,const类型的变量或对象的值是不能被更新的。

约定:

常量指针: 指向的变量不可被修改的指针; 类似于: const char * pr;
指针常量: 指针在初始化之后不可被修改; 类似于: char * const pr;
记忆方式:
关注const的位置即可>
常量指针、指针常量 <---> const char *、 char * const

0x0200 const作用

const int a=100;
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修饰) && 该指针不可以指向其它值了(初始化之后)

具体使用如下:

  1. 常量指针
int num = 10;
const int *ptr;
ptr = &num;
*ptr = 20; //error

const 限定符在* 的左侧表示, ptr 不可修改指向的值;

小结:

允许把非const对象的地址赋给指向const对象的指针
const对象的指针不允许修改指向的内容

  1. 指针常量
    const指针必须进行初始化,且const指针的值不能修改。
    例子1:
#include<iostream>
using namespace std;
int main(){
    int num=0;
    int * const ptr=&num; //const指针必须初始化!且const指针的值不能修改
    int * t = &num;
    *t = 1;
    cout<<*ptr<<endl;
}

例子二:

#include<iostream>
using namespace std;
int main(){
    const int num=0;
    int * const ptr=&num; //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修饰普通变量以及指针的含义基本相同:

  1. 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)。

以上解决了两个问题:

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修饰的函数, 只对本类中的成员具有"读"权限, 不具有"写"权限