如何在另一个类中使用前置声明一个类的成员函数?

21
我创建了两个完全相同的类 X 和 Y,并在它们之间互相使用指针。下面是 X.h 的代码,Y.h 与所有 X 和 Y 互换。然而,在我的 Connect 方法中出现了一个错误(错误 C2027:使用未定义的类型“Y”)。在 X.h 中,我已经前向声明了类 Y,但它不知道 Y 有一个名为 SetXPointer 的方法。因此,我还需要前向声明这个方法,对吗?
如果我尝试这样做(在 class Y; 下添加一行 Y::SetXPointer(X* pX_in);),我会得到编译器错误 C2761:“void Y :: SetXPointer(X *)”:不允许成员函数重声明。有没有办法在类 X 中使用类 Y 的公共方法?
// X.h

#pragma once

#include "Y.h"

// Forward declaration
class Y;

class X
{
public:
    X(void) : data(24) {};
    ~X(void) {};
    int GetData() { return data; }
    void SetYPointer(Y* pY_in) { pY = pY_in; }
    Y* GetYPointer() { return pY; }
    void Connect(Y* Y_in) { pY = Y_in; Y_in->SetXPointer(this); }
private:
    int data;
    Y *pY;
};
4个回答

26

不要将方法体包含在类体中。编写两个类,在两个类都完成后,再编写方法实现:

class Y;
class X {
  …
  void Connect(Y* Y_in);
  …
};
class Y {
  …
  void Connect(X* X_in);
  …
};
inline void X::Connect(Y* Y_in) {
  pY = Y_in;
  Y_in->SetXPointer(this);
}
inline void Y::Connect(X* X_in) {
  pX = X_in;
  X_in->SetXPointer(this);
}

这样,通过实现Connect方法之前,就可以获得关于类对象在内存中布局的全部信息。而且,由于在类体中定义和声明为inline的方法都会以同样的方式进行内联,因此性能也将相同。

唯一的缺点是您不能合理地将这两个类拆分到两个头文件中。


我将两个答案结合起来,并将实现放在单独的cpp文件中。如果我删除关键字inline,它可以工作,否则,我会得到一个链接器错误LNK2019:未解析的外部符号“public: void __thiscall Y :: Connect(class X *)”(?Connect@Y@@QAEXPAVX@@@Z)在函数_main中引用 - physicalattraction
2
inline函数必须放在其类所在的头文件中。否则,实现它们的编译单元将不会为它们生成链接器符号,因为它期望编译器将它们内联。其他任何编译单元都不会知道内联,因此会生成对永远不可用的符号的引用。 - MvG

4

如果你的两个类在其方法中都需要完整类型,则唯一的方法是将实现分离到一个实现文件中。


1
它必须在类体外,但不一定在单独的文件中。如果您想将其保持为内联,则必须是头文件,而不是实现文件。 - MvG
@MvG建议使用一个常见的头文件,虽然这样做可以工作,但并不是一个好建议。 - Luchian Grigore
为什么?这些类是密切相关的。如果您想要内联一些东西,没有其中一个就不能包含另一个。那么它们为什么不应该共享一个头文件呢? - MvG
@MvG 为什么你需要这些方法是内联的? - Luchian Grigore
不一定需要。主要原因是性能,并且另一个原因是尽可能接近生成的机器代码的原始问题。我知道我会将这样的东西内联,因为我的书中两个简单的赋值不值得用方法调用的力气。 - MvG
@MvG 我怀疑瓶颈不是函数调用。这完全取决于代码组织。记得不止你一个人在使用它。你知道我在哪里可以找到 X 类的定义吗?当然,它在 Y.h 中... - Luchian Grigore

3
你可以将B类放入A类中。
using namespace std;

class A
{
        public:
        void run()
        {
                B b("hi");
                b.run(this);
        }

        void print(string &msg) { cout << msg << endl; }

        private:
        class B
        {
                public:
                B(string m) : msg(m) {}
                void run(A *a) { a->print(msg); }

                private:
                string msg;
        };
};

int main()
{
        A a;
        a.run();
        return 0;
}

嵌套类可以非常有用! - nicodjimenez

2

如果你想在X和Y之间共享大部分实现,可以考虑使用模板进行操作。以下是一个示例:

template<bool isX> class XY
{
public:
  typedef XY<!isX> YX; // This is the opposite type to the current one.
  XY(void) : data(24) {};
  ~XY(void) {};
  int GetData() { return data; }
  void SetPointer(YX* pYX_in) { pYX = pYX_in; }
  YX* GetPointer() { return pYX; }
  void Connect(YX* YX_in) { pYX = YX_in; YX_in->SetPointer(this); }
private:
  int data;
  YX *pYX;
};

typedef XY<true> X;
typedef XY<false> Y;

模板方法只有在使用时才实例化,因此您避免了上面提到的问题,因为在它们实例化时,两种类型都已知。如果以后XY之间存在差异,则可以使用继承代替typedef


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接