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

时间:2018-09-14 03:58:41

标签: c++ header-files friend protected forward-declaration

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

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

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);
};

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

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

2 个答案:

答案 0 :(得分:1)

  

...但是我需要在单独的文件中

将内联函数移到B.cpp的{​​{1}}所在的单独的#include文件中。

就像被视为转发类声明A.hpp的声明那样,在您开始在A中使用它的时候还不完整,编译器理所当然地对此表示抱怨。

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

这是草图:

B.hpp

inline

B.cpp

#ifndef B_HPP
#define B_HPP

using namespace std;

class A;

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

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

答案 1 :(得分:1)

选项1:非内联

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

选项2:奇怪的#include逻辑

仅当编译器看到A的前向声明,B的定义,A的定义和{{1的定义时, }},按此顺序进行。因此,如果要在一个文件中定义B::geta,在另一个文件中定义A,则需要使预处理器在文件之间来回切换。

A.hpp

B

B.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

现在,如果文件执行#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 ,则预处理器将:

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

如果文件首先执行B,则操作会有些棘手:

  1. 由于未设置#include "A.hpp",因此请立即进入B.hpp。
  2. 从B.hpp的第一部分输出INCLUDED_FROM_B_HPPclass A;的定义。
  3. 定义B
  4. 遇到B.hpp中间的INCLUDED_FROM_B_HPP时,预处理器将递归返回A.hpp。但是这一次因为定义了#include "A.hpp",所以它输出A.hpp的代码内容。
  5. 清除INCLUDED_FROM_B_HPP的定义。
  6. 从B.hpp的第二部分输出INCLUDED_FROM_B_HPP的内联成员的定义。

选项3:B

与其仅向朋友指定一个成员函数friend class B;,不如将类本身与声明B::geta成为朋友。现在,A.hpp不需要包含B.hpp,因此不存在循环依赖问题。

从什么访问是不可能的角度来看,这并不会减少封装,因为通常可以修改friend class B;的任何部分的程序员也可以修改class B。但这确实为在B::geta的其他成员中使用A的非公共成员打开了“偶然”的可能性。

选项4:重构访问方法

A.hpp

B

B.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

此代码引入了一种新的封装:现在inline void B::geta(A& ao) { b = A::AccessForB::geta(ao); cout << b; } 仅可以从成员class B获取值,不能修改该值,并且不能访问该对象的任何其他非公共成员。 a。可以根据需要为其他成员添加其他访问器。为了允许修改成员,该类可以提供“设置”访问器,也可以提供返回引用的访问器。对于非公共函数,该类可以提供仅传递给实际函数的包装器函数。

A以外的B成员有可能利用友谊,但现在输入B::geta并不能算是偶然。