Can you create a std::map of inherited classes? - c++

I'm wondering if it's possible to create a map of pointers of inherited classes. Here's an example of what I'm trying to do:
#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;
}
The errors I get are:
main.cpp(23): error C2133: 'm' : unknown size
main.cpp(23): error C2998: 'std::map<std::string,myClass>m' : cannot be a template definition
I get why I'm getting this error, but I'm wondering if it's possible to create a map that can hold different levels of inherited classes? If not, is it possible to create some sort of management system that can hold various class types? Or would I have to make different maps/vectors/arrays/etc. for each type of class?

Yes you can store inherited classes in map, but pointers to them, not objects themselves. Here's a short example (it lacks memory management on pointers)
#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;
}

First things first:
template<class myClas>
map<string, myClass> m;
This is not valid C++ and could only mean something like a template alias, but I believe, that is not what you are looking for.
Storing polymorphic objects in C++ is complicated by slicing (constructing a value of the base type from a value of a derived type). Dynamic polymorphism can only be handled through references or pointers. You could potentially use std::ref or boost::ref for situations in which the map will only be passed down the callstack, but this requires some care. Often, storing pointers to the base is the way to go: std::map<std::string, base*>. Managing deallocation yourself is rather tedious and either std::map<std::string, std::unique_ptr> or std::map<std::string, std::shared_ptr> are preferred, depending if you need shared semantics or not.
Basic example. Someone should replace this with something more meaningful.
#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;
}

You may have template on classes and functions, but not on instances.
You should stick to the map to BaseClass*'es.

Below is the expansion of solution suggested by anton.
#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;
}

Related

Creating an array of pointers of classes in an hierarchy

I have this hierarchy of classes :
B1 B2
\ /
C
How should I make an array of pointers in C++ , in which I can store objects from all the classes?
This is what I tried and is not working:
#include <iostream>
using namespace std;
class MyClass {
public:
void myFunction() {
cout << "Some content in parent class." ;
}
};
class MyOtherClass {
public:
void myOtherFunction() {
cout << "Some content in another class." ;
}
};
class MyChildClass: public MyClass, public MyOtherClass {
};
int main() {
MyChildClass **v=new MyChildClass*[3];
v[1]=new MyClass();
v[2]=new MyOtherClass();
v[1]->myFunction();
return 0;
}
First of all you have to understand, that you can store only pointer to child into pointer to parent and only in that order. Secondly, You may want to use std::variant, so your code will looks like this
#include <iostream>
#include <variant>
using namespace std;
class MyClass {
public:
void myFunction() {
cout << "Some content in parent class." ;
}
};
class MyOtherClass {
public:
void myOtherFunction() {
cout << "Some content in another class." ;
}
};
class MyChildClass: public MyClass, public MyOtherClass {
};
int main() {
std::variant<MyClass*, MyOtherClass*> v[3];
v[1]=new MyClass();
v[2]=new MyOtherClass();
std::get<MyClass*>(v[1])->myFunction();
std::get<MyOtherClass*>(v[2])->myOtherFunction();
return 0;
}

Linkedlist contain differnt classes of object in cpp

How we can create a linked list which can contain a different class of object. for example, if a person is base class and it has student and teacher as a derived class so How I can create a linked list which can contain both classes of objects like student also and teacher also.
LinkedList example for understanding my problem:
head->studentobject->teacherobject->studentobject->teacherobject... so on
Try it here. A std::list is a linked list . .
#include <list>
#include <iostream>
#include <memory>
using namespace std;
class Person {
public:
virtual ~Person() = default;
virtual void talk()=0;
};
class Student : public Person {
public:
void talk() {
cout << "Teach me!\n";
}
};
class Teacher : public Person {
public:
void talk() {
cout << "Listen!\n";
}
};
int main() {
list<std::unique_ptr<Person>> people;
people.push_back(unique_ptr<Person>(new Student()));
people.push_back(unique_ptr<Person>(new Teacher()));
for (auto& p : people) {
p->talk();
}
return 0;
}
Usually you would create a base class and derived classes:
#include <iostream>
#include <list>
#include <memory>
class Base {
public:
virtual ~Base() = default;
virtual void printClass() = 0;
};
class Student : public Base {
public:
virtual void printClass() { std::cout << "Student\n"; }
};
class Teacher : public Base {
public:
virtual void printClass() { std::cout << "Teacher\n"; }
};
class Node {
public:
void setTeacher() {
data = std::make_unique<Teacher>();
}
void setStudent() {
data = std::make_unique<Student>();
}
void printClass() { data->printClass(); }
private:
std::unique_ptr<Base> data;
};
int main() {
std::list<Node> l;
l.push_back({});
l.front().setTeacher();
l.front().printClass();
}
Use some kind of pointer to store the references. You can use raw pointers, references, smart pointers, ...
Here you can read when to use virtual destructors

