2013-10-27 5 views
4

Вступительное примечание: Я начал изучать C++ в колледже около месяца назад. Это для задания. Мы смотрим сейчас и не понимаем многие передовые концепции.Как передать класс (не объект) в качестве параметра в C++

tl; dr: давайте представим, что у вас есть Book. Book - динамический массив Pages*. Каждый Page может быть WrittenPage или DrawnPage. Если вы хотите распечатать все Pages, вы используете virtual method. Если вы просто хотите напечатать DrawnPages или WrittenPages, вам нужно будет сделать какую-то фильтрацию внутри Book. Как это сделать? На данный момент я обнаружил, что вам понадобится typeid или какое-то другое средство для сравнения subtype каждого Page. Если вы спешите что-то быстро и просто взглянуть на принятый ответ, @CantChooseUsernames. Это хорошо работает для моей проблемы. Если у вас есть еще какой-то опыт, я бы хотел услышать, что вы думаете о новом ответе на @ n.m. , Не допускайте, чтобы принятый в настоящее время ответ препятствовал вам комментировать или публиковать свои собственные, если вы считаете, что он привносит что-то новое и содержательное в обсуждение.


ORIGINAL ВОПРОС:

У меня есть класс MyObj это суперкласс TheseObj и ThoseObj.

Class TheseObj : public MyObj { 
} 

Class ThoseObj : public MyObj { 
} 

У меня есть еще один класс, который содержит зЬй :: вектор с указателями на экземпляры MyObj и не-статический метод, в котором я хочу перечислить только TheseObj:

Class MyClass { 
    private: 
    vector<MyObj*> vec; 

    public: 
    void listTheseObj() { 
     for each (myObj* obj in vec) { 
      if(typeid(*obj) == typeid(theseObj)) { 
       cout << *obj << endl; 
      } 
     } 
    } 
} 

Все операторы уже правильно перегружен.

Это прекрасно работает. Теперь проблема в том, что у меня гораздо больше мест, где мне нужно делать то же самое, поэтому мне нужен шаблонный метод, который может получить вектор GENERIC и класс TYPE, чтобы я мог сделать что-то вроде:

listObjects(class_name_here, vec); 

мне удалось создать:

template <class T> 
void listObjectsOfOneType(const type_info& class_name_here, const vector<T*>& vec) { 
    for each (T* obj in vec) { 
     if(typeid(*obj) == typeid(class_name_here)) { 
      cout << *obj << endl; 
     } 
    } 
} 

Но я не уверен:

  1. Если шаблонный метод является правильным
  2. Как я могу назвать это

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

+7

«Это отлично работает» - возможно, для очень маленьких величин. Вы должны использовать виртуальные функции для запуска кода подтипа. 'typeid' в принципе никогда не подходит. –

+0

Как насчет публикации чего-то более сложного? Если бы вы могли сделать несколько примеров, это было бы здорово. –

+0

Я не знаю, какова ваша настоящая проблема. Действительно ли это перечисление объектов определенного типа или что-то еще? –

ответ

4

Я бы, вероятно, избегал использования TypeID ..Хотя, я не уверен, что именно вы хотите достичь, но это то, что я верю, что вы просите:

#include <iostream> 
#include <vector> 
#include <typeinfo> 

template <class T, class U> 
void ListObjects(std::vector<U*> &vec) 
{ 
    for (U* obj : vec) 
    { 
     if (typeid(*obj) == typeid(T)) 
     { 
      obj->Print(); 
      std::cout<<"\n"; 
     } 
    } 
} 

class Parent 
{ 
    public: 
     Parent() {std::cout<<"Parent Constructed\n";} 
     virtual ~Parent() {std::cout<<"Parent Destructed\n";} 

     virtual void Print(){std::cout<<"Parent\n";} 
}; 

class Brother : public Parent 
{ 
    public: 
     Brother(){std::cout<<"Brother Constructed\n";} 
     virtual ~Brother(){std::cout<<"Brother Destructed\n";} 
     void Print() override {std::cout<<"Brother\n";} 
}; 

class Sister : public Parent 
{ 
    public: 
     Sister(){std::cout<<"Sister Constructed\n";} 
     virtual ~Sister(){std::cout<<"Sister Destructed\n";} 
     void Print() override {std::cout<<"Sister\n";} 
}; 

int main() 
{ 
    std::vector<Parent*> Objects; 
    Objects.push_back(new Parent()); 
    Objects.push_back(new Brother()); 
    Objects.push_back(new Sister()); 
    std::cout<<"\n"; 

    ListObjects<Parent>(Objects); 
    ListObjects<Brother>(Objects); 
    ListObjects<Sister>(Objects); 

    for (Parent* c : Objects) 
    { 
     delete c; 
    } 
} 

