I'm trying to teach myself C++, and one of the traditional "new language" exercises I've always used is to implement some data structure, like a binary tree or a linked list. In Java, this was relatively simple: I could define some class Node that maintained an instance variable Object data
, so that someone could store any kind of object in every node of the list or tree. (Later I worked on modifying this using generics; that's not what this question is about.)
I can't find a similar, idiomatic C++ way of storing "any type of object." In C I'd use a void
pointer; the same thing works for C++, obviously, but then I run into problems when I construct an instance of std::string
and try to store it into the list/tree (something about an invalid cast from std::string&
to void*
). Is there such a way? Does C++ have an equivalent to Java's Object (or Objective-C's NSObject)?
Bonus question: If it doesn't, and I need to keep using void pointers, what's the "right" way to store a std::string
into a void*
? I stumbled upon static_cast<char*>(str.c_str())
, but that seems kind of verbose for what I'm trying to do. Is there a better way?
C++ does not have a base object that all objects inherit from, unlike Java. The usual approach for what you want to do would be to use templates. All the containers in the standard C++ library use this approach.
Unlike Java, C++ does not rely on polymorphism/inheritance to implement generic containers. In Java, all objects inherit from Object
, and so any class can be inserted into a container that takes an Object
. C++ templates, however, are compile time constructs that instruct the compiler to actually generate a different class for each type you use. So, for example, if you have:
template <typename T>
class MyContainer { ... };
You can then create a MyContainer
that takes std::string
objects, and another MyContainer that takes int
s.
MyContainer<std::string> stringContainer;
stringContainer.insert("Blah");
MyContainer<int> intContainer;
intContainer.insert(3342);
You can take a look at boost::any class. It is type safe, you can put it into standard collections and you don't need to link with any library, the class is implemented in header file.
It allows you to write code like this:
#include <list>
#include <boost/any.hpp>
typedef std::list<boost::any> collection_type;
void foo()
{
collection_type coll;
coll.push_back(boost::any(10));
coll.push_back(boost::any("test"));
coll.push_back(boost::any(1.1));
}
Full documentation is here: http://www.boost.org/doc/libs/1_40_0/doc/html/any.html
What you are looking for are templates. They allow you to make classes and function which allow you to take any datatype whatsoever.
Templates are the static way to do this. They behave like Java and C# generics but are 100% static (compile time). If you d'ont need to store different types of objetcs in the same container, use this (other answers describe this very well).
However, if you need to store different types of objects in the same container, you can do it the dynamic way, by storing pointers on a base class. Of course, you have to define your own objects hierarchy, since there is no such "Object" class in C++ :
#include <list>
class Animal {
public:
virtual ~Animal() {}
};
class Dog : public Animal {
public:
virtual ~Dog() {}
};
class Cat : public Animal {
public:
virtual ~Cat() {}
};
int main() {
std::list<Animal*> l;
l.push_back(new Dog);
l.push_back(new Cat);
for (std::list<Animal*>::iterator i = l.begin(); i!= l.end(); ++i)
delete *i;
l.clear();
return 0;
}
A smart pointer is easier to use. Example with boost::smart_ptr
:
std::list< boost::smart_ptr<Animal> > List;
List.push_back(boost::smart_ptr<Animal>(new Dog));
List.push_back(boost::smart_ptr<Animal>(new Cat));
List.clear(); // automatically call delete on each stored pointer
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With