友谊从派生类方法到基类成员

friendship from derived class method to base class members

本文关键字:基类 成员 类方法 派生 友谊      更新时间:2023-10-16

我想知道是否有一种方法可以使派生类的方法成为其基类的朋友。比如:

class Derived;
class Base
{
    int i, j;
    friend void Derived::f();
protected:
    Base();
};
class Derived : public Base
{
public:
    void f();
};
我得到的错误是:
error: C2027: use of undefined type 'Derived'
see declaration of 'Derived'
error: C2248: 'Base::i' : cannot access private member declared in class 'Base'
see declaration of 'Base::i'
see declaration of 'Base'
error: C2248: 'Base::j' : cannot access private member declared in class 'Base'
see declaration of 'Base::j'
see declaration of 'Base'
error: C2027: use of undefined type 'Derived'
see declaration of 'Derived'

我一整天都在挣扎。我发现所有关于友谊的东西都只使用分离类,不使用继承

没有直接的方法:Base需要Derived::f的定义,而Derived也需要它的Base类的定义。

但没关系,你不应该这样做,你可以,按优先顺序:

    Base类中提供受保护的访问器
  • 使整个Derived类成为好友(一般不需要)
  • 你可以使用一个中间的助手类,它只转发这个特定方法的调用,并赋予它友谊:

例子:

class Base;
class Derived;
class Helper final
{
    friend class Derived;
    public:
        void f(Base* base);
    private:
        Helper() {}
};
class Base
{
    int i, j;
    friend class Helper;
protected:
    Base() {}
};
class Derived : public Base
{
public:
    void f();
private:
    Helper helper;
};
void Helper::f(Base* base)
{
    base->i = 10; base->j = 5;
    std::cout << "Help !" ;
}
void Derived::f()
{
    helper.f(this);
}

处理这类问题的一种方法是应用"如果它是一个东西,那么它就是一个类"的规则。

@quantdev解决方案就在这几行。

基于注释:

假设我有两个类都派生自基类和具有相同的私有成员。为什么不保存一些代码呢将该成员放入基类中,并提供对的友元访问需要该成员的两个派生类。假设其他的类将根本无法访问该成员。这就是我试图达到

[我知道这不能回答指定的问题,但可能是你需要的。]

我将通过将公共元素分解为一个中间类来解决这个问题:

class Base
{
public:
    Base();
    virtual ~Base() = 0;
};
class Derived : public Base
{
public:
    void f()
    {
        i = 1;
    }
private:
    int i, j;
};
class Foo : public Derived
{};
class Bar : public Derived
{};
class Fred : public Base
{};

我不知道是否有可能做到你想要的(尽管在我看来它应该是)——我很有兴趣看到其他的答案,如果它是——但是有一些其他的方法来实现你想要的。我假设您是在询问一般情况下的这种情况——也就是说,您也对可以有许多不同的派生类的情况感兴趣,并不是所有的派生类都需要,也不应该访问Base的私有字段(否则您应该使这些字段当然是protected)。

首先,最简单的方法是将Derived设置为Base的朋友,尽管这感觉像是一种hack(部分原因是它不允许对Base进行任何关于Derived的封装),并且在这里肯定不是最优的。

在我看来,在封装方面,一个更好的方法是创建一个外部的"自由"(非成员)函数,它是Base的朋友(如果你也需要的话,也可能是Derived)。这可以绕过循环编译器错误,但不幸的是仍然失去了作为"派生操作"的概念语义。