How to forward declare a member function of a clas

2019-01-22 14:54发布

问题:

I have made two identical classes X and Y, with a pointer to each other. See the code below for X.h, Y.h is identical with all X's and Y's interchanged. This code gives however an error in my method Connect (error C2027: use of undefined type 'Y'). In X.h, I have forward declared the class Y, but it doesn't know that Y has a method named SetXPointer. Therefore I also need to forward declare this method, correct?

If I try to do this (adding the line Y::SetXPointer(X* pX_in); under the line class Y;), I get a compiler error C2761: 'void Y::SetXPointer(X *)' : member function redeclaration not allowed. Is there a way to use a public method of class Y in class X?

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

回答1:

Don't include the method body in the class body. Write both classes, and after both classes are complete, write the method implementations:

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

That way, full information about how the objects of the class will be layed out in memory is available by the time the Connect method is implemented. And as a method in the class body and a method declared inline will both be inlined the same way, performance will be the same as well.

The only downside is that you won't be able to split these two classes over two headers in a reasonable way.



回答2:

The only way to do this, if both your classes need a full type in their methods, is to separate the implementation to an implementation file.



回答3:

If you intend to share large parts of the implementation between X and Y, you might want to do that using a template. One example is the following:

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;

As template methods are only instantiated when used, you avoid the problem outlined above, as by the time they are instantiated, both types are known. If you later on have differences between X and Y, you might use inheritance instead of typedef.



回答4:

You can place class B into class 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;
}