我的目标很简单 - 我想从另一个类的方法访问一个类的受保护成员。为此,我有以下 -
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;
}
当我编译这个时,我得到以下错误。
我该如何解决?我在这里看到的大多数答案只是将所有类放在一个文件中并在适当的位置定义函数来实现这一点,但我需要它们在单独的文件中。
您当然可以将B::geta
的定义移动到包含A.hpp的B.cpp文件中,并删除inline
关键字。但这可能会降低编译器优化的可能性。
#include
logic只有当编译器看到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"
,预处理器将:
class A;
和B
的定义。INCLUDED_FROM_B_HPP
。INCLUDED_FROM_B_HPP
的定义。B
内联成员的定义。如果一个文件首先执行#include "A.hpp"
,事情有点棘手:
INCLUDED_FROM_B_HPP
,只需立即进入B.hpp。class A;
和B
的定义。INCLUDED_FROM_B_HPP
。#include "A.hpp"
时,预处理器将以递归方式返回到A.hpp。但是这次定义了INCLUDED_FROM_B_HPP
,它输出了A.hpp的代码内容。INCLUDED_FROM_B_HPP
的定义。B
内联成员的定义。friend class B;
而不是仅仅向朋友指定一个成员函数B::geta
,只需与类本身建立联系,声明为friend class B;
。现在A.hpp不需要包含B.hpp,因此没有循环依赖问题。
从访问是和不可能的角度来看,这并没有太大减少封装,因为通常任何可以修改class B
的任何部分的程序员也可以修改B::geta
。但它确实在A
的其他成员中使用B
的非公开成员“意外地”开辟了可能性。
#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
的任何其他非公共成员。可以根据需要为其他成员添加其他访问者。要允许修改成员,该类可以提供“set”访问器,或者提供返回引用的访问器。对于非公共函数,该类可以提供仅传递给实际函数的包装函数。
除了B
之外,B::geta
的成员有可能利用友谊,但现在输入A::AccessForB::
并不能真正被视为意外。
...但我需要它们在单独的文件中。
将内联函数移动到单独的B.cpp
文件,#include
的A.hpp
。
由于刚刚被视为转发类声明,A
的声明尚未完成,您将开始在B.hpp
中使用它,并且编译器会抱怨这一点。
您仍然可以保留inline
关键字,编译器会像往常一样将其视为提示。
这是草图:
#ifndef B_HPP
#define B_HPP
using namespace std;
class A;
class B
{
protected:
int b;
public:
B();
~B(){};
void geta(A& ao);
};
#endif
#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;
}
这些问题和答案与您的问题非常相关: