Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maintain reference to any object type in C++?

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?

like image 926
Tim Avatar asked Oct 27 '09 18:10

Tim


4 Answers

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 ints.

MyContainer<std::string> stringContainer;
stringContainer.insert("Blah");

MyContainer<int> intContainer;
intContainer.insert(3342);
like image 92
Charles Salvia Avatar answered Nov 17 '22 02:11

Charles Salvia


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

like image 42
parti3an Avatar answered Nov 17 '22 03:11

parti3an


What you are looking for are templates. They allow you to make classes and function which allow you to take any datatype whatsoever.

like image 3
lhahne Avatar answered Nov 17 '22 04:11

lhahne


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
like image 2
KeatsPeeks Avatar answered Nov 17 '22 03:11

KeatsPeeks