在C++中,如何在不使用友元函数的情况下通过对象调用私有函数?

5
我发现了这段用C ++编写的代码:
#include<iostream>
using namespace std;

class Base {
public:
    virtual int fun(int i) { cout << "Base::fun(int i) called"; }
};

class Derived: public Base {
private:
    int fun(int x)   { cout << "Derived::fun(int x) called"; }
};

int main()
{
    Base *ptr = new Derived;
    ptr->fun(10);
    return 0;
}

输出:

 Derived::fun(int x) called 

在以下情况下:
#include<iostream>
using namespace std;

class Base {
public:
    virtual int fun(int i) { }
};

class Derived: public Base {
private:
    int fun(int x)   {  }
};
int main()
{
    Derived d;
    d.fun(1);
    return 0;
} 

输出

编译器错误。

有人能解释一下为什么会发生这种情况吗?在第一个案例中,通过对象调用了一个私有函数。


5
自学C++,就不需要靠朋友帮你编写代码了。(开玩笑) - user529758
@H2CO3 OP 的意思是 friend 关键字 :) - PiotrNycz
d.fun(1) 不是虚函数调用,但 ptr->fun(10) 是。 - Zyx 2000
1
@sTEAK。通过指针调用将是虚拟的,直接在对象上调用则不会。在调用点时,d的确切类型始终是已知的。 - Zyx 2000
1
@sTEAK:所有这些都与使用->.运算符无关,它只与您用于调用方法的类型有关:如果您通过Base调用该方法,则编译为公共方法Base::fun。如果您通过Derived调用该方法,则会失败,因为Derived::fun是私有的。现在,fun是虚拟的,这意味着调用它默认情况下是动态绑定...除非编译器可以保证对象的确切类型在编译时已知。在这种情况下,调用将被优化,以静态方式解析正确的方法。 - paercebal
显示剩余10条评论
2个回答

4

因为在第一个案例中,您使用了来自Base的声明,这是公共的,并且调用正在被延迟绑定到Derived的实现。请注意,更改继承中的访问说明符是危险的,几乎总是可以避免。


3

多态发生在第一个案例中,这会导致动态或迟绑定。正如第二个答案中提到的那样,有时候它可能变得非常危险。

您无法直接从类定义外部访问类的私有接口。如果您想在不使用友元函数的情况下访问第二个实例中的私有函数(如您的问题标题所示),请在类中创建另一个公共函数。使用该函数来调用此私有函数。像这样。

 int call_fun (int i) ;

在函数内部调用fun()

int call_fun (int i)
{
  return fun (i) ;  //something of this sort, not too good
}

被用来调用另一个函数的函数被称为包装函数

并不总是建议进行友元操作。这与信息隐藏原则相违背。


虽然我同意“交朋友也不总是明智的”这句话,但我绝对认为“这违反了信息隐藏的原则”是错误的。友谊并不会破坏封装,而是增加了它。与友谊相反的是将数据暴露给更多系统(这确实破坏了封装)。友谊以牺牲紧密耦合的代价来增加封装,并使其成为接口的一部分。http://programmers.stackexchange.com/a/99595/12917 - Martin York
我同意这个观点。但是这通常取决于方法、情况和手头的问题。就像在这种情况下,友谊可以很容易地避免。 - Coding Mash

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接