来自C++的"Const式"傲娇

来自C++的"Const式"傲娇

好久之前就想要做一个有关Const的总结了.作为C++里实用但复杂的限定符,Const可谓是让人又爱又恨.废话不多说了,提前做个说明:这是一个C++语言的Const语法简明介绍.讲求快速参考,快速应用.如果想深入详细地参考应用实例与讲解的请自行参考Primer第五版.

Const是对变量类型的限定,是一个限定符

1、const对象必须初始化,且一旦初始化就没有办法改变.

2、对const对象进行初始化的可以是任意复杂的表达式.

3、默认const对象仅仅在其单个文件内有效,因为编译器会想define一般直接替换.如果希望在多个文件内有效可以extern修饰.

//无论是定义还是声明都要写成统一的形式
//file_1
extern const int maxn=12;//但是初始化只能在其中一个位置
//file_2
extern const int maxn;

引用与Const

1、允许为一个常量引用绑定非常量的对象字面值,甚至是一般表达式.

2、引用本身不是对象,const与引用打配合其实是说:该引用不能改变被引用对象.

3、引用的对象是常量还是非常量可以决定其所能参与的操作,却无论如何都不会影响到引用和对象的绑定关系本身.

顶层Const与底层Const

1、用于声明引用的const都是底层const. 引用并不是对象,所以就无所谓什么引用本身是否能够被修改.Const修饰引用一定是限制引用使其不能修改被引用对象的值.至于被引用对象是否能够通过其他途径改变自身的值,就不得而知了.

int a=2;
const int &p=a;//不能通过p修改a的值
//int &s=42; 不能用字面值初始化一个非常量引用

2、限制指针的Const

之所以有顶层与底层的概念是因为:指针本身是不是常量指针所指向的对象是不是常量是两个独立的问题.

指针的顶层const:表示指针本身是个常量.

指针的底层const:表示指针所指的对象是一个常量.

int i=0;
const int k=0;//不是指针,不能改变k,这是顶层const
const int* temp1=&i;//不能改变i,这是底层const =>常量(的)指针
int* const temp2=&i;//不能改变指针本身,这是顶层const =>指针(是)常量

3、对象拷贝过程中的const

  • 顶层const不受影响。
  • 拷入、拷出对象必须具备相同的底层const资格,或者两个对象的数据类型必须能够转换。一般地,非常量可以转换成常量,反之不行(通俗的理解就是类型转换可以朝着加const限制的方向转换,但是不能反过来)。
int &s=42; //错误:不能用字面值初始化一个非常量引用
int p;
const int *const p3=&p;//p3具有底层const资格(int前有const)
/*上面两句中:非常量初始化了底层const对象,若反过来则不行*/
int* t=p3;//错误:p3具有底层const,而t没有.
int i=0;
const int ci=i;
const int &pi=i//正确:const int&变量可以绑定到普通int上
int &r=ci;//错误:普通的int&不能绑定到const int变量上

类成员函数的尾后Const

1、编译器会自动为成员函数传参this指针.尾后Const限制了这个this指针,使得函数只能访问而不能修改类的成员变量.

2、无论一个类的实例是否被Const限制,它都可以调用这种带有尾后Const的类成员函数.但是Const实例是不能够调用尾后无Const的成员函数的.

class Time
{public:
    int s;
    Time():s(100){};
    const Time& getTime() const 
    {//尾后Const使得this指针const化,只能做访问的操作.
        return *this;//返回的对象本身是const类型
        //由于返回的是引用,所以必须是const类型的引用
    };
    void show()const {cout<<s<<endl;};
};

int main()
{
	Time c;
	c.getTime().show();
	return 0;
}//输出:100

class Time
{public:
    int s;
    Time():s(100){};
    Time getTime()const 
    {
    	return *this;
        //此时返回的并不是引用,所以返回类型不需要const修饰.
    };
    void show()const {cout<<s<<endl;};
};

int main()
{
	Time c;//这里也可以是const Time c;
	c.getTime().show();
	return 0;
}//输出:100

3、函数尾后Const的差别在重载中是允许的

//同一个类中允许一下两个函数同时出现
void fun() const{...};
void fun() {...};
//无Const限制的对象在调用fun()时会优先调用无尾后const限制的函数
//有Const限制的对象只允许调用带有尾后const限制的fun()函数

Const形参与实参

1、当用实参初始化形参时会忽略掉形参的顶层const.

void fcn(const int i) {/*fcn内可以读取i但不能向i写值*/}
//调用fcn函数时,既可以传参const int也可以传参int
//void fcn(int i){...} 这种重载是不允许的,因为传参时忽略了顶层const

2、形参的初始化方式同变量的初始化方式相同。

  • 我们可以使用非常量初始化一个底层const对象,但反过来不同。
  • 一个普通的引用必须用同类型的对象初始化
void reset(int*);
void reset(int&);
int i=0;
const int ci = i;
string::size_type ctr = 0;
//
reset(&i);//正确:传入i的地址
reset(&ci);//错误:const int*不可初始化int*
//
reset(i);//传入以引用作参数的函数
reset(ci);//错误:普通引用不能绑定对应的const对象
reset(42);//错误:普通引用不能绑定到字面值上
reset(str);//错误:类型不匹配

3、函数形参尽量定义成const类型的引用

  • const引用能够比普通引用接收更多类型的实参传参
  • 保证函数体内的计算不会修改被引用对象的内容,保证稳定性
原文地址:https://www.cnblogs.com/savennist/p/12821287.html