用const_cast来去除const限定
对于const变量,我们不能修改它的值,这是这个限定符最直接的表现。但是我们就是想违背它的限定希望修改其内容怎么办呢?
下边的代码显然是达不到目的的:
const int constant = 10;
int modifier = constant;
因为对modifier的修改并不会影响到constant,这暗示了一点:const_cast转换符也不该用在对象数据上,因为这样的转换得到的两个变量/对象并没有相关性。
只有用指针或者引用,让变量指向同一个地址才是解决方案,可惜下边的代码在C++中也是编译不过的:
const int constant = 21;
int* modifier = &constant
// Error: invalid conversion from 'const int*' to 'int*'
(上边的代码在C中是可以编译的,最多会得到一个warning,所在在C中上一步就可以开始对constant里面的数据胡作非为了)
把constant交给非const的引用也是不行的。
const int constant = 21;
int& modifier = constant;
// Error: invalid initialization of reference of type 'int&' from expression of type 'const int'
于是const_cast就出来消灭const,以求引起程序世界的混乱。
下边的代码就顺利编译功过了:
const int constant = 21;
const int* const_p = &constant;
int* modifier = const_cast<int*>(const_p);
*modifier = 7;
传统转换方式实现const_cast运算符
准转换运算符是可以用传统转换方式实现的。const_cast实现原因就在于C++对于指针的转换是任意的,它不会检查类型,任何指针之间都可以进行互相转换,因此const_cast就可以直接使用显示转换(int*)来代替:
const int constant = 21;
const int* const_p = &constant;
int* modifier = (int*)(const_p);
或者我们还可以把他们合成一个语句,跳过中间变量,用
const int constant = 21;
int* modifier = (int*)(&constant);
替代
const int constant = 21;
int* modifier = const_cast<int*>(&constant);
然而这样就可以改变const了么
int main()
{
const int s = 7;
const int * n = &s;
int * t = const_cast<int *> (n);
cout << s << endl;
cout << *t << endl;
*t = 21;
cout << "*****************" << endl;
cout << s << endl;
cout << *t << endl;
cout << "*****************" << endl;
cout << &s << endl;
cout << t << endl;
}
输出:
7
7
*****************
7
21
*****************
0041FE10
0041FE10
相同的地址但结果却不同,相信这样的结果已经改变了你的认知,它是一个未定义的行为。
结论 : 绝对不对const数据进行重新赋值
a)对于const修饰的局部常量而言,g++编译器(不同编译器机制未必相同)会为其分配内存,由于是静态分配而且是局部常量,所以分配在栈中。
b)对代码中直接引用到该常量(即该常量直接作为右值使用)的情况,编译器会将其直接替换成定义时赋的值。这样一来,无论运行期该常量所在内存被如何修改,都不会影响常量的使用。对于取址操作&con_var而言,&con_var整体被当作一个右值,没有被编译器替换成常值,这也是为什么既然编译器还要为const修饰的常量分配内存的原因。(const folding技术在这里被用到)。
c)const只在编译期间保证常量被使用时的不变性,无法保证运行期间的行为。程序员直接修改常量会得到一个编译错误,但是使用间接指针修改内存,只要符合语法则不会得到任何错误和警告。因为编译器无法得知你是有意还是无意的修改,但是既然定义成const,那么程序员就不应当修改它,不然直接使用变量定义好了。
d)c语言中原本没有const关键字,后来为了兼容,引入了const,但是编译器对其的操作与c++中不同。
为什么还要去const呢?
参考下面两个情形:
//使用const_cast去除const限定,以便函数能够接受这个实际参数。
#include <iostream>
using namespace std;
void Printer (int* val,string seperator = "\n")
{
cout << val<< seperator;
}
int main(void)
{
const int consatant = 20;
//Printer(consatant);
//Error: invalid conversion from 'int' to 'int*'
Printer(const_cast<int *>(&consatant));
return 0;
}
//我们定义了一个非const的变量,但用带const限定的指针去指向它,
//在某一处我们突然又想修改了,可是我们手上只有指针,
//这时候我们可以去const来修改了。
#include <iostream>
using namespace std;
int main(void)
{
int variable = 21;
const int* const_p = &variable;
int* modifier = const_cast<int*>(const_p);
*modifier = 7;
cout << "variable:" << variable <<endl;
return 0;
}
/**variable:7**/
static
testStatic() {
static int count = 10;
//这里的赋值实际上从未被执行过,这个count的初始化在程序装载的时候被执行
//它的值被放在了全局变量区
//这个过程发生在main函数被执行之前(和全局变量的初始化类似,在所有程序执行之前已经完成了初始化)
return count--;
}
int main()
{
int count = 1;
for (; count <= 10; count++) {
cout << "Static Count : " << count << " ";
cout << "Main function Conut : " << testStatic() << endl;
}
system("pause");
return 0;
}
输出:
Static Count : 1 Main function Conut : 10
Static Count : 2 Main function Conut : 9
Static Count : 3 Main function Conut : 8
Static Count : 4 Main function Conut : 7
Static Count : 5 Main function Conut : 6
Static Count : 6 Main function Conut : 5
Static Count : 7 Main function Conut : 4
Static Count : 8 Main function Conut : 3
Static Count : 9 Main function Conut : 2
Static Count : 10 Main function Conut : 1
static特性
- static 变量在程序装载的时候就被初始化,它存在于程序内存空间的静态储存区中而不是堆栈中,这样在下一次被调用的时候它还是保持原来的值
- 和全局变量不一样,它只在自己的作用范围内可见,但是在可用范围之外它并不会消失(与堆栈和堆中的数据不一样)
- 它和全局变量最大的不同之处,在于它具有隐藏的功能(全局变量在所有被编译的.c文件中都可见)
- 静态全局变量、静态局部变量、全局变量都放在内存的静态存储区,局部变量则存在于堆栈区
在程序被装载进内存时,所有定义为 static 的变量已经完成了初始化,并被放在了程序段的全局变量区,所以我们无论定义了多少次这个static的对象,内存中实际存在的变量还是只有这一个。
无论这个static变量在我们看来作用于哪一个范围内,比如一个函数中,函数结束后这个变量依然存在,并不会因为函数结束而被回收。