который печатает:

  • Родитель Изготовленные
  • Родитель Изготовленные
  • Brother Constructed
  • Родительская строительная компания
  • Сестра построена

  • Родитель

  • брат
  • сестра

  • Родитель уничтоженного

  • брат уничтоженного
  • Родитель уничтоженного
  • сестра уничтоженного
  • Родитель уничтоженного

  • Процесс возвращается 0 (0x0) время выполнения: 0.066 сек

  • Нажмите любую клавишу для продолжения.

Много комментариев говорят вам использовать не использовать TypeID, потому что мы не уверены в том, что вы хотите .. Но то, что мы имеем в виду «нет необходимости TypeID» при условии, что мы знаем, что вы хотите, то следующий будет действительным:

#include <iostream> 
#include <vector> 
#include <typeinfo> 

template <class T> 
void ListObjects(std::vector<T*> &vec) 
{ 
    for (T* obj : vec) 
    { 
     //TypeID isn't needed here because the virtual call will figure out which class's << operator to call. 

     //If each class has a print function, it can also figure out which class's print function to call.. 
     //obj->Print(); //works too because each class has a print func. 
     std::cout<<*obj<<"\n"; //Works because each class has an overloaded << operator. 
    } 
} 

class Parent 
{ 
    protected: 
     virtual void Print(std::ostream& os) const {os<<"Parent\n";} 

    public: 
     Parent() {std::cout<<"Parent Constructed\n";} 
     virtual ~Parent() {std::cout<<"Parent Destructed\n";} 

     friend std::ostream& operator << (std::ostream &os, const Parent &p); 
}; 

std::ostream& operator << (std::ostream &os, const Parent &p) 
{ 
    p.Print(os); 
    return os; 
} 

class Brother : public Parent 
{ 
    protected: 
     void Print(std::ostream& os) const override {os<<"Brother\n";} 

    public: 
     Brother(){std::cout<<"Brother Constructed\n";} 

     virtual ~Brother() {std::cout<<"Brother Destructed\n";} 
}; 

class Sister : public Parent 
{ 
    protected: 
     void Print(std::ostream& os) const override {os<<"Sister\n";} 

    public: 
     Sister(){std::cout<<"Sister Constructed\n";} 
     virtual ~Sister(){std::cout<<"Sister Destructed\n";} 
}; 

int main() 
{ 
    std::vector<Parent*> Objects; 
    Objects.push_back(new Parent()); 
    Objects.push_back(new Brother()); 
    Objects.push_back(new Sister()); 
    std::cout<<"\n"; 

    ListObjects(Objects); //NOTICE we all template types are now inferred. 

    for (Parent* c : Objects) 
    { 
     delete c; 
    } 
} 

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


Теперь, так как вы просили предыдущий код шаблона является параметром вместо, то:

template <class T, class U> 
void ListObjects(std::vector<U*> &vec) 
{ 
    for (U* obj : vec) 
    { 
     if (typeid(*obj) == typeid(T)) 
     { 
      obj->Print(); 
      std::cout<<"\n"; 
     } 
    } 
} 

станет:

template<typename T> 
void ListObjects(std::vector<T*> &vec, const std::type_info &type) 
{ 
    for (T* obj : vec) 
    { 
     if (typeid(*obj) == type) 
     { 
      std::cout<<*obj<<"\n"; 
     } 
    } 
} 

и вы бы использовать его как: ListObjects(Objects, typeid(Child));

Опять же, все они дают точный результат. Все зависит от ваших потребностей/прецедентов. Мы точно не знаем, «чего» вы хотите достичь. Тем не менее, это должно помочь вам.

+0

Спасибо! Это почти то, что я ищу. Проблема в том, что вам нужно сделать что-то вроде [ListObjects <Родитель, Брат> (Объекты); ]. Таким образом, я вынужден ввести имя суперкласса внутри вывода diammond, что для меня не имеет смысла, поскольку я уже знаю, что вектор содержит элементы класса T. Любые альтернативы? –

+0

Привет, сэр. Я сейчас редактировал код. Теперь вам нужно только ввести: «ListObjects (Objects);» вместо «ListObjects» <«Родительский, дочерний» (объекты); '.Таким образом, он автоматически определяет (передает) другой тип через вектор. Все, что я сделал, это обмен параметрами шаблона. Я считаю, что так же хорошо, как и получается? Если у вас не было чего-то другого. В таком случае я не уверен, что вы имеете в виду? – Brandon

+0

Возможно, лучше также использовать виртуальные функции, а не выбирать этот способ. – phoad

2

Если вы не делаете это как часть теста, чтобы выяснить, что происходит где-то в вашем коде, я согласен с комментаторами в том, что это очень плохая идея.