Threading and Polymorphism

#include<iostream>
#include <thread>
#include <algorithm>
#include <vector>
#include <functional>
using namespace std;
class base
{
public:
virtual void fun_1() { cout << "base-1\n"; }
virtual void fun_2() { cout << "base-2\n"; }
};
class derived : public base
{
public:
void fun_1() { cout << "derived-1\n"; }
void fun_2() { cout << "derived-2\n";
}
};
class caller
{
private:
base *p;
derived obj1;
p = &obj1;
public:
void me()
{
std::thread t(std::bind(&base::fun_2, p), this);
t.join();
}
};
int main()
{
caller c;
c.me();
return 0;
}
I have a written a very simple threading and polymorphism example. All I wanted to do is to call a derived function from a different class which is containing that another class object. The program fails to compile with the message p does not name a type which I could not understand why.
Your error is in line:
p = &obj1;
It is better to write like this, it should help:
class caller
{
private:
derived obj1;
base *p = &obj1;
......
};
or initialize pointer in costructor:
class caller
{
private:
derived obj1;
base *p = nullptr;
caller() : p(&obj1) {}
......
};

Alternative to virtual variables

I have two classes, Base and Derived. Derived inherits from Base and additionally includes several functions and variables -- therefore the need to have two separate classes. However, they do share one function, run.
In the example below I have to pass an argument to run in order to execute read - this argument depends on which class the object refers to. Is it possible to write a generic version of read such that the program automatically uses vars_Base or vars_Derived depending on which object calls run?
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
class Base
{
protected:
void read(std::vector<std::string>);
public:
void run(std::vector<std::string> vars) { read(vars); }
std::vector<std::string> vars_Base;
};
void Base::read(std::vector<std::string> int_vars)
{
for (auto int_vars_it : int_vars)
{
std::cout << int_vars_it << "\n";
}
}
class Derived : public Base
{
protected:
public:
std::vector<std::string> vars_Derived;
///Here are other functions only known to Derived, not Base
};
int main()
{
Base b;
b.vars_Base.push_back("aB");
b.vars_Base.push_back("bB");
b.vars_Base.push_back("cB");
b.run(b.vars_Base);
Derived d;
d.vars_Derived.push_back("aD");
d.vars_Derived.push_back("bD");
d.vars_Derived.push_back("cD");
d.run(d.vars_Derived);
return 0;
}
Is that the result you want to get?
class Base
{
protected:
using vars_type = std::vector<std::string>;
private:
vars_type vars_Base;
protected:
virtual vars_type& get_vars() {
return vars_Base;
}
public:
void push_back(const std::string& str) {
get_vars().push_back(str);
}
void run()
{
for (auto int_vars_it : get_vars()) {
std::cout << int_vars_it << " ";
}
}
};
class Derived : public Base
{
private:
vars_type vars_Derived;
protected:
vars_type& get_vars() override {
return vars_Derived;
}
public:
///Here are other functions only known to Derived, not Base
};
int main(int argc, char* argv[])
{
Base b;
b.push_back("aB");
b.push_back("bB");
b.push_back("cB");
b.run(); // prints aB bB cB
std::cout << std::endl;
Derived d;
d.push_back("aD");
d.push_back("bD");
d.push_back("cD");
d.run(); // prints aD bD cD
return 0;
}
If so then the explanation is next: there is no such thing as a "virtual variable", but there are virtual functions. You can use virtual functions as "internal accessors" for your member variables. Although, Derived class contains both vars_Base and vars_Derived, get_vars() lets you override access to appropriate instance of vars.
Hope you'll find it helpful.
You can make use of virtual functions to solve your problem, without the use for generics.
Here is a solution using virtual functions (applied to your sample code) :
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
class Base
{
protected:
void read(std::vector<std::string>);
public:
virtual void run() { read(vars_Base); }
std::vector<std::string> vars_Base;
};
void Base::read(std::vector<std::string> int_vars)
{
for (auto int_vars_it : int_vars)
{
std::cout << int_vars_it << "\n";
}
}
class Derived : public Base
{
protected:
public:
void run() override { read(vars_Derived); }
std::vector<std::string> vars_Derived;
///Here are other functions only known to Derived, not Base
};
int main()
{
Base b;
b.vars_Base.push_back("aB");
b.vars_Base.push_back("bB");
b.vars_Base.push_back("cB");
b.run();
Derived d;
d.vars_Derived.push_back("aD");
d.vars_Derived.push_back("bD");
d.vars_Derived.push_back("cD");
d.run();
return 0;
}
This solution allows to encapsulate vars_Base and vars_Derived inside the classes.
Hope this helps.

