2010-08-23 4 views
3

У меня есть два класса. Один из них - это класс управления, в котором хранится куча рабочих классов. Рабочие на самом деле являются шаблонами.Templated Class имеет круговую зависимость

#include "worker.h"  
class Management { 

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

}; 

Проблема возникает из-за того, что шаблонные классы должны использовать Management.

Пример:

class Worker{ 
    ... 
}; 

#include "Worker.inl" 

Рабочий INL файл:

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

Worker::Worker(){ 
    //Management is accessed here 
    Management mgmt1(); 
    mgmt1.doSomething(); //Can't use that forward declaration! 
} 
... 

Обычно вы бы вперед объявить Management.h в заголовочном файле рабочий, и назвать его в день. К сожалению, поскольку шаблон шаблонизирован, он всегда будет включен.

Я думаю, вы можете утверждать, что дизайн плох, поскольку шаблонный класс не должен быть шаблоном, если ему нужно знать такую ​​информацию, но это то, что есть, и я должен работать с ним.

Вы также можете просмотреть этот вопрос в качестве микромира офисной жизни.

+0

Я не думаю, что это имеет какое-либо отношение к шаблонам ... возможно, переименовать и повторно пометить? –

+0

@Oli: Если это не имеет никакого отношения к шаблонам, то это дубликат несколько раз. –

+0

В коде, который вы опубликовали, конструктор Worker создает объект 'Management', который содержит два' Worker ', конструктор которых вызывается и создает ... Вы уверены, что хотите это сделать? Какова реальная проблема, которую вы хотите решить? Там могут быть лучшие решения, чем подход, который вы принимаете. –

ответ

2

Если нет реальных членов Management не называются по worker.inl (то есть только указатели/ссылки на Management), вы могли бы переслать объявить:

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 не может быть вперед объявлены, так как он содержится в стоимостном выражении в Management:

worker<type1> worker1; 

таким образом, компилятор должен знать свое полное определение в этой точке.

Вы также должны рассмотреть вопрос о переносе двух определений классов в один и тот же заголовочный файл, чтобы выразить, что они взаимозависимы, таким образом, образуют компонент.

+0

Правильно, некоторые действия должны выполняться в классе управления. Точно так же, как mgmt2.doSomething(). Вот почему мне нужен файл .h. Звучит для меня, что единственный способ обойти это немного переписать. О, радость. – Alex

+0

На самом деле вы можете использовать типы значений здесь - это не должны быть указатели. Пример: http://codepad.org/gtLynfPC –

0

Я предполагаю, что вы используете include guards, чтобы избежать ошибки компиляции из-за рекурсивного включения.

Но для решения проблемы я считаю, что решение заключается в использовании форвардных деклараций и при необходимости использовать указатели/ссылки.

1

Я не вижу, как шаблоны меняют это. Вперед объявить об удалении круговых зависимостей в заголовках.

РЕДАКТИРОВАТЬ: Извините, я отменил это изначально.Второй пример является правильным (см. CodePad)

рабочий класс шаблона;

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

template<typename T> class worker 
{ 
///... okay to use class Managment here. 
}; 

#include <iostream> 

class type1 {}; 
class type2 {}; 

class Managment; 

template<typename T> struct worker 
{ 
    inline void doSomethingElse(); 
}; 

class Managment 
{ 
    worker<type1> a; 
    worker<type2> b; 
public: 
    inline void doSomething(); 
}; 


inline void Managment::doSomething() 
{ 
    a.doSomethingElse(); 
    b.doSomethingElse(); 
} 

template<typename T> 
inline void worker<T>::doSomethingElse() 
{ 
    std::cout << "Hello!"; 
} 

int main() 
{ 
    Managment mgmt; 
    mgmt.doSomething(); 
} 
+1

Это работает только с указателями и ссылками на 'worker ' ... –

+0

AFAIK компилятору необходимо знать точный размер объекта «worker» - таким образом, его полное определение класса - для решения этой проблемы. –

+0

Возможно, я неправильно понимаю ваш фрагмент кода, но если ваша первая строка - просто простое объявление 'worker ', то компилятор не будет знать, насколько велик объект 'Management'. –

1

Один из способов решить эту проблему с чистым синтаксисом & не во время выполнения накладного является использование интерфейса. В основном, все операции, которые Worker необходимо выполнить на Management, положить в какой-то интерфейс, как это:

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

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

class Management : public IManagementOps 
{ 
    ... 
}; 

Пожалуйста, обратите внимание, что ваш дизайн хорош, хотя и что это на самом деле подходит для Worker, чтобы делать вещи с Management.

+0

@tenfour: накладные расходы на запуск ('virtual is a cost'), но я согласен, что это вряд ли будет заметно. –

+1

'#define interface class' ??? –

+0

@Matthieu, накладные расходы времени выполнения можно избежать, если вы никогда не используете указатели или ссылки. Если вы вызываете члена конкретного объекта, компилятор знает, какой член будет вызываться и обходит виртуальный поиск. –

0

Если вам действительно нужно получить доступ к управлению из класса работников, вы можете добавить Управление в качестве дополнительного параметра шаблона для рабочего. I.e. что-то вроде этого:

 
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; 

}; 

Смежные вопросы