C++函数传引用问题一则

今天碰到一个C++问题,体现了基础知识不够好。在这里总结一下问题的来龙去脉。

首先我参照SAFE_DELETE(这个MACRO应该用过微软C++的都熟悉)写了一个SAFE_DESTROY_DELETE。用于传入CWnd*后先DestroyWindow再delete指针。

[c light=”false” toolbar=”false”]
void SAFE_DESTROY_DELETE(CWnd* pWnd)
{
if (pWnd)
{
if (::IsWindow(pWnd))
{
pWnd->DestroyWindow();
}
SAFE_DELETE(pWnd);
}
}
[/c]

​在写这个函数的时候我只是想到不要用Macro,这样可以有一个编译器的类型检查。这里面其实有一个比较低级的错误。开始没发现,你能看出来吗?

然后发现这个错误后我试图把函数修改一下,为了不动到其他会调用此函数的地方,我灵机一动,改了一处,觉得应该就可以了。修改后如下:

[c light=”false” toolbar=”false”]
void SAFE_DESTROY_DELETE(CWnd* &pWnd)
{
if (pWnd)
{
if (::IsWindow(pWnd))
{
pWnd->DestroyWindow();
}
SAFE_DELETE(pWnd);
}
}
[/c]

编译后发觉一堆错误。VC2010的错误信息如下:

error C2664: ‘SAFE_DESTROY_DELETE’ : cannot convert parameter 1 from ‘ B* ‘ to ‘CWnd *&’

这里B是一个CWnd的派生类。同样的代码在VC6中的错误信息如下:

error C2664: ‘SAFE_DESTROY_DELETE’ : cannot convert parameter 1 from ‘class B *’ to ‘class CWnd *& ‘        A reference that is not to ‘const’ cannot be bound to a non-lvalue

错误编号一样,VC6的文字描述更加详细。这里我开始怎么都不明白,我并没有把右值(非左值)传递给函数啊。而如果调用的时候传入CWnd*类型的变量是肯定没有问题的。也就是说结论是不能传入CWnd*以外的任何类型,包括CWnd的子类的指针也不行。

这下傻眼了。感觉这个写法完全和我所学的C++引用部分的知识没有矛盾啊。

再到gcc上一试,也有错误,错误信息如下:

error: invalid initialization of reference of type ‘CWnd*&’ from expression of type ‘B*’

彻底死心了。看来不是编译器的问题。所有C++的编译器都证明了这样不可行。那么到底是什么原因呢?去认真翻看C++书中关于引用部分的解释吧。

看完之后,彻底明白了。关于引用,明确规定了不能将右值传递给引用(VC6的错误文字提示很准确)。

写一些很明显错误的代码你就会明白:

[c light=”false” toolbar=”false”]
int &a = 1; // error
A &b = A(2); // 假设A类有一个传递一个整型的构造函数,也是error。
[/c]

以上这两句,一个是直接传常量,一个是传递了一个临时对象。这些都是右值,违反了C++语法。那么在我上面的实例中,并没有传递右值啊。我传递的确是是一个变量。其实错误就在这里,看似是一个变量,其实按照C++隐式类型转换,已经变成了一个临时对象。展开写如下:

[c light=”false” toolbar=”false”]
B* pB;
SAFE_DESTROY_DELETE( (CWnd*)pB );
[/c]

这里传入的是(CWnd*)pB;这就是一个临时对象,类型为CWnd*。而这也解释了为什么直接传一个CWnd*类型的变量不会有问题。因为相同类型编译器当然不会做什么隐式转换啦。

到这里为止基本问题解决了。可能还有同学会问那C++的多态在2种情况下可以发挥作用:引用或者指针,也就是传入子类的引用或者指针然后调用其中的虚函数都会发挥多态的功能。这上面的代码如果说明了子类引用不能传入给父类,这多态岂不是无法使用?其实这里和书中讲的子类父类是不一样了。B确实是CWnd的子类,但是并不说明 B*是CWnd*的子类。所有指针都只是一个32位或者64位的值(或者以后字长更长)。赋值或者函数值传递时都不会有问题。但是引用明确规定了不能将右值传入,也就是这里的关键是首先考虑传给引用的是否不是左值。如果确实不是左值,那么接下来多态什么的都不用考虑了。

By Lu Jun

80后男,就职于软件行业。习于F*** GFW。人生48%时间陪同电子设备和互联网,美剧迷,高清视频狂热者,游戏菜鸟,长期谷粉,临时果粉,略知摄影。

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.