变量声明的阅读: int* (*(*const& value)[3][4])(double, double)
?#
常见的声明类型#
复杂声明的阅读#
我个人是 非常不建议学习这个的, 能写出复杂的声明, 那说明你的代码本身已经过于复杂了. 实际遇到了建议通过 明确复杂声明的语义 解决.
规则#
警告
这是我自创的规则, 可能与实际存在微妙的差异.
括号分层, 由里及外; 调用括号, 同时延展; 其他元素, 先右后左.
- 括号分层
对于将名字包括起来的括号, 先分析括号内, 再分析括号外.
例如
int (*value)
的阅读顺序为value
-(*value)
-int (*value)
.- 由里及外
对于同一层的内容, 以名字为里面, 向外面读.
例如
int* value
的阅读顺序为value
-* value
-int* value
.- 调用括号, 同时延展
对于没将名字包括起来的括号, 它是作为函数的参数存在的. 既然是函数, 则除了参数还存在返回值, 故应该向右分析参数部分并向左分析返回值部分, 即为 "同时延展".
例如
int (*value)(int, double)
, 右边读取到参数(int, double)
, 左边读取到返回值int
.- 其他元素, 先右后左.
对于同一层的其他内容, 先分析所有右边的内容, 再分析所有左边的内容.
例如
int value[3]
的阅读顺序为value
-value[3]
-int value[3]
.
示例#
int value[3]
value[3]
:value
是一个长度为 3 的数组; 其元素是?int value[3]
: 其元素是int
.所以
value
是一个长度为 3 的数组, 数组的元素是int
.
int (*value)[3]
(*value)
:value
是一个指针; 其指向的对象是?(*value)[3]
: 指向一个长度为 3 的数组; 其元素是?int (*value)[3]
: 其元素是int
.所以
value
是一个指针, 指针指向一个长度为 3 的数组, 数组的元素是int
.
int* (*const& value)[3]
(& value)
:value
是一个引用; 其引用的对象是?(const& value)
: 是一个 const 对象; const 作用于?(*const& value)
: 作用于一个指针; 其指向的元素是?(*const& value)[3]
: 指向一个长度为 3 的数组; 其元素是?* (*const& value)[3]
: 其元素是指针; 其指向的对象是?int* (*const& value)[3]
: 指向一个int
对象.所以
value
是一个引用, 引用一个被 const 作用的指针, 指针指向一个长度为 3 的数组, 数组的元素是指针, 指向一个int
对象.
int* (*value[3])(int, double)
(value[3])
:value
是一个长度为 3 的数组; 其元素是?(*value[3])
: 其元素是指针; 其指向的对象是?int* (*value[3])(int, double)
: 其指向一个函数, 参数是(int, double)
, 返回值是int*
.所以
value
是一个长度为 3 的数组, 数组的元素是指针, 指向一个函数, 函数的参数是(int, double)
, 返回值是int*
.
习题#
int* (*(*const& value)[3][4])(double, double)
.
点击查看答案
(& value)
:value
是一个引用; 其引用的对象是?(const& value)
: 是一个 const 对象; const 作用于?(*const& value)
: 作用于一个指针; 其指向的元素是?(*const& value)[3]
: 指向一个长度为 3 的数组; 其元素是?(*const& value)[3][4]
: 其元素是长度为 4 的数组; 其元素是?(*(*const& value)[3][4])
: 其元素是指针; 其指向的对象是?int* (*(*const& value)[3][4])(double, double)
: 其指向一个函数, 函数的参数是(double, double)
, 返回值是int*
.所以
value
是一个引用, 引用一个被 const 作用的指针, 指针指向一个长度为 3 的数组, 数组的元素是一个长度为 4 的数组, 数组的元素是指针, 指向一个函数, 函数的参数是(double, double)
, 返回值是int*
.
明确复杂声明的语义#
如果真的考这种题那只能说没活了, 在实际的程序设计中, 复杂的类型往往有明确的语义乃至 不变式, 应该通过命名来解决.
类型别名#
C++11 前通过 typedef 原来的类型名 别名
, C++11 及以后通过 using 别名 = 原来的类型名
可以创建类型别名, 为类型增加语义.
1using Log_type = int;
2using Log_function = void(Log_type); // 函数参数为 Log_type, 返回值为 void
但要注意类型别名只是一个别名, 实际使用的还是原来的类型.
1void function(int);
2
3int main() {
4 using Log_type = int;
5 Log_type value = 0;
6 function(value); // 通过
7};
用自定义类型包装#
有时候我们需要区别于原来的类型定义一个新的类型名, 但又与原来的类型有同样的功能, 这在目前的 C++ 版本没有很好的解决方案.
折中方法是, 通过自定义类型进行包装. 这方面涉及的内容很多, 此处仅给出一个非常简单的例子.
1void function(int);
2
3struct Widget {
4 public:
5 int value;
6};
7
8int main() {
9 Widget widget;
10 function(widget); // 预期发生编译错误
11}