Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the C++ equivalent of java.lang.Object x = new Foo()?

What is the C++ equivalent of java.lang.Object x = new Foo()?

like image 966
fredoverflow Avatar asked Nov 20 '10 14:11

fredoverflow


1 Answers

There is no equivalent of this in C++ and it would be pointless to attempt to program Java in C++. That being said, I will approach this from a perspective of attempting to mimic as much of the assignment characteristics and spirit of the statement as possible. Each way I will suggest has downsides and limitations. The first two are not truly idiomatic C++ but it's important to know about them to see what problems the last two solved.

1. C-style void pointers.

Let me start with the most basic and least useful, a void pointer:

void* foo = new Foo();

Anything can be assigned to a void pointer from the new operator as new, placement new and the like always return a void pointer. The downsides should be obvious: loss of type information about the object pointed at. For one, C++ lacks reflection or any means of interrogating the object. You'd have to keep the type information in your head and use casting back and forth to actually use it. Since there's no type-safe way to cast from a void pointer, hilarity could ensue.

If this were a return type from a function:

void* foo = some_function( _arg0 );

any author using your code would need to figure out what should happen. Unfortunately, often times what they thing should happen and what you, the author, think should be returned from a function are very different.

2. C-style Unions

If you want to restrict yourself to N types which are supported instead of infinite types that java.lang.Object can handle then there are unions. These can hold a set of pre-defined value types on the same memory space as long as they are POD datatypes. Unions lack two very important things: the ability to know which value was assigned and the ability to hold non-POD types. This completely rules them out for use with any object with any sort of functionality such as std::string.

To clarify what the above actually means:

union myType{
    int a;
    char b[4];
};

If I set the first char within the "b" portion of an instance of "myType" then I also am setting the first byte of the int to that same value. In C++ these are really only useful for memory hacks and extremely low level programming (think embedded and the like.) They are not idiomatic C++.

3. Boost::Any

Now, if you truly want a "I can hold anything" then use a Boost::Any. This can hold any object without destroying a lot of type information which is so useful. The Boost documents state better than I in their purpose. Taken from the introduction section of Any:

There are times when a generic (in the sense of general as opposed to template-based programming) type is needed: variables that are truly variable, accommodating values of many other more specific types rather than C++'s normal strict and static types.

Think of Any solving many of the problems associated with a void pointer, such as loss of information about the contained object and the ability to safely cast to proper types.

4. Boost::Variant

Boost::Variant solves the same type of problem of that of a union without losing object information. Moreover it can be used with non-POD type objects. As the documentation states it best:

Typical solutions feature the dynamic-allocation of objects, which are subsequently manipulated through a common base type (often a virtual base class Hen01 or, more dangerously, a void*). Objects of concrete type may be then retrieved by way of a polymorphic downcast construct (e.g., dynamic_cast, boost::any_cast, etc.).

However, solutions of this sort are highly error-prone, due to the following:

  1. Downcast errors cannot be detected at compile-time. Thus, incorrect usage of downcast constructs will lead to bugs detectable only at run-time.
  2. Addition of new concrete types may be ignored. If a new concrete type is added to the hierarchy, existing downcast code will continue to work as-is, wholly ignoring the new type. Consequently, the programmer must manually locate and modify code at numerous locations, which often results in run-time errors that are difficult to find.

Edit:

Reorganized to show the what and the why of my thoughts when I answered the OP. I've also addressed comments below.

like image 102
wheaties Avatar answered Nov 15 '22 10:11

wheaties