其中的一些概念:
常量表达式是指:值不会改变并且在编译过程之后就能得到计算结果的表达式。字面值属于常量表达式,且用常量表达式初始化的const对象也是常量表达式。
而常量的概念不同:常量的范围应该是大于常量表达式,无论是运行时初始化还是编译时初始化都是属于常量的一部分。
例如:
const int j = get_size(); //在运行时初始化
const int j = 42; //在编译时初始化
int i = j; //将j的值拷贝给了i
j的常量特征只体现在执行改变j的操作时才会发挥作用,所以在j去初始化i时根本无须在意j的常量特性。
const修饰一般的对象:
- const int k =1
- 在文件之间共享时,const 常量需要在一个文件之中定义,并在多个文件之间声明(这里也体现出定义和声明的不同了)
//在声明并定义的文件之中
extren const int bufsize = 1;
//在其他用到的的文件之中
extren const int bufsize;
和#define不同,一个const定义的常量在程序之中只有一个内存,而编译器在编译的过程之中会将所有用到该变量的地方用bufsize的值来替代,如1(共用了同一个地址).而#define会在所有使用到的地方生成立即数。
const修饰的引用
const int ci = 1024;
const int &r1 = ci;//引用是怎么表现的,指向同一个地址?或许在编译原理里面会有解释
1.当一个常量的引用绑定到另外一种类型上时,发生的结果如下所示:
double dval = 3.14;
const int &ri = dval;
而实际上
const int temp = dval;
const int &r1 = temp; //编译器为了让r1绑定上整数,进行了如上的转换。很显然,r1绑定上了临时变量并没有任何的意义
量temp上,所以对r1的改变不会影响到dval(如果r1不是常量的话),显然这样定义引用就没有什么意义了(把int引用绑到double上本来就没有意义吧)
2.当const 引用引用了一个非const的对象
由于常量引用仅仅对引用可参与的操作进行了限制(封死了通过引用来改变原值的能力),所以一个非const对象通过除了引用之外的别的途径还是能够照常来修改值的
int i = 42;
int &r1 = i;
const int &r2 = i;
r1 = 0; //r2引用影响不到i和r1,所以r1照常
r2 = 0; //r2引用自身被限定不能改变值,可能在传参数的时候用这种形式会好一点
3.const和指针
主要有三种情况,分清楚就可以了
int d = 1; //从左向右读
const int *a = &d; //*a指针指向常量类型的int,指针自身的类型是变量 ,即指向常量的普通指针
int * const a = &d; //*const a指针指向一个普通的int变量,即指向普通变量的常指针
const int *const a = &d; //*const a指针指向一个const类型的int变量,即指向常变量的常指针
c++P上有这么一句话:所谓指向常量的指针或是引用,不过是指针或是引用“自以为是”(一厢情愿也蛮适合的)罢了,它们觉得自己指向了常量(然而未必),所以自觉地不去改变所指对象的值。(严于律己,宽以待人的意思)
顶层const:可以表示任意的对象是常量(当然也包括了指针在内)
底层const:表示指针所指的对象是一个常量(都是从自身的角度出发),与指针和引用等复合类型的基本类型部分有关。
也只有指针可以同时拥有两种属性(虽然引用可以const const,但是引用本身不是对象?const的部分和指针类型不一样,那是什么?)
constexpr
在C++11新标准规定之中,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式,constexpr之中隐含了const。
constexpr int mf = 20;
constexpr int limit = mf+1; //先知道了mf的情况然后再mf的基础上加1?跟编译的顺序有关系吗
constexpr int sz = size(); //成立的前提是size是一个constexpr函数
还有如果在constexpr声明之中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指向的对象无关。
const int *p = nullptr; //p是一个指向整型常量的指针
constexpr int *q = nullptr;//q是一个指向整数的常量指针,constexpr把它所定义的对象置为了顶层const
constexpr也可以指向非常量
int j = 0;
constexpr int *p1 = &j
字面值类型
常量表达式的值需要在编译的时候就得到计算,因此对声明constexpr时用到的类型必须有所限制。因为这些类型一般比较简单,值也显而易见、容易得到。就把他们成为字面值类型(constexpr声明所用的类型)
算数类型,引用和指针都属于字面值类型。而自定义类 Sales_item(我不知道他是什么)、IO库、string类型则不属于字面值类型。
在函数之中使用const
可能有必要了解一下参数返回的事。
返回一个值的方式和初始化一个变量或形参的方法完全一样:返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果。相当于是右值
如果 int i = func();//又相当于把临时量赋给了i
1.void func(const int var)
void func(int *const var)//无意义,形参本身只是临时变量,对原数产生不了影响。
2.void func(const int *var)//对比上面就有了比较实际的意义
3.参数为引用,增加效率,且同时防止被修改
void func(const A &a) //const类型防止修改,引用减少了临时对象的生成。但前提是A为非内部数据,比如比较复杂的类类型,这种类型产生临时变量的构造、析构会消耗比较多的资源,而int这种内部类型就完全没有必要。
4.const int func1();//返回了一个const类型的值
但是它本身没有任何的意义
const int func1()
{
return 1;
}
int x = func1();
x = 2;
cout<<"right"<<endl;//也能够正常执行
因为函数返回值作为一个临时量,它的作用就只在于给别人拷贝或是输出等。拷贝等过程并不会改变它的值。
5.const int *func2(); 返回值为一个指针,指针指向的内容不能改变。
但是返回指针的话,指针指向的内容不能是临时的,不能是在函数体中定义的,所以要有传入的有意义的数据(指针或者引用等)
6.int *const func3();
返回一个不能改变的指针,和1一样没有太大的意义
在类中使用const
不太理解原因这个到时候再看