C++ 友元函数-声明顺序

C++ 友元函数-声明顺序,c++,header-files,friend-function,C++,Header Files,Friend Function,我有两个类叫做Screen和Window\u mgr 屏幕允许窗口管理器通过好友函数声明修改其私有/受保护成员 因此,Window\u-mgr在代码的最后定义了一个名为Window\u-mgr::clear的非成员函数,该函数应该使用它 不幸的是,我犯了一些可笑的错误,我无法解释自己 我错过了什么 屏幕.h #pragma一次 #ifndef屏幕 #定义屏幕 #包括 #包括 班级窗口经理{ 公众: //窗口上每个屏幕的位置ID 使用ScreenIndex=std::vector::size\u

我有两个类叫做
Screen
Window\u mgr

屏幕
允许
窗口管理器
通过好友函数声明修改其私有/受保护成员

因此,
Window\u-mgr
在代码的最后定义了一个名为
Window\u-mgr::clear
的非成员函数,该函数应该使用它

不幸的是,我犯了一些可笑的错误,我无法解释自己

我错过了什么

屏幕.h
#pragma一次
#ifndef屏幕
#定义屏幕
#包括
#包括
班级窗口经理{
公众:
//窗口上每个屏幕的位置ID
使用ScreenIndex=std::vector::size\u type;
//在给定位置将屏幕重置为所有空白
空白清除(屏幕索引);
私人:
病媒筛选{Screen(24,80,')};
};
类屏幕{
公众:
//朋友
朋友无效窗口管理器::清除(屏幕索引);
//朋友班窗口经理;
//田地
//typedef=>创建别名
//typedef std::string::size_type pos;
//使用类型别名声明类型成员的替代方法
使用pos=std::string::size\u类型;
//建设者
Screen()=default;//需要,因为Screen有另一个构造函数
//游标由其类内初始值设定项初始化为0
屏幕(位置ht,位置wd,字符c):高度(ht),宽度(wd),内容(ht*wd,c){}//获取光标处的字符
Screen&display(std::ostream&os)//函数位于类body=>隐式内联中
{
do_显示(操作系统);
归还*这个;
}
const Screen&display(std::ostream&os)const//函数位于类body=>隐式内联中
{
do_显示(操作系统);
归还*这个;
}
//方法
char get()const{return contents[cursor];}//隐式内联
内联字符get(pos-ht,pos-wd)const;//显式内联
屏幕和移动(位置r、位置c);//稍后可以内联进行
屏幕和设置(字符);
屏幕和设置(pos、pos、char);
私人:
//田地
可变大小访问控制;
pos游标=0;
位置高度=0,宽度=0;
std::字符串内容;
//方法

void do_显示(标准::ostream&os)常量{os您不能在
窗口\u mgr
类中声明
Screen
对象的向量,因为编译器在代码中的该点不知道
Screen
。如果要声明指针向量,您可以通过向前声明
Screen
来修复它,但对于实际对象的向量,则需要完整定义必须是可用的

您需要切换
窗口管理器
屏幕
的顺序,并向
窗口管理器
类声明友谊:

class Screen {
public:
    friend class Window_mgr;
    ...
};
class Window_mgr {
public:
    // location ID for each screen on the window
    using ScreenIndex = std::vector<Screen>::size_type;
    // reset the Screen at the given position to all blanks
    void clear(ScreenIndex);
private:
    std::vector<Screen> screens{ Screen(24, 80, ' ') };
};
类屏幕{
公众:
朋友班窗口经理;
...
};
班级窗口经理{
公众:
//窗口上每个屏幕的位置ID
使用ScreenIndex=std::vector::size\u type;
//在给定位置将屏幕重置为所有空白
空白清除(屏幕索引);
私人:
病媒筛选{Screen(24,80,')};
};
为什么编译器知道
Window\u-mgr
但不知道
Window\u-mgr::ScreenIndex

C++对友谊声明中使用的类名有一个特殊规则:

如果尚未声明友元声明中使用的类的名称,则会当场进行前向声明

这就是编译器“知道”窗口管理器的原因(也就是说,它不知道;你可以相信它)。对于在“befriended”类中声明的成员函数或成员类型,没有这样的规则。这就是为什么编译器不知道关于
窗口管理器::ScreenIndex
,我建议不要使用“friend”为此,只需将clear()public member函数添加到类屏幕,并从窗口管理器调用它

// declaration
Screen &clear();

// definition
inline Screen &Screen::clear() {
    contents.resize(height * width, ' ');
    return *this; // return this object as an lvalue
}

// use
void Window_mgr::clear(ScreenIndex i)
{
    // s is a reference to the Screen we want to clear
    Screen &s = screens[i];
    // reset the contents of that Screen to all blanks
    s.clear();
}

中查看它是否缺少
#include
#include
?很抱歉,忘记复制它的最新版本,即使包含向量,我仍然会收到关于“屏幕:未声明的标识符”的错误消息等等。我在文件顶部添加了一个
类屏幕;
用于声明
屏幕
,但这并不能完全解决问题。
类屏幕;
在顶部可以解决问题,如果你想做
向量
,但是对于
向量
,必须提供完整的
屏幕
。非常感谢你的帮助您的答案-这是我最终找到的解决方案之一。不幸的是,这不适用于
friend void Window\u mgr::clear(ScreenIndex)
而不是
朋友类窗口管理器;
-这是因为它自己引用的朋友函数声明是
窗口管理器的
ScreenIndex
-知道如何避免这种交叉引用吗?是的,仍然是相同的错误。无论如何,我认为会有一个简单的解决方案。尽管我仍然不能完全理解nd为什么
好友类窗口\u mgr
没有问题,而
好友无效窗口\u mgr::clear(窗口\u mgr::屏幕索引)没有问题
。两者都引用了
窗口\u mgr
或编译器此时不应知道的其中一个成员。@Hansmusterwhat若要向前声明函数,编译器需要在向前声明窗口\u mgr::clear(窗口\u mgr::ScreenIndex)时定义参数类型编译器不知道什么是Window\u mgr::ScreenIndex,您正在将Window\u mgr的定义推迟到Screen定义之后。那么,为什么编译器知道
Window\u mgr
,而不知道
Window\u mgr::ScreenIndex
?这是基于它与
friend class Window\u mgr;
@HansmusterHatels完美配合的知识e有关此差异的解释,请参见编辑。
// declaration
Screen &clear();

// definition
inline Screen &Screen::clear() {
    contents.resize(height * width, ' ');
    return *this; // return this object as an lvalue
}

// use
void Window_mgr::clear(ScreenIndex i)
{
    // s is a reference to the Screen we want to clear
    Screen &s = screens[i];
    // reset the contents of that Screen to all blanks
    s.clear();
}