仅头文件库:使用稍后在头文件中定义的类

时间:2017-05-30 15:00:57

标签: c++

我正在创建一个仅限标头的库。在这个库中,我想使用一个稍后在头文件中定义的类。例如:

class A {
public:
  int i;

  A(int x) : i(x) {};

  B func(B inp) {
    B out(i*inp.j);
    return out;
  };
};

class B {
public:
  int j;

  B(int x) : j(x) {};

  A other(A inp) {
    A out(j*inp.i);
    return out;
  };
};

当然我的编译器不喜欢这个。它在定义func的行上产生第一个错误:

 error: unknown type name 'B'

有没有办法克服这个问题?

3 个答案:

答案 0 :(得分:4)

一种方法是转发声明类B,并将函数定义移出类。

通常会将定义放在源文件中,但由于它是一个仅限标题的库,您可以将函数定义标记为inline,并且可以将其放在头文件本身中,但是在定义之后当然是B课程。

如果B不依赖A,则另一种更简单的方法是将B的定义移到上面的定义头文件中的A

答案 1 :(得分:4)

使用引用指针需要相应类型的声明。使用对象(实例)或任何成员需要完整的定义或相应的类型。宣言是

class A;   // declares: there is a class called 'A'

虽然定义是

class A {  // (re-)declares A and defines its layout and members
   /* details */
};

声明可以在定义之前(但不必使定义有效)。这立即给出了以下解决方案。

  1. 订购类 definitions ,以便以后定义的类仅在其中使用先前定义的类的对象成员接口。例如

    class A {
      int i;
    public:
      A(int x) : i(x) {}
    };
    
    class B {
      A a;                // object of type A: okay, since A is defined
    public:
      B(int i) : a(i) {}
      int foo() const {
        return a.i;       // member of a: okay, since A::i is defined
      }
    };
    
  2. 在任何早期声明的类中使用引用指针的类可以转发声明(不是定义的)在任何类定义之前。例如

    class B;              // forward declaration of B
    
    class A {
      int i;
    public:
      A(int x) : i(x) {}
      A(B*);              // pointer to B okay, since B is declared
                          // cannot define A::A(B*): it depends on definition of B
    };
    
    class B {
      int x;
      A a;                // object of type A: okay, since A is defined
    public:
      B(int i) : x(i+i), a(i) {}
      B() : a(this);
    }
    
  3. 定义任何依赖于声明时未知的类定义的实现细节。

    inline A::A(B*b)      // either inline or in a source file
      : i(b->x/2)         // using member B::x okay: B is defined
    

答案 2 :(得分:1)

不是说这是可行的方法,但是如果你对模板的一些误用很好,你可以做这样的事情(相同的例子但是删除了不相关的东西,并且如你所提到的那样使用A添加了B):< / p>

template <typename X>
struct A {
  X func(X inp) { return X(/*...*/); }
};

struct B {
public:
    A<B> func(A<B> inp) { return A<B>(); }
};

正如我所说,我不会这样做只是为了避免前瞻声明。另一方面,如果您确实需要循环依赖,则应重新考虑。如果两个班级相互依赖太多,你就不能接触一个而不必检查另一个班级是否没有破坏。多数民众赞成不好,如果可能,我会避免它。