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++。