C++中私有虚函数的语意是否合理?

Mephisto_76 2004-08-05 04:01:08
如下所示的代码及其变体:

#include "stdafx.h"
#include <iostream>
using namespace std;

class base
{
public:
virtual ~base(){}

void Out()
{
OutPut();
}

private:
virtual void OutPut() = 0;
};

class derived : public base
{
public:
virtual ~derived(){};

private:
void OutPut()
{
cout << "Hello world,I am a derived object!";
}
};

class derived2 : public derived
{
private:
};

int _tmain(int argc, _TCHAR* argv[])
{
base* p = new derived2;

p->Out();

delete p;

return 0;
}

在vc7.1,cbx,dev c++ 中都通过了,调用的是derived的Output函数。

我的疑问是在这个地方,作为实际类型是derived2的指针,p是否真的有资格通过Out函数调用derived类的私有函数Output?若是可以,岂不是使得private控制形同虚设?

望各位大虾指教。
...全文
2121 33 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
33 条回复
切换为时间正序
请发表友善的回复…
发表回复
Mephisto_76 2004-08-13
  • 打赏
  • 举报
回复
好了,谢谢大家的发言。
key20003 2004-08-10
  • 打赏
  • 举报
回复
我刚才看了下大家的发言,都有道理,发言的人起码对c++都掌握的不错,我也发表点意见吧

我觉得这的确是c++本身矛盾的地方,就像它的多重继承一样,在我看来对private函数设成虚函数有点紊乱,当然例子只是一个demo罢了,说明了问题,但在实际运用中我觉得private设为虚函数很不符合,我的论据是java:

在java中,public,protected,friendly函数自动可以多态的(不用加什么virtual之类的词),但是有种情况不可以除外,当函数设为private的时候,就已经隐藏申明是final的了,final就是不允许它被子类改写,所以拒绝多态。大家不懂java没关系,我就是说java作为比c++更加符合oo的语言,去掉private函数的多态性肯定有它的道理的。
oyd 2004-08-10
  • 打赏
  • 举报
回复
看不出来有什么不对劲的地方,这个代码意图相当明显,一切都在类设计者的掌握之中,不知道失控一说从何而来。
darkstar21cn 2004-08-10
  • 打赏
  • 举报
回复
如果改成:
class base
{
public:
virtual ~base(){}
private:
virtual void OutPut() = 0;
};

class derived : public base
{
public:
virtual ~derived(){};
void Out()
{
OutPut();
}
private:
void OutPut()
{
cout << "Hello world,I am a derived object!" << endl;
}
};

那么:
base* p = new derived2;
p->out ();//这个就会编译报错了
然而:
derived* p = new derived2;
p->Out ();//这样似乎又不符合楼主的希望了
darkstar21cn 2004-08-10
  • 打赏
  • 举报
回复
class derived2 : public derived
{
public:
void out2 ()
{
this->OutPut ();//这样才是错误的,楼主的Out ()是从base
} //继承下来的,当然可以访问Output ()了
private:
};
wingfiring 2004-08-10
  • 打赏
  • 举报
回复
从语义学来说,我认为C++强调的是静态安全,BS也说过,C++从来也不打算防范hack代码。
当然,private virtual 不算hack代码。但是,我想强调的是:通过运行时刻的动态处理,跳过访问控制,不是C++要控制的内容。
还有一个方法可以"安全"突破:类成员返回一个私有的成员函数指针,在外部调用这个成员函数指针,也是不会出错的。
Mephisto_76 2004-08-10
  • 打赏
  • 举报
回复
好像没有人过来嘛,要是没人过来这个礼拜就揭帖了。
IDispatchwang 2004-08-07
  • 打赏
  • 举报
回复
不是C++或者编译器的问题吧。
Out是公有的,它调用自己的从基类继承来的私有函数,这个有什么问题?
这是类和继承的基本概念的体现。
你这里之所以出现了这种看似不合理的输出,还是你写程序的原因啊。
有时template method这种模式要小心,设计不合理时,确实可能造成程序的各种潜在错误。
我觉得这个是楼主的没有贯彻基类的思想:
你的积累要求派生链中所有子类都这样实现,但是你最底层的子类显然违法了这个规则,你没有让这个类打印自己的信息
LoveCreatesBeauty 2004-08-07
  • 打赏
  • 举报
