2010-05-04 91 views
2

比方说,我有下面的类层次结构:Friendness和派生类

class Base 
{ 
    protected: 

    virtual void foo() = 0; 

    friend class Other; 
}; 

class Derived : public Base 
{ 
    protected: 

    void foo() { /* Some implementation */ }; 
}; 

class Other 
{ 
    public: 

    void bar() 
    { 
     Derived* a = new Derived(); 

     a->foo(); // Compiler error: foo() is protected within this context 
    }; 
}; 

我想我可以改变它太a->Base::foo()但由于foo()是纯虚在Base类,呼叫将导致调用Derived::foo()反正。

但是,编译器似乎拒绝a->foo()。我想这是合乎逻辑的,但我不明白为什么。我错过了什么吗?不能(不应该)它处理这种特殊情况?

谢谢。

回答

6

当你有资格的方法名与类名,如Base::foo()动态调度(运行时绑定)不适用。它会总是致电Base执行foo(),不管foo()是否虚拟。因为在这种情况下它是纯虚拟的,没有实现并且编译器抱怨。

你的第二个问题是,在C++中,友谊不被继承。如果你想Other有特殊的访问Derived,它需要是Derived的朋友具体。

此,在另一方面,工作原理:

Base* a = new Derived(); 

a->foo(); 

因为在这里,您是在Base*其中foo()是公共调用foo(),既然你不与类名排位赛foo(),它使用动态派遣并最终致电Derived版本的Foo

+0

实际上,当我指定'a-> Base :: foo()'时,编译器似乎对它有用。你确定吗 ? – ereOn 2010-05-04 17:39:43

+0

你是否一直走到连接步骤?链接器应该抱怨对'Base :: foo'方法的未定义引用。 – 2010-05-04 17:42:15

+0

我没有去连接步骤,你是对的。感谢您选择/更正确的方法。 – ereOn 2010-05-04 17:44:10

0

试着把这个“朋友类别其他;”在派生类中。

更新:现在想起来,我同意泰勒你应该改变一个基地指针。

Base* a = new Derived(); 
+0

谢谢。我想避免它(如果可以的话)。 – ereOn 2010-05-04 17:38:27

0

但是,编译器似乎拒绝了这一点。

拒绝什么?听起来好像你在说编译器拒绝让Other通过Base指针调用foo()函数。当然不应该如此。

要回答你的基本问题,友谊不会遗传....期间。权限范围在与名称解析相同的阶段进行检查,并且由于foo()在您使用的名称中受到保护,因此无法调用它。

另一方面,多态性是通过指针重定向解决的,并且与名称解析或访问权限无关。

+0

我的不好...我编辑了我的问题,并删除了有关“that”的模糊性,这是为了指代'a-> foo()'。 – ereOn 2010-05-04 17:42:41

1

我想你可以做到这一点

void bar() 
{ 
    Base* a = new Derived(); 

    a->foo(); 
}; 
0

这是不幸的,但友谊是用C固有破++在我看来:

  • 没有继承
  • 给所有的内部不受限制的访问,没有可能限制它

我放弃了使用它“as-是“,我现在主要使用Key模式(缺乏更好的名称)。

/// 
/// Key definition 
/// 
class Friend; 

class FriendKey: boost::noncopyable { friend class Friend; FriendKey() {} }; 

/// 
/// Base/Derived definition 
/// 
class Base 
{ 
public: 

    void mySpecialMethod(const FriendKey&) { this->mySpecialMethodImpl(); } 

private: 
    virtual void mySpecialMethodImpl() = 0; 
}; // class Base 

class Derived: public Base 
{ 
public: 

private: 
    virtual void mySpecialMethodImpl() {} 
}; // class Derived 

/// 
/// Friend definition 
/// 
class Friend 
{ 
public: 
    void mySpecialCall() 
    { 
    Derived d; 
    d.mySpecialMethod(FriendKey()); 
    } 
}; // class Friend 

这个概念很简单:每个类声明的一个关键(即使是在向前头可能),以及那些希望给予他们特殊的访问只会有可能使这个密钥。

这并不完美,因为你当然可以滥用它(通过密钥的传递性)。但是在C++中,你可以滥用所有东西,所以它比Machiavelli更像是一个防止墨菲问题。