C++ 模板化类具有循环依赖关系

C++ 模板化类具有循环依赖关系,c++,templates,C++,Templates,我有两节课。一个是管理类,它存储一组工人类。工人实际上是一个模板化的类 #include "worker.h" class Management { private: worker<type1> worker1; worker<type2> worker2; ... }; 工作进程inl文件: #include "Management.h" //Circular Dependency! Worker::Wo

我有两节课。一个是管理类,它存储一组工人类。工人实际上是一个模板化的类

#include "worker.h"    
class Management {

     private: 
      worker<type1> worker1;
      worker<type2> worker2;
      ...

};
工作进程inl文件:

#include "Management.h"  //Circular Dependency!

Worker::Worker(){
    //Management is accessed here
    Management mgmt1();
    mgmt1.doSomething(); //Can't use that forward declaration!
}
...
通常,您会在Worker头文件中转发declare Management.h,并将其终止。不幸的是,由于类是模板化的,所以它总是会被包括在内

我想你可以说这个设计很糟糕,因为模板化类如果需要知道这类信息就不应该被模板化,但它就是这样,我必须使用它


你也可以把这个问题看作办公室生活的缩影

如果worker.inl没有引用
管理层
的实际成员(即仅指向
管理层
的指针/引用),您可以转发声明:

class Management;

Worker::Worker(){
    Management* mgmt1; // fine
    Management& mgmt2; // fine
    //mgmt1 = new Management(); // won't compile with forward declaration
    //mgmt2.doSomething(); // won't compile with forward declaration
}
否则,您唯一的选择可能是将方法实现移动到cpp文件中,而不包括在标题中

请注意,
Worker
不能向前声明,因为它由
管理中的值包含:

  worker<type1> worker1;
工人工人1;
因此,编译器此时需要知道它的完整定义

您还应该考虑将两个类定义放在同一个头文件中,以表示它们是相互依存的,从而形成一个组件。

< P>我假定您是为了避免由于递归包含而导致的编译错误。


但要解决这个问题,我认为解决方案是使用前向声明,并在必要时使用指针/引用。

我看不出模板如何改变这一点。转发声明以删除标头中的循环依赖项

编辑:对不起,我一开始就颠倒了。第二个示例是正确的(请参阅)

模板类工人

class Managment
{
    worker<type1> a;
    worker<type2> b;
};

template<typename T> class worker
{
///... okay to use class Managment here.
};
类管理
{
工人a;
工人b;
};
模板类工作程序
{
///…可以在这里使用班级管理。
};

#包括
类type1{};
类类型2{};
班级管理;
模板结构工作程序
{
内联void doSomethingElse();
};
班级管理
{
工人a;
工人b;
公众:
内联无效剂量测定法();
};
内联无效管理::doSomething()
{
a、 doSomethingElse();
b、 doSomethingElse();
}
模板
内联void worker::doSomethingElse()
{

std::cout解决这个问题的一种方法是使用一个接口。基本上,
Worker
需要在
Management
上执行的所有操作都放在这样的接口中:

interface IManagementOps
{
public:
    void DoStuff() = 0;
};

class Worker
{
    IManagementOps* pOps;
    ...     
};

class Management : public IManagementOps
{
    ...
};
template <class mngmnt, int type > class worker{ mngmnt* p_mngmnt; public: char* getManagementName(){return p_mngmnt->management_name;} }; class Management { public: char* management_name; private: worker<Management,1> worker1; worker<Management,2> worker2; };
但请注意,您的设计很好,而且它实际上适合
Worker
使用
Management

进行操作。如果您确实需要从Worker类访问管理,则可以将管理作为附加模板参数添加到Worker中。 也就是说,类似这样的事情:

interface IManagementOps
{
public:
    void DoStuff() = 0;
};

class Worker
{
    IManagementOps* pOps;
    ...     
};

class Management : public IManagementOps
{
    ...
};
template <class mngmnt, int type > class worker{ mngmnt* p_mngmnt; public: char* getManagementName(){return p_mngmnt->management_name;} }; class Management { public: char* management_name; private: worker<Management,1> worker1; worker<Management,2> worker2; }; 模板 班主任{ mngmnt*p_mngmnt; 公众: char*getManagementName(){return p_mngmnt->management_name;} }; 班级管理{ 公众: 字符*管理名称; 私人:
workerI我认为这与模板无关……可能是重命名和重新标记?@Oli:如果与模板无关,那么它会重复多次。在您发布的代码中,
Worker
构造函数创建一个
管理
对象,该对象包含两个
Worker
对象,其构造函数被调用,并且创建…你确定要这样做吗?你要解决的真正问题是什么?可能有比你所采取的方法更好的解决方案。在实际代码中,管理类保存工作人员使用的信息,工作人员持有指向该信息的指针。我在工作人员中实例化了管理类以模拟是。我不能发布真正的代码。好吧,编译器需要知道
工作者
对象的确切大小,从而知道它的完整类定义,才能解决这个问题。也许我误解了您的代码段,但是如果您的第一行只是
工作者
的转发声明,那么编译器就不知道
管理有多大de>对象是。@Oli:我不明白为什么编译器需要知道此时管理对象有多大——当然,Worker需要是一个完整的类型才能实例化管理,但据我所知,在声明时并不需要它——它只是一个声明。但你是对的,它没有编译。我已经更新了我的answe使用编译示例。对,需要在管理类上执行一些操作。与mgmt2.doSomething()完全相同。这就是我需要.h文件的原因。听起来唯一的解决方法就是重写。哦,很高兴。实际上,您可以在这里使用值类型——它不必是指针。例如:@tenfour:将有运行时开销(
虚拟有成本
)但是我同意这不太可能引起注意。@Matthieu,如果您从不使用指针或引用,可以避免运行时开销。如果您调用的是具体对象的成员,编译器知道将调用哪个成员并绕过虚拟查找。@Mark:但这会破坏使用接口的意义!您也可以使用
\uuu declspec(novtable)
。重点是将
Worker
使用的操作捆绑到一个界面中,这是一个组织问题。实际上,我通常更喜欢Billy ONeal的解决方案,但有时界面方法也更干净,这取决于上下文。
interface
我认为这是MS唯一的东西,但你也可以类似地使用
类< /代码>。@ TAN4:是的,<代码>接口< /代码>不是,也从来没有标准C++。