两个单独文件中另一个类的类方法朋友

问题描述 投票:2回答:2

我的目标很简单 - 我想从另一个类的方法访问一个类的受保护成员。为此,我有以下 -

A.HPP

#ifndef A_HPP
#define A_HPP

#include "B.hpp"
using namespace std;

class A
{
    protected:
        int a;
    public:
        A();
        ~A(){};
        friend void B::geta(A& ao);
};

inline A::A()
{
    a = 2;
    cout << a;
}

#endif

Bihp

#ifndef B_HPP
#define B_HPP

using namespace std;

class A;

class B
{
    protected:
        int b;
    public:
        B();
        ~B(){};
        void geta(A& ao);
};

inline B::B()
{
    b = 1;
    cout << b;
}

inline void B::geta(A& ao)
{
    b = ao.a;
    cout << b;
}

#endif

main.cpp中

#include <iostream>

#include "A.hpp"
#include "B.hpp"

int main()
{
    A ao;
    B bo;
    bo.geta(ao);
    return 0;
}

当我编译这个时,我得到以下错误。

error

我该如何解决?我在这里看到的大多数答案只是将所有类放在一个文件中并在适当的位置定义函数来实现这一点,但我需要它们在单独的文件中。

c++ header-files friend protected forward-declaration
2个回答
1
投票

Option 1: Not inline

您当然可以将B::geta的定义移动到包含A.hpp的B.cpp文件中,并删除inline关键字。但这可能会降低编译器优化的可能性。

Option 2: Weird #include logic

只有当编译器看到A的前向声明,B的定义,A的定义以及B::geta的定义时,才能编译你的定义。因此,如果你想在一个文件中定义A,在另一个文件中定义B,你需要让预处理器在文件之间来回切换。

A.hpp

// NO A_HPP include guard!
#ifdef INCLUDED_FROM_B_HPP

class A
{
    protected:
        int a;
    public:
        A();
        ~A(){};
        friend void B::geta(A& ao);
};

inline A::A()
{
    a = 2;
    cout << a;
}

#else
#  include "B.hpp"
#endif

B.hpp

#ifndef B_HPP
#define B_HPP

class A;

class B
{
    protected:
        int b;
    public:
        B();
        ~B(){};
        void geta(A& ao);
};

#define INCLUDED_FROM_B_HPP
#include "A.hpp"
#undef INCLUDED_FROM_B_HPP

inline B::B()
{
    b = 1;
    cout << b;
}

inline void B::geta(A& ao)
{
    b = ao.a;
    cout << b;
}

#endif

所以现在如果文件执行#include "B.hpp",预处理器将:

  1. 从B.hpp的第一部分输出class A;B的定义。
  2. 定义INCLUDED_FROM_B_HPP
  3. 输出A.hpp中的定义。
  4. 清除INCLUDED_FROM_B_HPP的定义。
  5. 输出来自B.hpp第二部分的B内联成员的定义。

如果一个文件首先执行#include "A.hpp",事情有点棘手:

  1. 由于没有设置INCLUDED_FROM_B_HPP,只需立即进入B.hpp。
  2. 从B.hpp的第一部分输出class A;B的定义。
  3. 定义INCLUDED_FROM_B_HPP
  4. 当遇到B.hpp中间的#include "A.hpp"时,预处理器将以递归方式返回到A.hpp。但是这次定义了INCLUDED_FROM_B_HPP,它输出了A.hpp的代码内容。
  5. 清除INCLUDED_FROM_B_HPP的定义。
  6. 输出来自B.hpp第二部分的B内联成员的定义。

Option 3: friend class B;

而不是仅仅向朋友指定一个成员函数B::geta,只需与类本身建立联系,声明为friend class B;。现在A.hpp不需要包含B.hpp,因此没有循环依赖问题。

从访问是和不可能的角度来看,这并没有太大减少封装,因为通常任何可以修改class B的任何部分的程序员也可以修改B::geta。但它确实在A的其他成员中使用B的非公开成员“意外地”开辟了可能性。

Option 4: Refactor access method

A.hpp

#ifndef A_HPP
#define A_HPP

class B;

class A
{
    protected:
        int a;
    public:
        A();
        ~A(){};

        class AccessForB {
        private:
            static int geta(A& aobj) { return aobj.a; }
            friend class ::B;
        };
};

inline A::A()
{
    a = 2;
    cout << a;
}

#endif

B.hpp

...

inline void B::geta(A& ao)
{
    b = A::AccessForB::geta(ao);
    cout << b;
}

此代码引入了一种新的封装:现在class B只能从成员a获取值,无法修改该值,并且无法访问A的任何其他非公共成员。可以根据需要为其他成员添加其他访问者。要允许修改成员,该类可以提供“set”访问器,或者提供返回引用的访问器。对于非公共函数,该类可以提供仅传递给实际函数的包装函数。

除了B之外,B::geta的成员有可能利用友谊,但现在输入A::AccessForB::并不能真正被视为意外。


1
投票

...但我需要它们在单独的文件中。

将内联函数移动到单独的B.cpp文件,#includeA.hpp

由于刚刚被视为转发类声明,A的声明尚未完成,您将开始在B.hpp中使用它,并且编译器会抱怨这一点。

您仍然可以保留inline关键字,编译器会像往常一样将其视为提示。

这是草图:

B.hpp

#ifndef B_HPP
#define B_HPP

using namespace std;

class A;

class B
{
    protected:
        int b;
    public:
        B();
        ~B(){};
        void geta(A& ao);
};
#endif

B.cpp

#include "B.hpp"
#include "A.hpp"

inline B::B()
{
    b = 1;
    cout << b;
}

inline void B::geta(A& ao)
{
    b = ao.a; // <<<<<<<<<<<< here's the problem to be solved
    cout << b;
}

这些问题和答案与您的问题非常相关:

© www.soinside.com 2019 - 2024. All rights reserved.