第八章 常量 Thinking in C++ V1
值替代:
在C中可以使用宏定义,例如:#define pi 3.1415
在C++中使用const替代 常量的宏定义,这样可以增加程序的安全系数。
C++中cosnt默认为内部连接 ,而在C中cosnt默认为外部连接。
书中说编译器无法获得内存中的数据,这只是对某些编译器成立,在VS2013中下面的代码1无法通过编译:
//代码1
在VS2013中无法通过编译, 提示:error C2133: 'array2' : unknown size |
//代码2 在VS2013中无法通过编译, 提示:error C2133: 'C' : unknown size |
//代码3 const int B = 9; char C[B]; VS2013可以通过编译 说明编译器对B做了优化(常量折叠)。 |
说明VS2013的编译器无法在编译时获得array1[1]中的数据。但在g++编译器中代码1可以通过编译并且输出为8 。
C中和C++中对const变量的定义有本质的差别:
C : 一个不可以被改变的变量,这个变量无法直接更改,但可以使用间接的方法,C中常量一定会被分配内存空间(不在只读存储空间)。在 C 中const默认为外部连接。在C中可以声明:const int bufsize ;不初始化,但C++不行。
C++: 一个常量,编译期间的常量。(const在C++中还有其他的意义,见下面)声明和定义一般同时完成(除了类中的常量),因为定义后无法更改。 在C++中const默认为内部连接。故需要加上关键字extern才能使其被外部文件连接。
C++中使用类似C中使用指针的方法改变const变量的行为没有定义(未定义行为),也就是更改结果与编译器实现有关。例如:
同样的代码不一样的执行结果:
C++编译输出: M = 9 M = 6 *pInt = 6 |
//代码2: C++
#include<iostream>
using namespace std;
#define PR(x) (cout<<#x##" = " <<x<<endl);
int main()
{
const int M = 9;
//不一定开辟空间
PR(M)
//强迫编译器为常量开辟空间
int *pInt =(int *) &M;
*pInt = 6;
PR(M)
PR(*pInt)
system("pause");
}
C++编译输出: M = 9 M = 9 *pInt = 6 |
//代码3: C C编译输出: M = 9 M = 6 *pInt = 6 |
C++中的常量是编译期间的一个定值,故编译器没必要为这个常量分配内存。而且常量的值会保存在符号表中,编译器会将局部const变量进行常量折叠。故如果强迫编译器为常量分配内存,即使在C++中改变了常量在内存中的值,我们在代码中使用的常量的值依旧不会改变,故会出现上面代码2的情况。但是如果在常量的前面添加关键字volatile,告诉编译器不要对const常量进行优化即不做常量折叠,每次使用const常量时都从内存中读取,那么就会出现代码1中的结果。C中一定会为常量分配内存,而且每次使用时都会从内存中读取,故会出现上面的代码3的结果。从上面的结果可知,const常量没有像字符串常量那样被分配在静态存储区。
参考资料:http://blog.csdn.net/heyabo/article/details/8745942
下面的 C 代码可以通过编译但会出现运行时错误:
#include<stdio.h>
#include<stdlib.h>
int main()
{
char *pch = "Hello Qt !";//字符串会被写入只读存储区,静态存储区,这个只读是由硬件控制的
const int i = 2; //奇怪的是,C中const常量不会被写入只读存储器,因为可以使用指针改变其中的值
int *ip = &i;
*ip = 3 ; //运行时没错
pch[2] = 'e'; //运行时会出错,不能对只读存储器进行写操作。
}
const常量没有像字符串常量那样被分配在只读存储区。
const int *pInt;
int const *pInt; //这两个声明的意思是相同的,pInt指向一个int型常量(这个变量中的值不能变)。
int * const pInt; //pInt是一个常量型指针(指针内容不可变),其指向一个int型变量。
字符串是默认的常量并且保存在只读存储区,字符串中的数据是无法更改的(一般更改字符串的代码可以通过编译,但在执行时会触发硬件中断)。字符数组一般是变量,字符数组一般在栈中或静态存储区保存,因为是变量所有其中的内容是可以更改的。char *pChar = "Hellow !"这样的代码在技术上而言是错误的,因为赋值号的右侧是字符串而左侧却是一个指针。但编译器允许这样,编译器默认将字符串的首地址赋予指针。
重定义的错误是在什么时候发现的?预处理还是链接?还是在预处理和链接时都会发现。
在pass-by-value形式的函数前添加关键字const是没意义的,因为完全没有必要,原来的数据一定不会被更改。
如果一个函数返回一个const型的对象,那么这个函数不能作为左值,因为其返回的内存空间是不能被更改的,更不可能被赋值。当一个函数返回的是一个对象而且这个对象不是const型时,这个函数可以作为左值,例如:f(x1) = f(x2),在C中是看不见这样的赋值形式的,在C中左值永远都是变量,而不可能是函数的形式。如果一个函数返回的是内建类型,那么编译器不允许其作为左值。允许返回值为对象的函数作为左值可以写出链式的表达式例如:f(x).fun(y) ,其中fun( y )为f(x)返回值的一个成员函数。可以将上面的代码作为右值,这样可以简化代码。
类中的常量:
类中的常量有两种形式:
1、对象中的常量:对于每一个对象而言某一个变量是常量,由同一个类创建对象的常量值不同,一般这样的常量在类中声明的形式为const int data; 必须在构造函数初始化列表中以类似FUN(...) : data(9)...{ function }的形式初始化这些数据(必须以函数的形式初始化,不能用等号的形式)。
2、类常量:对于由同一个类创建的对象而言,每一个对象中这个常量都是相同的,这样的常量的声明形式如:static cosnt data = T;这样的常量在声明的同时必须初始化,也可以使用无标记枚举来实现这样的效果。
这类常量只是在编译期间存在,因为编译器没必要为这样变量分配内存。
const对象和成员函数:
const对象只能调用对应类中的const成员函数。其中cosnt成员函数的定义,必须声明和定义的时候在参数表之后函数体之前添加const关键字。如果一个成员函数被定义为const类型那么在函数体内将无法更改对象中任何数据成员(除非这个数据被被关键词mutable修饰),否则将无法通过编译。
//const成员函数的声明和定义
class test
{
public:
test(int a);
void Nchange(int b ) const; //const成员函数的声明
private:
const int m_a;
int m_c;
};
void test::Nchange(int b )const //const成员函数的定义
{
//m_c = b;//无法通过编译
int c = b;//c 不是类中的成员数据,故可以通过编译
}


浙公网安备 33010602011771号