Re: Доопределение методов подклассов при наследовании
От: Кодт Россия  
Дата: 17.06.10 14:30
Оценка: 17 (5)
Здравствуйте, 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*/ { ...... }
};
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.