友类或友成员函数-前向声明和包含头

Friend Class or Friend member function - Forward declaration and Header inclusion

本文关键字:声明 包含头 成员 函数      更新时间:2023-10-16

这个问题已经被讨论了很多次了。我几乎很清楚它们之间的区别。我对书中的一个例子只有一个疑问。

这个问题与我之前的问题有关,我在《c++入门》一书中给出了2个类作为例子。

对于这些类,书中引用了以下段落,特别涉及到将WindowManager类的成员函数声明为友元函数。它是这样写的:

将成员函数设置为友元需要对程序进行仔细的构造适应声明和定义之间的相互依赖关系。在这个例如,我们必须按如下方式对程序进行排序:

  • 首先,定义Window_mgr类,它声明,但不能定义clear。Screen必须在clear之前声明才能使用Screen的成员。
  • 接下来,定义Screen类,包括一个友元声明用于clear。
  • 最后,定义clear,它现在可以引用Screen中的成员。

我在那个问题中给出的代码只遵循这个结构。但这似乎并没有奏效。这让我想到,如果以上几点是误导,或者我没有正确地实现它。

问题是,当我在ScreenCls类中声明clear函数为友元函数时,我陷入了头文件的循环包含。我将在这里再次简要介绍这两个类的具体部分:

ScreenCls.h:

#ifndef SCREENCLS_H
#define SCREENCLS_H
#include <iostream>
#include "WindowManager.h"
using namespace std;
class ScreenCls {
    friend void WindowManager::clear(ScreenIndex);
    // Some other code
}

这里我必须包含WindowManager.h头文件,因为clear函数现在使用那里定义的ScreenIndex前向声明在这里不起作用(如果我错了请纠正我)。

现在,我们继续WindowManager.h:

#ifndef WINDOWMANAGER_H
#define WINDOWMANAGER_H
#include <iostream>
#include <vector>
#include "ScreenCls.h"
using namespace std;
class WindowManager {
public:
    // location ID for each screen on window
    using ScreenIndex = vector<ScreenCls>::size_type;
private:
    vector<ScreenCls> screens{ ScreenCls(24, 80, ' ') };
};

在这里集中讨论screens的声明。他们使用列表初始化器将默认的ScreenCls添加到vector。因此,这里我们需要再次包括WindowManager.h。现在我们进入循环包涵体。这将阻止我的项目构建。

但是,如果我更改友元函数声明,使整个类都成为友元,那么我可以使用forward declaringWindowManager类。在这种情况下,它会工作得很好。

基本上朋友函数在这里不起作用,但是朋友类在起作用。那么,是上述几点的实施不顺利,还是我的类有问题?我只是想知道这个,清楚地理解header inclusionforward declaration的概念。

我上一个问题中的问题很好地描述了这一点。但是它在上面的情况下不工作,所以我再问一遍。

我想你的问题是屏幕初始化器。不能初始化类内部*.h文件中的任何数据。所以,我建议你这样做:

#ifndef WINDOWMANAGER_H
#define WINDOWMANAGER_H
#include <iostream>
#include <vector>
//#include "ScreenCls.h"
using namespace std;
class ScreenCls;
class WindowManager {
public:
    // location ID for each screen on window
    using ScreenIndex = vector<ScreenCls>::size_type;
private:
    vector<ScreenCls> screens; //{ ScreenCls(24, 80, ' ') };    remove this
};

只要你不使用类,即在对象上调用方法或调用new for instance或保留类的实例数组,你可以只使用前向声明。作为一个经验法则:如果你使用前向声明时编译器没有报错,那么就使用它们,并避免使用会减慢编译速度的include。

唯一的危险:当您使用多重继承进行强制转换而没有包含时,强制转换将不能很好地工作-但这通常会在您应该包含您使用的类的.cpp中进行。