Можете ли вы создать std :: map унаследованных классов? - PullRequest
3 голосов
/ 25 декабря 2011

Мне интересно, возможно ли создать карту указателей унаследованных классов. Вот пример того, что я пытаюсь сделать:

#include <string>
#include <map>

using namespace std;

class BaseClass
{
    string s;
};

class Derived1 : public BaseClass
{
    int i;
};

class Derived2 : public Derived1
{
    float f;
};

// Here's what I was trying, but isn't working
template<class myClass>
map<string, myClass>m;

int main()
{
    // Add BaseClasses, Derived1's, and/or Derived2's to m here
    return 0;
}

Я получаю следующие ошибки:

main.cpp(23): error C2133: 'm' : unknown size
main.cpp(23): error C2998: 'std::map<std::string,myClass>m' : cannot be a template definition

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

Ответы [ 4 ]

3 голосов
/ 25 декабря 2011

Да, вы можете хранить унаследованные классы на карте, но указатели на них, а не сами объекты.Вот короткий пример (ему не хватает управления памятью на указателях)

#include <iostream>
#include <string>
#include <map>
#include <utility>

using namespace std;

class BaseClass
{
    string s;
public: 
    BaseClass() { s = "BaseClass";} 
    virtual void print() 
    {
        cout << s << std::endl;
    }
};

class Derived1 : public BaseClass
{
    int i;
public:
    Derived1() { i = 10; }
    void print() 
    {
        cout << i << std::endl;
    }

};

class Derived2 : public Derived1
{
    float f;
public:
    Derived2() { f = 4.3;}
    void print() 
    {
        cout << f << std::endl;
    }
};

int main()
{
    map<string, BaseClass*>m;
    m.insert(make_pair("base", new BaseClass()));
    m.insert(make_pair("d1", new Derived1()));
    m.insert(make_pair("d2", new Derived2()));
    m["base"]->print();
    m["d1"]->print();
    m["d2"]->print();

    return 0;
}
2 голосов
/ 25 декабря 2011

Перво-наперво:

template<class myClas>
map<string, myClass> m;

Это недопустимый C ++ и может означать только что-то вроде template alias, но я считаю, что это не то, что вы ищете.

Хранение полиморфных объектов в C ++ усложняется срезанием (построение значения базового типа из значения производного типа). Динамический полиморфизм может быть обработан только с помощью ссылок или указателей. Вы можете потенциально использовать std::ref или boost::ref для ситуаций, в которых карта будет передаваться только по стеку вызовов, но это требует некоторой осторожности. Зачастую хранение указателей на базе - это путь: std::map<std::string, base*>. Самостоятельное управление освобождением является довольно утомительным занятием, и std::map<std::string, std::unique_ptr> или std::map<std::string, std::shared_ptr> предпочтительнее, в зависимости от того, нужна ли вам общая семантика или нет.

Базовый пример. Кто-то должен заменить это чем-то более значимым.

#include <memory>
#include <string>
#include <map>
#include <iostream>

class animal
{
public:
  virtual ~animal() {};
  virtual void make_sound() const = 0;
};

class dog : public animal
{
public:
  void make_sound() const { std::cout << "bark" << std::endl; }
};
class bird : public animal
{
public:
  void make_sound() const { std::cout << "chirp" << std::endl; }
};

int main()
{
  std::map<std::string, std::unique_ptr<animal>> m;
  m.insert(std::make_pair("stupid_dog_name", new dog));
  m.insert(std::make_pair("stupid_bird_name", new bird));
  m["stupid_dog_name"]->make_sound();
  return 0;
}
1 голос
/ 16 июня 2016

Ниже приведено расширение решения, предложенное Антоном.

#include <iostream>
#include <string>
#include <map>
#include <utility>

using namespace std;

class BaseClass
{
    string s;

public: 
    BaseClass() { s = "BaseClass";} 
virtual ~ BaseClass(){}
virtual void print()=0;
};


class Derived1 : public BaseClass
{
    int i;
public:
    Derived1() { i = 10; }
    void print() 
    {
        cout << i << std::endl;
    }

};

class Derived2 : public Derived1
{
    float f;
public:
    Derived2() { f = 4.3;}
    void print() 
    {
        cout << f << std::endl;
    }
};

class factory
{
map<string, BaseClass*>m;
BaseClass* obj;
public:
factory()
{
obj=NULL;
}
BaseClass* FindType(string s);
void AddType(string s,BaseClass *obj);
void deleter();
~factory(){cout<<"deleting objects from map"<<endl;
deleter();
}
};
void factory :: AddType(string s,BaseClass* obj)
{ 
 m.insert(make_pair(s,obj ));
}
void factory ::deleter ()
 {

   for (auto  pObj = m.begin( );
        pObj != m.end( ); ++pObj) {

      delete pObj->second;
   }

   m.clear( );  

 }
BaseClass* factory::FindType(string s)
{
if(m.find(s)!=m.end())
{

return  m[s];
}

return NULL;
}

int main()
{   
   BaseClass* obj;
   factory fact_obj; 
   fact_obj.AddType("d1",new Derived1());
   fact_obj.AddType("d2",new Derived2());
   obj=fact_obj.FindType("d1");
   if(obj!=NULL)
      {
    obj->print();
      }
    obj=fact_obj.FindType("d2");
    if(obj!=NULL)
     {
     obj->print(); 
     }

    return 0;
}
1 голос
/ 25 декабря 2011

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

Вы должны придерживаться карты BaseClass* es.

...