回复
在C++中使用指针甚至可以更改一个被申明为const的值。
LoveCreatesBeauty 2004-08-07
  • 打赏
  • 举报
回复
虚函数在继承层次用于动态绑定。但是如果是private的,则根本无法调用,动态绑定更无从谈起。

作为晚于C++的另一个oo语言C#,具有private访问控制的虚函数会引发编译错误。
Mephisto_76 2004-08-07
  • 打赏
  • 举报
回复
打家都说的很好,但是编译器在这里难道不应该有一些强有力的保证吗?这样的类是我故意写的,目的就是测试编译器以及C++有没有这样的强制。很显然,在三家不同厂商的环境下测试结果都表明,C++没有提供这样的保证,这或许是为了效率。
shenyiwen 2004-08-07
  • 打赏
  • 举报
回复
支持楼主的观点,这象是C++设计的一个漏洞,或者说不严密的地方。

最根处的私有函数不应当允许为虚函数,否则违反了私有的定义

hewittlee 2004-08-06
  • 打赏
  • 举报
回复
up
sharkhuang 2004-08-06
  • 打赏
  • 举报
回复
关键还是存虚函数的处理和虚函数的不同.
waini12 2004-08-06
  • 打赏
  • 举报
回复
太正常不过了 这就是C++的多态性呀
freefalcon 2004-08-06
  • 打赏
  • 举报
回复
哦,没有看到废人的回答
赞成废人的部分观点

这里有几个细节
base* p = new derived2;
p->Out();
这里,p->Out()的调用是“静态绑定”的,因为p的类型是base*,这里访问的也是base中的Out,完全可以在derived2中也定义一个私有的Out()方法,而结果不会受影响。

问题的关键是base里的Out在调用虚函数OutPut()时“越权”访问了derived的私有虚函数,而其访问是通过derived2的虚函数表进行的,如果要像普通函数或成员那样在编译期进行权限控制,那么要求编译器在进行虚函数指针赋值时就进行检查,但这对于一个庞大的虚函数链来说,其代价是相当高的。否则就是在运行期进行检查,当调用到了某个虚函数时,就检查其权限,这样的代价是影响程序执行效率

各位继续
sharkhuang 2004-08-06
  • 打赏
  • 举报
回复
感觉是不太合理!看看书编译器一般怎么处理这样的情况.
freefalcon 2004-08-06
  • 打赏
  • 举报
回复
这其实也说明了访问权限检查发生在编译期
虚函数的调用是通过虚函数指针访问虚函数表得到实际调用函数的地址进行的,虚函数表里并没有权限信息,函数的权限信息通过名字修饰反映在内部函数名上,因此,动态检测权限不是不可能,而是出于效率考虑忽略了检查——虚函数的效率已经“很低”了,楼主可以看看《深度探索C++对象模型》,C++的很多设计都是从高效的角度出发的

至于你这个问题,如果要追究责任的话,那么责任人应该是类的设计者,而不是语言的设计者,或者编译器的开发者
renheihei 2004-08-06
  • 打赏
  • 举报
回复
mark
Wolf0403 2004-08-06
  • 打赏
  • 举报
回复
呵呵,好像可以发言了。
1、我们可以确信,这样的调用是有意义的:它是c++实现《设计模式》之 Template Methods的最便捷的途径了。隐藏虚拟函数而通过非虚拟函数,通过非虚拟函数作为接口来间接调用,可以防止派生类更改公有接口

2、为什么编译器允许这样违反访问控制原则,调用虚拟函数?
首先,我们可以确定,在基类中因为有这样一个 pure virtual function 的存在,我们无法直接实例化一贯基类对象。right? 这样得到有关一个结论:通过正常方式,我们能从一个基类借口访问的对象,只能是一个派生类的对象。也就是说,我们是通过一个派生类对象的非虚拟函数接口,上溯到基类寻找这个j接口的实现,然后通过虚拟函数,回归到自己“体内”的 virtual 函数实现,也就是一个由内而外再回归于内的过程,并没有违反关于访问控制的原则

另外,上面的只是我的个人观点。有一本《c++经典对话》的小品文集里面有一篇关于这个内容的精辟论述,值得研读。
加载更多回复(13)

65,187

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