引子:
关于const指针、const引用、以及const形参和const实参的内容,《C++primer》书中有介绍,并且有举例还有习题,但都是以简单类型举例,const对象的指针、const对象的引用、const对象的形参,const对象的实参的例子基本没有。虽然说可以举一反三,但对大部分人来说还是很难理解。下面从一个考题中抽出一段代码,通过分析促进大家理解相关内容。
迷雾重重:
分析下面代码,查找代码错误原因。
struct CDog
{
int m_age;
CDog(int ia):m_age(ia){}
void ShowAge(){
cout << m_age << "," << endl;
}
};
void UseObj(const CDog* pData)
{
pData->m_age = 20; //位置1、编译错误
pData->ShowAge(); //位置2、编译错误
}
int main()
{
CDog* oData = new CDog(9);
UseObj(oData);
cout << oData->m_age;
delete oData;
return 0;
}
上面代码有2处错误:
1)pData->m_age = 20;的错误
这处错误的原因好理解,常对象指针(指向的常量对象的指针)指向值是不能修改的。
2) pData->ShowAge(); 这里编译也出错?什么原因呢?
抽丝剥茧:
难道是void UseObj(const CDog* pData)函数中const的原因,尝试修改main函数如下:
int main()
{
// CDog* oData = new CDog(9); 修改为:
const CDog* oData = new CDog(9);
...
}
编译仍然报错,为什么呢?还是看看VS2017下的错误提示吧:
error C2662: “void CDog::ShowAge(void)”: 不能将“this”指针从“const CDog”转换为“CDog &” |
先复习一下this指针的概念(百度百科):this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,即使你没有写上this指针,编译器在编译的时候也是加上this指针的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。
仔细理解上面这句话,结合我们的例子,可以把我们的成员函数改写一下,这样就好理解了。
成员函数表面上的形式 | 成员函数实际的形式 |
void CDog::ShowAge() | void ShowAge(CDog *this) |
这样就可以理解上面代码的编译错误提示了。
凶手浮现:
上面代码编译错误原因:成员函数ShowAge中定义的this指针类型是CDog,同时由于函数
void UseObj(const CDog* pData)定义的形参带const的原因,将实参从CDog指针类型转为了const CDog指针类型,那么它对应的对象的this指针类型也变成了const CDog类型。这样对于成员函数ShowAge(CDog *this)而言,需要将const CDog转为CDog,转换失败,导致错误。
解决办法:
1、将void UseObj(const CDog* pData)改为void UseObj(CDog* pData),这并不是这个考题真正要考察的内容。
2、将CDog类中 void ShowAge()的定义改为void ShowAge() const{ ... },这才是出题者真正想要的答案。如下面代码:
// void ShowAge() { 修改为
void ShowAge() const {
cout << m_age << "," << endl;
}
这里的const也是C++面试经常问道的,我们都知道常成员函数内部不能修改此类的成员变量的值,至于为什么,很多人不清楚,但当看到如下形式的常成员函数时:void ShowAge(const CDog *this),我们就可以理解为什么了,这才是常成员函数中const真正的位置。
风云再起:
同样是上面的代码,我们将裸指针改为智能指针。
struct CDog
{
int m_age;
CDog(int ia):m_age(ia){}
void ShowAge() {
cout << m_age << "," << endl;
}
};
void UseObj(const shared_ptr<CDog> pData)
{
pData->ShowAge(); //编译不报错,为什么???
}
int main()
{
shared_ptr<CDog> oData(new CDog(9));
UseObj(oData);
cout << oData->m_age;
return 0;
}
我们编译、运行此代码。发现这个例子中pData->ShowAge()这句并不报错,程序运行正常,为什么?