2013-11-06 2 views
1

Я пишу библиотеку, и в ней у меня есть класс BaseClass. Любой, кто использует библиотеку, будет создавать свои собственные классы, которые наследуются от BaseClass. У меня есть еще один класс, позвоните ему Manager, который содержит вектор указателей BaseClass, который может содержать любой объект, полученный из BaseClass.C++ шаблоны с производными классами

Класс Manager ДОЛЖЕН обрабатывать создание и уничтожение любого объекта, добавленного в его вектор BaseClass. Это связано с тем, что любой объект в векторе можно удалить в любой момент, а сам Менеджер также можно удалить. Из-за этого пользователь библиотеки не может добавить объект в вектор baseClass Manager, передав ему указатель на уже существующий объект, полученный из BaseClass. На самом деле, я мог позволить пользователю сделать это. Но это будет связано с копированием фиктивного объекта, который я бы предпочел не делать.

Чтобы решить эту проблему, я пытаюсь использовать функцию шаблона. Пользователь должен передать тип объекта, полученного из BaseClass, при попытке добавить его в вектор Менеджера. Это то, что я сейчас имею.

//Manager.h 
#include <vector> 
#include "BaseClass.h" 
#include <typeinfo> 

class Manager { 
    //Vector holding pointers to any objects inherited from BaseClass 
    vector<BaseClass*> baseClasses; 

    //Template function that needs to add NEW object to baseClass vector 
    //This I'm having problems with 
    template<class T> 
    BaseClass* Add() { 
     BaseClass* baseClass = new T(); 
     baseClasses.push_back(baseClass); 
     return baseClass; 
    } 

    //Template function that gets object from baseClass vector 
    //This works fine 
    template<class T> 
    BaseClass* Get() { 
     for (int i = 0; i < baseClasses.size(); i++) { 
      if (typeid(*baseClasses[i]) == typeid(T)) { 
       return baseClasses[i]; 
      } 
     } 
     return NULL; 
    } 
}; 

Например, пользователь должен делать это при добавлении или получении объекта из базового кластера менеджера. DerivedClass происходит от BaseClass

Manager manager; 
//Add a new DerivedClass object to Manager's vector 
manager.Add<DerivedClass>(); 
//Get a pointer to the DerivedClass object that was just added 
DerivedClass* derivedClass = (DerivedClass*)manager.Get<DerivedClass>(); 

Функция Get() отлично работает. Мне нужно знать, как я могу заставить свою функцию Add() работать? Помощь будет принята с благодарностью.

+1

Исправить код, чтобы отразить то, что является членом типа, а что нет. Вы также должны попытаться объяснить еще немного, почему вы думаете, что менеджер должен создавать объекты, и что именно вы подразумеваете под * любым объектом в векторе, может быть удален в любое время *, кто будет делать удаления? - что это не работает с вашей функцией «Добавить»? –

+0

Какие проблемы у вас есть с функцией 'Добавить'? Из вашего вопроса, я понимаю, что это не сработает, но я хотел бы получить более подробную информацию, например, не скомпилировать/увязать? Это вызывает ошибку сегментации? Возвращает ли оно неправильное значение? – anatolyg

+0

Если у вас есть указатель на базовый класс, а базовый класс имеет виртуальный деструктор, можно безопасно удалить объект с помощью указателя. –

ответ

5

Есть довольно много вещей, нечетких о вашем проекте, но если вопрос, можно ли применять, что тип T в функции члена Templa Add может быть обеспечен, чтобы вывести из BaseClass, варианты просты:

  • ничего не делать, то компилятор с удовольствием жалуется на линии BaseClass* baseClass = new T();
  • добавить статические проверки, чтобы сделать это более очевидный
  • использование фантазией трюки SFINAE, чтобы удалить функцию из набора перегрузок

Я бы выбрал либо один из первых двух. Статическая утверждают, может быть написано как:

static_assert(std::is_base_of<BaseClass,T>::value); 

SFINAE трюк, я действительно избежать этого, как он собирается сделать код более запутанным, но может быть реализован как:

template <class T> 
typename std::enable_if<std::is_base_of<BaseClass,T>::value,T*>::type 
Add() { 
    baseClasses.push_back(new T()); 
    return baseClasses.back(); 
} 

(Обратите внимание, что я изменил тип возвращаемого значения на T*, объект T, зачем возвращать BaseClass*? То же самое относится к функции Get, нет смысла возвращать BaseClass*, когда вы знаете, что объект действительно T)

Теперь актуальная проблема намного сложнее, так как ваш дизайн активно избегает соображений собственности, чего вам не следует. Подумайте о владении объектами и убедитесь, что есть четкий владелец (или общий ресурс). Как только вы знаете, кому принадлежат объекты, создайте протокол для остальной части кода, чтобы уведомить владельца, когда объект нужно удалить. Если вы разрешите код, чтобы удалить указатели, которые вы держите, вы скоро столкнетесь с неопределенным поведением.

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

Использование typeid, как правило, является кодовым запахом, и я не думаю, что это исключение. Возможно, вам будет лучше, если вы спроектируете иерархию типов, а не запускаете typeid. Если вы действительно настроены сделать интерфейс на основе типов, тогда подумайте, может ли быть dynamic_cast. Он будет более эффективным, но если у вас есть несколько уровней наследования, он позволит вам возвращать наиболее производные объекты в качестве промежуточных объектов.

+0

Я использую метод static_assert, а новый T() создает объекты типа BaseClass вместо производного класса. Вы знаете, почему это так? И я знал, что мой выбор дизайна будет поставлен под сомнение. Но на самом деле менеджер (или GameObject) владеет объектами. Но любой объект может сказать менеджеру удалить их –

+0

Хорошо, теперь он работает. Большое спасибо! –

+0

@JanzDott: Существует большая разница между заявлением о том, что любой может удалить объект и заявить, что любой может попросить менеджера удалить объект. –

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