2013-12-07 3 views
3

Я довольно новичок в C++ и все еще пытаюсь понять некоторые из тонкостей концепций промежуточного уровня, таких как шаблоны/пишущий общий код. Я пишу приложение, использующее OpenSceneGraph (OSG), и в основном это то, что я пытаюсь сделать:Как ссылаться на переменную без знания определенного типа?

  • Я хочу иметь общий класс элемента, который может обрабатывать любое количество различных элементов «типа»
  • Каждый экземпляр класса элемента должен содержать другую форму (в зависимости от типа)
  • Различные типы элементов (и фигуры, к которым они привязаны) будут обнаружены только во время выполнения, поскольку они зависит от исходных данных - например, может быть 6 разных типов элементов, все из которых представлены ящиками разных размеров. Или могут быть 3 разных типа элементов - одна коробка, один цилиндр, один конус.

Некоторые справочную информацию о OSG, чтобы объяснить источник моего вопроса:

  • osg::Box и osg::Cylinder являются оба вида osg::Shape
  • как производные типы имеют одинаковые методы, getCenter
  • даже если вы можно сделать osg::Shape myShape = osg::Box(); вы не можете сказать myShape.getCenter(); - не работает на osg::Shape объектов.

Вот пример того, что я пытаюсь сделать:

class MyClass { 
private: 
    // ???? How to reference 'shape' ?? 
public: 
    MyClass(string _type) { 
     // This is for example purposes. Eventually types & mappings will be discovered at run-time. 

     if (_type == "FOO") { 
      shape = new osg::Box(); 
     } else if (_type == "BAR") { 
      shape = new osg::Sphere(); 
     } 
    } 
    /* 
     ???? How to handle getShape()?? 
    */ 
} 

int main() { 
    string readFromData = "FOO"; 
    MyClass* myFoo (readFromData); 

    string alsoFromData = "BAR"; 
    MyClass* myBar (alsoFromData); 

    osg::Vec3f fooCenter = myFoo->getShape()->getCenter(); 
    osg::Vec3f barCenter = myBar->getShape()->getCenter(); 
} 

Я пробовал несколько различных подходов, но не вполне был в состоянии работать это:

  • , создавая класс MyShape, который расширяет osg::Shape и имеет заголовок виртуальной функции для getCenter, но это делает MyShape абстрактным классом, который не может быть создан.
  • template<typedef T> class MyClass... - но если мы обнаружим только типы отображения & во время выполнения, то что происходит в скобках на всем остальном моем коде? например, MyClass<?????????>* myFoo;
  • с использованием boost :: любой для хранения формы внутри - но такой же вопрос в основном. Как вы определяете функцию getShape, которая может возвращать указатель на один из нескольких типов?

Я не могу найти никаких предыдущих вопросов, которые касаются этого типа сценариев (извините, если я пропустил один!). Если кто-нибудь может мне помочь, это будет супер круто!

ответ

1

OSG поставляет класс osg::ShapeVisitor для таких ситуаций, как этот. Создайте класс CenterFinderVisitor, который расширяет osg::ShapeVisitor, переопределяя каждую из своих виртуальных функций-членов, чтобы получить центр соответствующей формы. Передайте экземпляр CenterFinderVisitor к функции accept() члена ЕС, osg::ShapeVisitor «s на экземпляре формы, которые хранятся по указателю внутри вашего класса, чтобы получить центр, как это:

struct CenterFinderVisitor : public osg::ShapeVisitor { 
    Vec3 center; 
    virtual void apply (Sphere &s) { center = s.getCenter(); } 
    virtual void apply (Box &b){ center = b.getCenter(); } 
    // ...and so on for other shapes 
}; 

Теперь вы можете реализовать метод getCenter() следующим :

class MyClass { 
private: 
    osg::Shape *shape; 
public: 
    MyClass(string _type) { 
     // This is for example purposes. Eventually types & mappings will be discovered at run-time. 

     if (_type == "FOO") { 
      shape = new osg::Box(); 
     } else if (_type == "BAR") { 
      shape = new osg::Sphere(); 
     } 
    } 
    Vec3 getShapeCenter() { 
     CenterFinderVisitor cf; 
     shape->accept(cf); 
     return cf.center; 
    } 
}; 

Если вы не знакомы с шаблоном посетителей, read this article on wikipedia.

+0

Спасибо @dasblinkenlight! Это похоже на то, что мне нужно. Я дам ему выстрел завтра утром, когда это не 3 часа ночи. :) – user3077634

+0

на самом деле - вопрос о том, может ли CenterFinderVisitor использовать указатели функций (или некоторые другие средства), чтобы быть полностью универсальными, вроде как «сквозные» функции? например поэтому, если бы я хотел сказать 'cf.anything();', он попытался бы выполнить 's.anything();' или 'b.anything();'? Это позволило бы мне создать только один производный класс посетителя, а не получать новый класс посетителей каждый раз, когда я хочу реализовать другую функцию ... – user3077634

+0

@ user3077634 Я не думаю, что вы могли бы это сделать - даже с шаблонами. Возможно, вы могли бы сделать это с помощью макроса, но это было бы сложно поддерживать. Попробуйте это, и дайте мне знать, как это работает. – dasblinkenlight

0

Это классический вопрос ООП.

Иметь shape базовый класс и иметь все формы, наследуемые от него. В форме объявить все функции (чисто виртуальные или просто виртуальные), которые вы хотите форму, чтобы:

class shape { 
public: 
    shape(string _name) : name(_name) {} 
    virtual ~shape(); // virtual desructor 
    virtual POINT getCenter() = NULL; 
    virtual getName() { return name; } // example - functionality in base class 

protected: 
    string name; 
}; 

class rectangle : public shape { 
{ 
    rectangle() : shape("rectangle") {} 
    virtual POINT getCenter() { return /* do math here :) */ } 
}; 

В классе MyClass, есть указатель/реф к shape типа.

+0

Я предполагаю, что это сработает, и у меня были определенные производные классы, определенные первоначально, но я пытаюсь отойти от этого, чтобы я мог сделать код более удобным для обслуживания/масштабируемым в долгосрочной перспективе. Например. что, если в итоге получится 10 разных типов элементов? Или 50? Или какое-то другое число, которое динамически определяется во время выполнения? – user3077634

+0

Это так же просто, как и с OO. Он также масштабируемый. Любые новые функциональные возможности могут быть введены в базовый класс и либо переопределены (base реализует виртуальную функцию), либо производные принудительно реализуются (чистые виртуальные в базовом классе). – egur

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