浅析带const的指针或引用复杂声明
2006-03-23 21:11
169 查看
C++中的const与non-const、指针和引用本来就搞得人头晕了,再把复杂的声明结合在一起,比如看下面这个
const double * const & crcdval = &cdval;
你看的明白吗?呵呵,如果不是很明白,那就读一读我的这篇心得吧。
[b]●复杂的ptr和const声明[/b]
#include <iostream>
#include <vector>
using namespace std;
struct X
{
X(int i) : m_i(i) { }
int m_i;
};
void main()
{
// 我把每个变量的类型都写出来了
// correct
const X cx(10);
const X * pcx = &cx;
//typeof(pcx) = const X (*)
const X * const cpcx = &cx;
//typeof(cpcx) = const X (*const)
cout << sizeof(const X *const(*const(*const))) << endl;
const X * const * ppcx = &pcx;
//typeof(ppcx) = const X (*const(*))
const X * const * const cppcx = &cpcx;
//typeof(cppcx) = const X (*const(*const))
const X * const * const * const cpcpcpx = &cppcx;
//typeof(cppcx) = const X (*const(*const(*const)))
cout << &(vector<int>()) << endl;
cout << sizeof(int(& (*)(int[20],int&,double**))[100]) << endl;
}
今天才发现原来以前C++老师讲const时讲错了,const int * p 和 int const * p是等价的,p为pci: Pointer to a const int(const int *);而int * const p中p为cpi:const pointer to int(int *const),我也一直都记错了。
另外,比如
const X * const * const cppcx = &cpcx;
//typeof(cppcx) = const X (*const(*const))
里面的第2个const可能会让很多人迷惑。我的理解是:它既修饰前面的指向const X的指针本身,看成是"const T *const"的后面那个const;又修饰了后面的那个指针*,说明它指向的对象是const的,看成是"T const *"的形式,其中的T展开就是const X*.
利用编译原理中的词法生成规则来描述它,我们可以清楚地看到这个声明语句是怎么得来的。
首先定义生成规则:
产生式:{
指针的声明语句→T id [= exp];
T→(const T)|(T const) * [const]
}
上面的那个复杂的声明语句的推导过程:
指针的声明语句 => T id = exp;
=> T const * const id = exp;
=> const X * const * const id = exp;
=> const X * const * const cppcx = &cpcx;
这里的两个理解其实是等价的,因为这是一个多重指针,第2个指针指向的就是第一个指针里面的地址值。
所以第一个指针本身是const和指向这第一个指针的第二个指针所指向的对象为const当然是一样的了。
以前我老是觉得,C++中为什么不规定只使用单一形式的const,而要这样搞的两个形式等价,现在看来其实应该是const T *用的人很多,而T const * 又是语法所必须的,所以就无从取舍。当然可以只保留T const *的形式:)。&的写法与此类似,只是只有const T&和T const&,而由于referrence本身的要求,就是一个const的了,所以没有T & const的写法.
[b]●为什么这个const不能少[/b]
今天偶尔看到《c++ Primer》中讲referrence的那一章中有个例子,
const double cdval = 3.14;
const double * pcd = &cdval;
const double * const & crcdval = &cdval;
double dval = 6.28;
double * const & rd = &dval;
书上说到其中的第3行,很可能大部分人都不能把它直接写对,因为里面的两个const一个都不能少!
需要前面的那个const是显然的,如果去掉cl会提示
error: a value of type "const double *" cannot be used to initialize an entity of type "double *const &"
即从const type到non-const type的转化是不允许的。
而如果去掉第二个const会怎么样呢?(可以只通过最后2行的例子来说明这个问题)
error: initial value of reference to non-const must be an lvalue
我觉得,第2个const可以理解做同时修饰(*const)和(const&),这就点和复杂的ptr和const声明一样,稍后会解释。这里因为&dval是non-lvalue表达式,而没有const修饰&,则它必须用一个lvalue表达式来初始化,故错误;而根据修饰转换的原则,non-const type 到const type的转化是允许的.就是"const double *"可以转化为"const double *const &" 所以只有两个const都加上才能通过编译。
const double * const & crcdval = &cdval;
你看的明白吗?呵呵,如果不是很明白,那就读一读我的这篇心得吧。
[b]●复杂的ptr和const声明[/b]
#include <iostream>
#include <vector>
using namespace std;
struct X
{
X(int i) : m_i(i) { }
int m_i;
};
void main()
{
// 我把每个变量的类型都写出来了
// correct
const X cx(10);
const X * pcx = &cx;
//typeof(pcx) = const X (*)
const X * const cpcx = &cx;
//typeof(cpcx) = const X (*const)
cout << sizeof(const X *const(*const(*const))) << endl;
const X * const * ppcx = &pcx;
//typeof(ppcx) = const X (*const(*))
const X * const * const cppcx = &cpcx;
//typeof(cppcx) = const X (*const(*const))
const X * const * const * const cpcpcpx = &cppcx;
//typeof(cppcx) = const X (*const(*const(*const)))
cout << &(vector<int>()) << endl;
cout << sizeof(int(& (*)(int[20],int&,double**))[100]) << endl;
}
今天才发现原来以前C++老师讲const时讲错了,const int * p 和 int const * p是等价的,p为pci: Pointer to a const int(const int *);而int * const p中p为cpi:const pointer to int(int *const),我也一直都记错了。
另外,比如
const X * const * const cppcx = &cpcx;
//typeof(cppcx) = const X (*const(*const))
里面的第2个const可能会让很多人迷惑。我的理解是:它既修饰前面的指向const X的指针本身,看成是"const T *const"的后面那个const;又修饰了后面的那个指针*,说明它指向的对象是const的,看成是"T const *"的形式,其中的T展开就是const X*.
利用编译原理中的词法生成规则来描述它,我们可以清楚地看到这个声明语句是怎么得来的。
首先定义生成规则:
产生式:{
指针的声明语句→T id [= exp];
T→(const T)|(T const) * [const]
}
上面的那个复杂的声明语句的推导过程:
指针的声明语句 => T id = exp;
=> T const * const id = exp;
=> const X * const * const id = exp;
=> const X * const * const cppcx = &cpcx;
这里的两个理解其实是等价的,因为这是一个多重指针,第2个指针指向的就是第一个指针里面的地址值。
所以第一个指针本身是const和指向这第一个指针的第二个指针所指向的对象为const当然是一样的了。
以前我老是觉得,C++中为什么不规定只使用单一形式的const,而要这样搞的两个形式等价,现在看来其实应该是const T *用的人很多,而T const * 又是语法所必须的,所以就无从取舍。当然可以只保留T const *的形式:)。&的写法与此类似,只是只有const T&和T const&,而由于referrence本身的要求,就是一个const的了,所以没有T & const的写法.
[b]●为什么这个const不能少[/b]
今天偶尔看到《c++ Primer》中讲referrence的那一章中有个例子,
const double cdval = 3.14;
const double * pcd = &cdval;
const double * const & crcdval = &cdval;
double dval = 6.28;
double * const & rd = &dval;
书上说到其中的第3行,很可能大部分人都不能把它直接写对,因为里面的两个const一个都不能少!
需要前面的那个const是显然的,如果去掉cl会提示
error: a value of type "const double *" cannot be used to initialize an entity of type "double *const &"
即从const type到non-const type的转化是不允许的。
而如果去掉第二个const会怎么样呢?(可以只通过最后2行的例子来说明这个问题)
error: initial value of reference to non-const must be an lvalue
我觉得,第2个const可以理解做同时修饰(*const)和(const&),这就点和复杂的ptr和const声明一样,稍后会解释。这里因为&dval是non-lvalue表达式,而没有const修饰&,则它必须用一个lvalue表达式来初始化,故错误;而根据修饰转换的原则,non-const type 到const type的转化是允许的.就是"const double *"可以转化为"const double *const &" 所以只有两个const都加上才能通过编译。
相关文章推荐
- 理解复杂的C/C++声明 const, typedef , 函数指针
- 指针的指针&指针的引用&复杂数组声明
- 理解复杂的C/C++声明 const, typedef , 函数指针
- 理解复杂的C/C++声明 const, typedef , 函数指针
- c++引用和const 用法 数组 指针
- const int *a, int const *a,int * const a,区别,指针数组,数组指针,声明与定义
- const int *a, int const *a,int * const a,区别,指针数组,数组指针,声明与定义
- 返回类型是函数指针的函数指针 类比复杂的数组声明
- C++学习第一弹: const 指针 引用答疑
- 理解c和c++的复杂类型声明 (掺杂指针)
- const和引用/指针的结合!
- 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序
- 为什么static成员的类型可以是类本身?又为什么非static成员被限定声明为其自身类对象的指针或引用?
- 复杂的指针声明的两种解读方法
- 不可或缺 Windows Native (18) - C++: this 指针, 对象数组, 对象和指针, const 对象, const 指针和指向 const 对象的指针, const 对象的引用
- const的指针和引用
- 关于各类复杂的数组声明,指针数组,数组指针等的理解
- 复杂指针声明解析
- C++-const_cast只能用于指针和引用,对象的const到非const可以用static_cast
- 二、从C到C++(二) 引用、引用常见用途、指针和引用区别、const引用