How to create an array of classes in c++?

Code:
struct Base { ... };
struct A : public Base { ... };
struct B : public Base { ... };
struct C : public Base { ... };
Is it possible to create an array, that holds that types of struct?
sample/expected result:
Type inheritedTypesOfStruct[3] = {A, B, C};
The purpose of this is that I later want to create an object with a random class retrieved from the array.
You could create an array of functions, each of which returns a base pointer(or smart pointer) that each point to objects of your various derived classes. e.g.
typedef std::unique_ptr<Base> base_ptr;
template<typename Derived>
base_ptr CreateObject()
{
return base_ptr(new Derived);
}
int main()
{
std::function<base_ptr(void)> f[3] = {
CreateObject<A>, CreateObject<B>, CreateObject<C>
};
base_ptr arr[10];
for (int i=0; i<10; ++i)
arr[i] = f[rand()%3]();
}
Here it is in action: http://ideone.com/dg4uq
If your compiler supports RTTI, you can do something like:
const type_info *inheritedTypesOfStruct[3] = {
&typeid(A), &typeid(B), &typeid(C)
};
However, you won't be able to instantiate a class using only its type_info. The factory pattern might be a better answer to your root problem.
Update: Since type_info instances cannot be copied (their copy constructor and assignment operator are private), and arrays of references are illegal, constant pointers have to be used in the example above.
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <map>
#include <vector>
#include <memory>
using namespace std;
// interface
class Base
{
public:
virtual ~Base() { }
virtual int getClassId() = 0;
};
// class A relizes interface Base, has ID == 1 (is used in automatic registration to factory)
class A : public Base
{
public:
const static int ID = 1;
static Base* CreateInstance()
{
return new A();
}
virtual int getClassId()
{
return ID;
}
virtual ~A() { }
};
// class B relizes interface Base, has ID == 2 (is used in automatic registration to factory)
class B : public Base
{
public:
const static int ID = 2;
static Base* CreateInstance()
{
return new B();
}
virtual int getClassId()
{
return ID;
}
virtual ~B() { }
};
// this is the objects factory, with registration only (unregister s not allowed)
class ObjectFactory
{
ObjectFactory() { }
ObjectFactory(ObjectFactory&) { }
public:
virtual ~ObjectFactory() { }
static ObjectFactory& instance()
{
static ObjectFactory objectFactory;
return objectFactory;
}
typedef Base* (*Creator) ();
void registerCreator(int id, Creator creator)
{
registry[id] = creator;
}
Base* CreateById(int id)
{
return registry[id]();
}
private:
map<int, Creator> registry;
};
// this template class is used for automatic registration of object's creators
template <class T>
struct RegisterToFactory
{
RegisterToFactory(ObjectFactory& factory)
{
factory.registerCreator(T::ID, &T::CreateInstance);
}
};
namespace
{
// automaticaly register creators for each class
RegisterToFactory<A> autoregisterACreator(ObjectFactory::instance());
RegisterToFactory<B> autoregisterBCreator(ObjectFactory::instance());
}
// lets this this solution
int main(int argc, char *argv[])
{
vector<int> ids;
ids.push_back(static_cast<int>(A::ID));
ids.push_back(static_cast<int>(B::ID));
srand(time(0));
for (int i = 0; i < 20; ++i)
{
int randomClasssId = ids[rand() % ids.size()];
auto_ptr<Base> testObject(ObjectFactory::instance().CreateById(randomClasssId));
cout << "Object of classId = " << testObject->getClassId() << " has been produced by factory." << endl;
}
system("PAUSE");
return EXIT_SUCCESS;
}
I don't get the question. Are you asking for an array that can hold different type of instances at the same time? That is possible using polymorphism, of course. Or are you trying to get an array of types (like reflection)? That would be possible using RTTI or Qt type information (as an example), but I never did that.
You can take a look here: http://www.java2s.com/Code/Cpp/Class/Objectarraypolymorphism.htm
on how to use Polymorphism in C++.