目的: 值代替。
预处理宏定义的缺点: 只做文本替换,并没有类型检查,这样会产生风险。内存存储形式,常量折叠
常量的定义会保存在符号表中,可折叠的常量像宏一样,在预编译阶段对常量的引用一律被替换为常量所对应的值。但在运行时常量也是会被分配内存空间的。下面的程序可以说明。
int main()
{
const int i=0;
int *j = (int *) &i;
*j=1;
printf(" addr_i=%d\n addr_j=%d\n val_i=%d\n val_j=%d\n",&i,j,i,*j);
return 0;
}
运行时指针j是可以取得常量i的地址,但无法修改地址中的值,原因是预编译时常量i已经通过常量折叠进行了替换,
在运行时用到i的地方已经替换为0,但i在内存中还是被分配了空间的。
使用形式:
1) 普通常量
const int ArrSize=10;int arr[ArrSize];
2) 常量集合
常量也可以用于集合,但集合无法被放入符号表中,必须分配内存。此时常量的含义是 "不能改变的一块存储"。// 正确
const int SizeArr[] = {1,2,3,4};
// 下面的使用方法是错误的
float farr[SizeArr[2]]; //错误
原因: 常量集合SizeArr无法被保存到符号表中,所以预编译时无法对SizeArr[2]进行替换。
从编译时提示的错误信息也可以看的很清楚
error C2057: expected constant expression
error C2466: cannot allocate an array of constant size 0
error C2133: 'farr' : unknown size
3) 指针
指向const指针 const int* x; int const* x;一个指针,指向一个const 变量, 不需要初始化,可以指向不同的常量。 地址可变::值不可变
const int i=10;
const int j = 100;
const int* x;
x = &i;
cout << *x << endl;
x = &j;
cout << *x << endl;
*x = 5; // 错误 error C3892: 'x' : you cannot assign to a variable that is const
const 指针 int* const x;
指针本身为const指针,指针指向的地址不能改变,必须初始化。 地址不可变::值可变
int i = 5;
int* const x = &i;
*x = 15;
cout << *x << endl; // output : 15
i = 25;
cout << *x << endl; // output : 25
4) 函数参数和返回值
const 参数: void fun1(const int i)void fun1(const int i)
{
i++; // 错误 error C3892: 'i' : you cannot assign to a variable that is const
}
当以穿值方式向函数中传递参数时,会发生创建副本的情况,const也作为副本的一部分在函数中生效。
但把这个过程呈现出来更好些,效果是一样的。
void fun1(int i)
{
const int ic = i;
}
但函数接口如果是传引用或指针,如果不希望被修改最好还是用const。
const 返回值:
const 如果用于简单类型的返回值,并没有太大意义。 例如:
const int fun2()
{
return 0;
}
用于用户自定义类型的比较有意义的。 例如:
class X
{
int _i = 0;
public:
X(int i):_i(i){};
void modify(){_i++;}
};
X func3(){return X();}
const X func4(){return X();}
void func5(X &x){x.modify();}
void main()
{
func3() = X(1);
func3().modify();
func5(func3());
func4() = X(1); // 错误
func4().modify(); // 错误
func5(func4()); // 错误
}
这里对于返回对象为const的是无法进行修改的,在实际应用中是有意义的。
如果传递的参数为指针或地址,如果不希望参数在函数执行过程中被改变,尽可能的用const进行修饰。
类中的const类型
错误的使用
class A
{
private:
const int _size=100; // 非 static 的 const型无法在类中被初始化
int _arr[_size]; // 所以此处也无法确定数组的大小
public:
A(){};
};
正确的使用
class A
{
private:
const int _size;
int *_arr;
public:
A() : _size(100){_arr = new int[_size]};
};
class A
{
private:
enum {_size=100};
int _arr[_size];
public:
A(){};
};
const 对象和成员函数
有个对象,如果被定义为const,在应用的过程中,它的行为应该是怎样的?
怎样保证 const 对象在调用它自身的成员函数时保证对象不会被改变?
例如一个类 B
class B
{
private:
int _i;
public:
B(int i): _i(i) {};
void add();
int const_add() const; // const 成员函数
void show() const; // const 成员函数
};
void B::add() { _i++; }
void B::show() const
{ cout << _i << endl; }
int B::const_add() const
{
//_i++; // 错误: 在const 成员函数中尝试改变对象的成员变量
return _i;
}
int main()
{
B b(2);
const B cb(4);
b.add();
b.const_add();
b.show();
cb.add(); // 错误: cb 为 const 类型,不能通过cb的成员函数修改cb。
cb.const_add();
cb.show();
return 0;
}
如何修改const 对象中的成员变量,使用关键字 mutable
class Y
{
int _i;
mutable int _j;
public:
Y(){};
void addi() const;
void addj() const;
};
void Y::addi() {_i++;} // 错误: 通过const 成员函数修改成员变量
void Y::addj() {_j++;} // 正确: _j 为mutable类型,可以跳过 const 限制
int main()
{
const Y cy;
cy.addi();
cy.addj();
return 0;
}