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

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

当我对此进行编译时,出现以下错误。

c&#43;&#43; - 在两个单独的文件中另一个类的类方法 friend-LMLPHP

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

最佳答案

选项1:非内联

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

选项2:奇怪的#include逻辑

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

丙型肝炎

// 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

因此,现在,如果文件执行#include "B.hpp",则预处理器将:
  • 从B.hpp的第一部分输出class A;B的定义。
  • 定义INCLUDED_FROM_B_HPP
  • 输出A.hpp中的定义。
  • 清除INCLUDED_FROM_B_HPP的定义。
  • 从B.hpp的第二部分输出B的内联成员的定义。

  • 如果文件首先执行#include "A.hpp",则事情会有些棘手:
  • 由于未设置INCLUDED_FROM_B_HPP,因此请立即进入B.hpp。
  • 从B.hpp的第一部分输出class A;B的定义。
  • 定义INCLUDED_FROM_B_HPP
  • 遇到B.hpp中间的#include "A.hpp"时,预处理器将递归返回A.hpp。但是这一次因为定义了INCLUDED_FROM_B_HPP,所以它输出A.hpp的代码内容。
  • 清除INCLUDED_FROM_B_HPP的定义。
  • 从B.hpp的第二部分输出B的内联成员的定义。

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

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

    选项4:重构访问方法

    丙型肝炎
    #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的任何其他非公共(public)成员。可以根据需要为其他成员添加其他访问者。为了允许修改成员,该类可以提供“设置”访问器,也可以提供返回引用的访问器。对于非公共(public)函数,该类可以提供仅传递给实际函数的包装器函数。

    的确,除B之外的其他B::geta成员也有可能利用友谊,但是现在输入A::AccessForB::确实不能算是偶然。

    09-04 16:04
    查看更多