C++变量声明再探讨
前言
每每学到指针,总会有这些知识点来折磨我们:
“int* a[3]”和“int (*a)[3]”不一样哟!一个是指针数组一个是数组指针;
“const int* a”和“int * const a”不一样哟!一个是可改a不可改*a,一个是可改*a不可改a;
“int* p()”和“int(*p)()”不一样哟!一个是函数一个是指针。
……
如果老师只念ppt,也许这些知识只能硬背。
1 基本声明格式
这一节主要是复习,声明原则在第二节
1.1普通变量
众所周知,C++中声明变量的方式是:
T var;
T是变量类型,var是变量名。比如声明一个int:
int a;
声明一个double:
double b;
这都很简单,相信你也是会的。这里就不赘述了。
请记住这个基本原则。
1.1 C语言部分:数组、函数、指针
如何声明一个数组?
T array[N];
T是类型,N是一个编译期间可确定的常量。可以说,声明一个元素是T类型的数组的语法,就是在变量名后面加个"[N]"。
那么,函数呢?
T func(ArgType1, ArgType2, ...);
比如一个参数类型是int的、返回值是double的函数,声明时只需要这样即可:
double func(int);
参数名在声明时可以不写,这是我们知道的语法。
所以可以总结为,声明一个返回值类型是T的函数,就是在变量名后面加个(ArgType1, ArgType2…)的参数类型列表。
指针呢?指针就是在变量名前面加个*。比如指向int的指针,声明为:
int *p;
1.2 C++部分:引用
这里不介绍什么是引用,单说一下声明。声明一个引用T类型变量的左值引用的语法,类似指针,就是在变量名前面加个&:
int &ref;
当然,左值引用必须在声明时初始化。如果声明右值引用,那就是加&&。
2 变量声明原则
2.1 核心:变量声明原则
经过第一节的复习,聪明的你一定发现了:只要我们在变量名上动一下手脚,就可以声明指针/函数/引用/数组。这里总结一下这个原则:
以变量名为中心,先右后左地结合,按下面的方式翻译变量类型:
- 左边加*是指针,a pointer to
- 左边加&是引用(加&&是右值引用),the reference of
- 右边加(…)是函数,a function with argument (…) that returns
- 右边加[N]是数组,an N-sized array of
2.2 核心:声明原则的举例理解
这个原则怎么应用呢?看一下下面的例子:
int *a[3];
int (*b)[3];
int (*c)(int, char);
int (*d(double))(int)
a,先右后左,所以先和[3]结合,我们写下:
a is a 3-sized array of
然后把[3]去掉,剩int *a。
对int *a,我们先右后左,那就是与 * 结合,我们接着上面的那句话往下添加pointer to:
a is a 3-sized array of pointer to
然后去掉*,剩下int a。
对int a,先右后左,与int结合,直接落下来:
a is a 3-sized array of pointer to int
翻译成汉语:a是一个长度为3、装有指向int的指针的数组。即,int *a[3]是数组。
那么就按照这个模式结合,我们可以得到abcd的类型分别是:
- a is a 3-sized array of pointer to int.
a是装了3个int*的数组 - b is a pointer to a 3-sized array of int.
b是指向int[3]的指针 - c is a pointer to a function with argument (int, char) that returns int。
c是指向一个函数的指针,函数类型:int(int, char)【返回值int,参数类型为int, char】 - d is a function with argument (double) that returns a pointer to a function with argument (int) that returns int.
d是参数类型为double,返回一个函数指针的函数,这个函数指针指向一个int(int)【返回值int,参数类型(int)】的函数。
你学会了吗?
2.3 初始化语句
初始化的值永远都要写在声明的后面
比如初始化一个函数指针和一个数组引用:
int array[10];
int(*p)(int,int) = nullptr; //pointer to function int(int,int)
int (&r)[10] = array; //reference of a 10-sized array of int
=nullptr、=array这些初始化值,要写在声明语句结束之后,而不是写在p、r的后面。
2.4 扩展:成员函数指针
普通函数指针和成员函数指针对比一下,如下:
class T{};
int (*pFunc)(int) = nullptr;
int (T::*pMethod)(int) = nullptr;
是的,成员函数指针要加一个T::,而且括号要括在(T::*p)这个外面,而不是T :: (*p)。
普通函数指针和成员函数指针的用法对比一下:
(*pFunc)(10); //ok
pFunc(10); //ok
T obj;
(obj.*pMethod)(10); //ok
//obj.pMethod(10); //不ok, 会报错
是的,普通函数指针有不用加*的语法糖,成员函数没有,必须得老老实实的(obj.*pMethod)(10) !
3 typedef与using
经过第二节的介绍,你会发现:类型但凡复杂一点,声明语句就可读性极差。比如一个返回数组指针的函数,应该这么写:
int (*func(int *p))[10]{
return (int(*)[10])p;
}
这**是人能读的东西啊?
C语言为此出了个typedef,到了C++里又可以用using
3.1 typedef
用法:typedef 变量声明语句
作用:声明语句声明出来的那个名字不再是变量名,而是这个类型的别名
例子:
typedef int IntArray[3];
IntArray3 arr = {1,2,3};
正常来讲,int IntArray[3]声明出来的IntArray应该是一个长度为3的int数组,但是加了typedef之后,IntArray就不再是一个类型是int[3]的变量了,而是int[3]的别名。你可以直接把它当类型用。
这样就可以把函数指针简写了:
typedef void (*p_event)(int);
p_event makeEvent(){
return nullptr;
}
//如果不用p_event的话,makeEvent的定义应该这么写:
void (*makeEvent_another())(int){
return nullptr;
}
3.2 using的类型起名用法
typedef是用变量声明语句来声明类型别名,using则是后面直接接一个类型。也就是下面的p_func和上面的p_event一样,都是void(*)(int):
using p_func = void(*)(int);
结语
祝大家学习cpp顺利!