C ++ конструирование и обнаружение динамических типов - PullRequest
0 голосов
/ 08 апреля 2010

В C ++ возникла интересная проблема, но она была больше связана с архитектурой.

Существует множество (10, 20, 40 и т. Д.) Классов, описывающих некоторые характеристики (смешанные классы), например:

struct Base { virtual ~Base() {} };

struct A : virtual public Base { int size; };
struct B : virtual public Base { float x, y; };
struct C : virtual public Base { bool some_bool_state; };
struct D : virtual public Base { string str; }
// ....

Основной модуль объявляет и экспортирует функцию (для простоты просто объявления функций без классов):

// .h file
void operate(Base *pBase);

// .cpp file
void operate(Base *pBase)
{
    // ....
}

Любой другой модуль может иметь такой код:

#include "mixing.h"
#include "primary.h"

class obj1_t : public A, public C, public D {};
class obj2_t : public B, public D {};

// ...
void Pass()
{
    obj1_t obj1;
    obj2_t obj2;

    operate(&obj1);
    operate(&obj2);
}

Вопрос в том, как узнать, каков реальный тип данного объекта в operate() без использования dynamic_cast и какой-либо информации о типах в классах (константы и т. Д.)?Функция operate() используется с большим массивом объектов за небольшие промежутки времени, и dynamic_cast слишком медленный для нее, и я не хочу включать константы (enum obj_type { ... }), потому что это не OOP-способ.

// module operate.cpp

void some_operate(Base *pBase)
{
    processA(pBase);
    processB(pBase);
}

void processA(A *pA)
{
}

void processB(B *pB)
{
}

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

Одно решение, которое пришло в голову, в редакторе я могу использовать составной контейнер:

struct CompositeObject
{
    vector<Base *pBase> parts;
};

Но редактор не нуждается в оптимизации времени и может использовать dynamic_cast для деталей, чтобы определить точный тип.В operate() я не могу использовать это решение.

Итак, можно ли избежать использования dynamic_cast и информации о типе для решения этой проблемы?Или, может быть, я должен использовать другую архитектуру?

Ответы [ 4 ]

2 голосов
/ 08 апреля 2010

Реальная проблема здесь в том, что вы пытаетесь достичь.

Хотите ли вы что-то вроде:

void operate(A-B& ) { operateA(); operateB(); }

// OR

void operate(A-B& ) { operateAB(); }

То есть вы хотите применить операцию к каждому подкомпоненту?(независимо), или вы хотите применять операции в зависимости от комбинации компонентов (гораздо сложнее).

Я выберу первый подход здесь.

1,Виртуальный?

class Base { public: virtual void operate() = 0; };

class A: virtual public Base { public virtual void operate() = 0; };
void A::operate() { ++size; } // yes, it's possible to define a pure virtual

class obj1_t: public A, public B
{
public:
  virtual void operate() { A::operate(); B::operate(); }
};

Еще немного работы, наверняка.Примечательно, что мне не очень нравится повторение.Но это один вызов _vtable, поэтому это должно быть одно из самых быстрых решений!

2.Composite Pattern

Это, вероятно, было бы более естественным здесь.

Обратите внимание, что вы можете прекрасно использовать шаблонную версию шаблона в C ++!

template <class T1, class T2, class T3>
class BaseT: public Base, private T1, private T2, private T3
{
public:
  void operate() { T1::operate(); T2::operate(); T3::operate(); }
};

class obj1_t: public BaseT<A,B,C> {};

Преимущества:

  • больше не нужно повторяться!напишите operate раз и навсегда (за исключением переменной ...)
  • только 1 виртуальный вызов, больше нет виртуального наследования, поэтому еще более эффективно, чем до
  • A, Bи C может быть произвольного типа, они вообще не должны наследоваться от Base
  • edit operate метод A, B и Cтеперь можно указать, что это не virtual

Недостаток:

Еще немного поработать над фреймворком, если у вас еще нет доступа к шаблонам с переменными параметрами, но это возможнопара десятков строк.

2 голосов
/ 08 апреля 2010

Первое, что приходит на ум, это спросить, чего вы действительно хотите достичь ... но опять же, вторая мысль, что вы можете использовать шаблон посетителя. Информация о типе среды выполнения будет неявно использоваться для определения того, в какой точке иерархии есть окончательный переопределитель метода accept, но вы не будете явно использовать эту информацию (ваш код не будет показывать никаких dynamic_cast, type_info, констант .. .)

Опять же, моя первая мысль возвращается ... так как вы спрашиваете об уместности архитектуры, чего вы действительно хотите достичь? - без знания проблемы вы найдете только общие ответы, как этот.

1 голос
/ 08 апреля 2010

Ваша проблема в том, что вы хотите решить, что делать на основе более чем одного типа объекта.Виртуальные функции делают это только для одного объекта (слева от . или ->).Для более чем одного объекта это называется множественная отправка (для двух объектов это также называется двойная отправка ), а в C ++ естьнет встроенной функции, чтобы справиться с этим.

Посмотрите на двойную отправку, особенно как это сделано в шаблоне посетитель .

1 голос
/ 08 апреля 2010

Обычным объектно-ориентированным способом было бы иметь (чистые) виртуальные функции в базовом классе, которые вызываются в operate() и которые переопределяются в производных классах для выполнения кода, специфичного для этого производного класса.

...