Здравствуйте, Acteon, Вы писали:
A>Добрый день.
A>Возникла такая проблема (см. код ниже). Необходимо доопределить метод run() в классе BaseThread в наследнике класса Base классе ConcreateClass. Первая идея которая у меня возникла, просто обратиться к методу класса Base переопределенному в ConcreateClass. Так и буду пока делать. Но теперь у меня академический интерес, какие еще могут быть способы решения.
То есть: у Base член baseThread имеет тип BaseThread, но у ConcreteClass тот же самый член должен иметь фактический тип, скажем, ConcreteThread.
Подходы здесь следующие.
1) Вместо inplace сделать указатель — и создавать в конструкторе финального класса нужный объект
class Base
{
shared_ptr<BaseThread> m_thread;
public:
explicit Base(shared_ptr<BaseThread> thread = shared_ptr<BaseThread>(new BaseThread())
{
.....
}
void useThread() { m_thread->doUsefulStuff(); }
};
class Concrete : public Base
{
public:
Concrete() : Base(shared_ptr<BaseThread>(new ConcreteThread()))
{
.....
}
};
2) Развитием этой темы является виртуальное наследование
class ThreadHolder
{
protected:
shared_ptr<BaseThread> m_thread;
explicit ThreadHolder(shared_ptr<BaseThread> thread) : m_thread(thread) {}
};
class Base : public virtual ThreadHolder
{
public:
Base(X,Y,Z) : ThreadHolder(shared_ptr<BaseThread>(new BaseThread()))
{
.....
}
};
class Concrete : public Base // и, соответственно, public virtual ThreadHolder
{
public:
Concrete(T,U) : ThreadHolder(shared_ptr<BaseThread>(new ConcreteThread())) // виртуальная база конструируется вперёд остальных!
, Base(x,y,z) // конструктор базы обойдёт вызов конструктора виртуальной базы
{
.....
}
};
3) Хотя можно и с обычным наследованием обойтись — но с оверхедом
class Base
{
protected:
virtual BaseThread& thread() { return m_thread; }
// обещанный оверхед
BaseThread m_thread;
public:
void useThread() { thread().doUsefulStuff(); }
.....
};
class Concrete : public Base
{
protected:
virtual ConcreteThread& thread() /*override*/ { return m_thread; }
ConcreteThread m_thread;
.....
};
4) либо с двухфазной инициализацией
class Base
{
shared_ptr<BaseThread> m_thread;
protected:
virtual shared_ptr<BaseThread> makeThread() { return shared_ptr<BaseThread>(new BaseThread()); }
public:
void postInit() { m_thread = makeThread(); } // вызвать у полностью сконструированного класса!
void useThread() { m_thread->doUsefulStuff(); }
.....
};
class Concrete : public Base
{
protected:
virtual shared_ptr<BaseThread> makeThread() /*override*/ { return shared_ptr<BaseThread>(new ConcreteThread()); }
.....
};
5) Шаблоны
template<class ThreadType>
class BaseT
{
ThreadType m_thread;
public:
Base() { ..... }
......
};
class Base : public BaseT<BaseThread> {};
class Concrete : public BaseT<ConcreteThread>
{
.....
};
6) Развитием темы шаблонов будет CRTP
template<class Final>
class BaseT
{
/*abstract: m_thread;*/
Final* myself() { return static_cast<Final*>(this); }
public:
void useThread() { myself()->m_thread.doUsefulStuff(); }
.....
};
class Base : public BaseT<Base>
{
friend class BaseT<Base>;
BaseThread m_thread; /*implement*/
.....
};
class Concrete : public BaseT<Concrete>
{
friend class BaseT<Base>;
ConcreteThread m_thread; /*implement*/
.....
};
7) А ещё, можно отказаться от разведения классов xxxxxThread. Возьмём boost::shared_ptr<boost::thread> и подсунем ему нужное замыкание.
Причём можно сделать несколько вещей
— создание объекта потока в финальном классе — аналогично (1), (2) или (4); только вместо new ConcreteThread будет bind(&Concrete::threadfunc,this) или что угодно своё
— создание объекта потока в базовом классе и пауза, а дальше как обычно, паттерн Шаблонный метод, вызов виртуальных функций (имхо, это хак — поскольку фактический тип объекта меняется по ходу выполнения функции)
class Base
{
bool m_passed;
boost::barrier m_gate;
boost::thread m_thread;
void waitForStart() // рабочий поток ждёт
{
assert(!m_passed);
m_gate.wait();
assert(m_passed);
}
void letOff() // ведущий поток запускает рабочего
{
if(m_passed) return; // либо assert(!m_passed)
m_passed = true;
m_gate.wait();
}
void threadEntry()
{
waitForStart();
threadBody(); // в этом месте vfptr уже выставлен на финальный класс
}
virtual void threadBody() { ..... }
public:
Base() : m_passed(false), m_gate(2), m_thread(boost::bind(&Base::threadEntry,this)) {}
void init() { letOff(); } // вызвать после создания финального класса
};
class Concrete : public Base
{
virtual void threadBody() /*override*/ { ...... }
};