template < typename T > 
void listObjects(const std::vector<MyObj*>& vec) { 
    for (MyObj* obj: vec) { 
     if (typeid(*obj) == typeid(T)) { 
      // one of the two below, depending on what your op<< looks like 
      std::cout << *obj << std::endl; 
      std::cout << dynamic_cast<T&>(*obj) << std::endl; 
     } 
    } 
} 

void calledLikeThis(const std::vector<MyObj*>& vec) { 
    listObjects<TheseObj>(vec); 
} 
+0

Спасибо за ваш ответ. Вы вынуждаете вектор MyObj *, который я не хочу. Метод должен получить вектор типа Шаблон. Мне удалось выполнить [listObjects (vec); ] Но этим способом я вынужден ввести имя суперкласса внутри вывода diammond, что для меня не имеет смысла, поскольку я уже знаю, что вектор содержит элементы класса T. –

+0

Возможно, вы это знаете, но компилятор не , Весь шаблон знает, что у вас есть тип X, и вы проверяете, является ли он также типом Y. У него нет оснований полагать, что это подкласс другого. В конце концов, отношения могут быть отменены или может быть задействовано множественное наследование. –

+0

Вектор четко обозначен как вектор vec; внутри мой класс. Конечно, все в порядке, но вектор такого типа, поэтому компилятор уже знает. Я создал простой шаблон метод, который получает vec и дает мне typeid (vec) .name. Прекрасно работает при вызове метода (vec); без вывода алмаза. –

0

Использование typeid таким образом нарушает принцип замещения Лисков.LSP, грубо говоря, говорит, что если ваша функция работает с объектами класса X, она также должна работать с (некоторыми) объектами любого подкласса X. Ваша функция listTheseObj будет отображать только объекты, которые точно типа TheseObj, но не любого подтипа.

Это нормально для целей отладки или для проектов инфраструктуры/каркаса, где вы реализуете сервисы, такие как отражение или сериализацию, и индексируете их с помощью typeid(obj). Но бизнес-логика не должна работать так. Пользователей не интересует какая-либо техническая причина, по которой вы разбиваете TheseObj на несколько подтипов; они хотят их понятие типа (если вообще).

Если вы хотите напечатать только TheseObjи любой объект подкласса типа, вы можете заменить

typeid(*obj) == typeid(TheseObj) 

с

dynamic_cast<TheseObj*>(obj) != 0 

шаблонного версия будет выглядеть следующим образом:

template<typename T, typename U> 
void ListObjects(std::vector<T*> &vec>) 
{ 
    for (T* obj : vec) 
    { 
     if (dynamic_cast<U*>(obj) != 0) 
     { 
      std::cout<<*obj<<"\n"; 
     } 
    } 
} 

Стоит отметить, что корпус if не использует это условие. Это указывает на возможность их разделить.

template<typename T> 
void ActOnObjects(std::vector<T*> &vec>, std::function<bool(T*)> predicate, 
             std::function<void(T*)> action) 
{ 
    for (T* obj : vec) 
    { 
     if (predicate(obj)) 
     { 
      action(obj); 
     } 
    } 
} 

Теперь вы можете фильтровать любой предикат с использованием RTTI или нет.

ActOnObjects(objects, [](T* obj){return dynamic_cast<ThatObj*>(obj) != 0;}, 
         [](T* obj){std::cout << *obj << std::endl;}); 

ActOnObjects(objects, [](T* obj){return obj->isVeryImportant();}, 
         [](T* obj){std::cout << *obj << std::endl;}); 

ActOnObjects(objects, [](T* obj){return obj->isBlue() && obj->isWobbly();}, 
         [](T* obj){std::cout << *obj << std::endl;}); 

Также, пожалуйста, используйте диапазоны итераторов вместо контейнеров, как любой хороший гражданин земли C++; Я оставляю это как упражнение.

+0

Что является преимуществом 'dynamic_cast (obj)! = 0' over' (typeid (* obj) == typeid (U)) '? Не желая создавать много дискуссий, я просто хочу сказать, что я читал, что 'typename' может быть менее эффективным, чем' class', в зависимости от контекста, но не наоборот. Об итераторах, единственное преимущество, которое я вижу в этом случае, заключается в том, что вы можете более легко изменить свой код в соответствии с другими контейнерами, правильно? Извините за то, что вы не обратились к остальной части своего ответа, но, честно говоря, я не понимаю эти понятия, мне нужно будет кое-что прочитать, прежде чем я смогу вернуться к вам с лучшим ответом. –

+0

Динамический литой будет работать над классом * и * всеми его производными классами, сравнение типов не будет. Предложение о typename неверно. Утверждение об итераторах является правильным (это может быть большим преимуществом). –

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