什么叫搞定:任意给一个类型声明语句,能够判断其是正确的还是错误的,如果是正确的,其是什么含义。
什么叫复杂类型声明:融合指针,引用,const,数组,函数声明,函数指针等各种元素的类型声明。
- 对象是一块具有类型的内存空间;
- *与&都是可以做为类型修饰符使用,用于修饰类型;
- const是对象修饰符,用于修饰对象,表示该对象的内容不能改变;
- 引用不是对象,引用初始化后与某个对象绑定后不能再解绑;
- 从内到外,从右到左阅读复杂的类型声明。
如果能够理解上述5句话的话,所有复杂的类型声明都不在话下,废话不多说,搞几个例子试一试:
1、常量引用(指向常量的引用)
首先我们知道,引用不是对象,而const是用来修饰对象的,所以不存在使用const去修饰一个引用,而只存在一个引用绑定的对象是const的。
const int &A = B; //指的是A是一个const int类型的引用。
其实这儿这么说是不严谨的,应该这么说,A认为自己引用的是一个const int类型的对象,其实不一定是。具体区别请看下述实例:
int B = 0;
const int &A = B;
const int B = 0;
const int &A = B;
下述两段代码在CPP中都是正确的。注意第一段中,引用A的类型与B的类型不是完全匹配的,为什么允许呢?这其中包含着下述逻辑:
B不是一个const对象,所以其值是可以被改变的,那么我将A定义为一个与const
int类型对象绑定的引用。A就认为自己引用的是一个const的对象,就不会使用A
去修改这个对象的值,另外对于第一种情况,B做为一个int型的对象,而不是const对象,就算修改都是没有关系的。
如果是下述这样:
const int B = 0;
int &A = B;
A的定义就是错误的,因为下述逻辑:
B是一个const修饰的对象,其内容是不能改变的;但是A却认为自己是与一个int类型的对象绑定的,所以A会去修改B的值,这就违反了CPP的原则了,所以不允许。
再看下述定义:
int & const A = B;
这个定义就是错误的,无法解释。按照我们从右向左阅读的原则来看,A是一个对象被const修饰的。但是再看&,发现A应该是一个引用,这就矛盾了。根据前面第4句话引用不是对象,所以上述定义是错误的
2、常量指针
引用不是对象,但是指针是正常对象的。
int B = 0;
int *const A = &B; //A是一个被const修饰的对象,所以A的内容不能够改变。
//而A的类型是int *,所以A是一个指针,即A指向的位置不能够改变
const int B = 0;
int *const A = &B;
/*
这个定义是错误的,与1中的逻辑是一样的。A做为一个指针,其认为自己是指向一个int型的,
而实际其指向的是一个const int型的,所以如果试图使用A去修改B的值就会发生错误。
*/
3、指向常量的指针
const int B = 0;
const int *A = &B;
/*
A是一个指针,其指向的是一个const int类型的对象。
*/
int B = 0;
const int *A = &B;
/*
这个定义也是正确的,与1中逻辑一样:
A做为一个指针,认为自己是指向const int类型的对象的,虽然其指向的是一个int型的对象,但是并不会使用A去修改B的内容,就算修改也没有关系的。所以这种定义是允许的。
*/
4、底层const与顶层const
顶层const:指针本身是一个常量
底层const:指针指向的对象是一个常量
const int B = 0;
const int *const A = &B;
/*
8个字:从内到外,从右到左
先看最右边的const————说明A是一个对象,而且其内容不能改变。
然后看*————说明A是一个指针,故A指向的位置不能改变的。
然后看const int————说明A指向的是一个int对象,而且这个对象被const修饰。
所以综上:A做为一个指针其指向的位置不能改变,同时A==认为==自己指向的是一个const int对象,所以也不能使用A去改变B的值。
*/
注:A认为自己指向的是一个const的对象,并不代表其真指向的是一个const对象,仅仅只能说明A认为自己不能够去修改其指向的对象的。详见1,2,3中的逻辑分析。
引入数组类型[],函数类型更复杂的类型声明呢?
牢记8个字:从内到外,从右到左。
接下来,我就用这8个字给大家分析一些更加复杂的例子:
int *A[];
/*
先看最右边[]————说明A是一个数组
再看*————说明A中的元素都是指针
再看int————说明A中的元素都是int型指针
*/
int *A();
/*
先看最右边()————说明A是一个函数
再看int *————说明A这个函数返回的是int *类型
*/
int (*A) ();
/*
从内到外,先看(*A)————说明A是一个指针
从右到左,再看()————说明这个指针是指向一个函数的
再看int————说明这个函数返回类型是int型的
*/
再来一个更高水平的,读者可以先自己试一试,再来看我的分析
int (*A(int) ) (int*, int);
/*
跟我一起念,从内到外,从右到左
先看(int)————说明A是一个函数
再看*A(int)————说明A这个函数返回的是一个指针
再看(int*, int)————说明返回的这个指针也指向一个函数,这个函数的形参是int* 与 int类型的
再看最左边的int————说明返回的这个函数其返回类型是int。
综上:A是一个形参为int类型,返回值类型为一个函数指针的函数。
而其返回的函数指针指向的函数,形参为int * 与 int 类型,返回类型为int。
*/
最后给出一个类似的练习,读者自行分析:
int (*A(int i)) [10];
一些其他的注意点
1、数组的维度是做为数组类型说明的一部分的。
2、函数的类型,由形参类型以及返回类型共同组成的。
3、&符号在CPP中的四种使用方式:
- 按位与运算符
- 取地址运算符
- 函数声明中的形参中表示引用传递
- 用在复合类型中,做为类型修饰符使用,表引用
4、*符号在CPP中的三种使用方式:
- 乘法符号
- 用在复合类型中,做为类型修饰符使用,表指针
- 用来解析指针,做为解引用符