8

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

fredoverflow
  • 256,549
  • 94
  • 388
  • 662

4 Answers4

22

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.

Community
  • 1
  • 1
wheaties
  • 35,646
  • 15
  • 94
  • 131
  • I think you need to check your syntax. – CB Bailey Nov 20 '10 at 14:15
  • 1
    Personally, I don't think this is a good enough match to the given java code to be described as "equivalent". In java you can safely cast from an `Object` to something else and recover if the cast was not valid. As you state, in C++ there is no way to dynamically cast from a `void*`. – CB Bailey Nov 20 '10 at 14:24
  • @Charles Bailey - Unfortunately nothing like java.lang.Object exists within C++. What I'm throwing out are the best things I know which have at least some similar properties. – wheaties Nov 20 '10 at 14:29
  • 1
    @Fred, @wheaties: I disagree with this completely. First of all, there is no C++ equivalent because not everything in C++ is an object. This needs to be stated if this is to be a FAQ. – John Dibling Nov 20 '10 at 14:33
  • 1
    Second, if you were to try to implement this functionality in standard C++, you'd start by implementing an abstract base class and derive off of that. that gives the most direct comparison to the asked question. but this wasnt even mentioned. – John Dibling Nov 20 '10 at 14:35
  • Third, the first thing mentioned is casting to `void*` which is not a generally accepted best practice. You lose all type information and do an end-around on the type system. You then say "try not to do this" but since this is the first thing mentioned, it will be taken as "this should be your first approach." but it shouldnt even be mentioned in an entry like this, at least not until some later "advanced techniques" section – John Dibling Nov 20 '10 at 14:37
  • 1
    @rodrigob: My point was that it is illegal to do that when the type of `your_pointer` is `void*`. – CB Bailey Nov 20 '10 at 14:37
  • @John Dibling definitely not a FAQ type answer. Please add your own answer. You've left a number of good comments and I've attempted to clarify some of the issues you raised above without taking all of the content. I'd happily vote up an answer that had what you left in the comments section. – wheaties Nov 20 '10 at 14:59
  • @Wheaties: I have posted a response. – John Dibling Nov 20 '10 at 15:44
  • and which cast should I use after when I have to get back from void* to Foo for example? – thiagoh Oct 10 '12 at 04:58
  • by C++17, `Boost::Any` is part of the standard library [`std::any`](https://en.cppreference.com/w/cpp/utility/any) – Billy May 31 '22 at 07:04
10

There is no direct equivalent to java.lang.Object x = new Foo() because in C++, not everything is an object. But depending on how you want to use these Objects, you can accomplish the same goal.

The closest equivalent to java.lang.Object x = new Foo() in C++ is the use of Abstract Base Classes (ABC). An ABC is a class that is designed to be a base class to other classes. You create an ABC by giving your class at least one pure virtual member function, and you specify that by using this syntax:

class Object
{
public:
  virtual int my_func() = 0; // The "= 0" means "pure virtual"
};

A Pure Virtual member function typically has no implementation in the base class (See footnote *1). It is not possible to create an instance of an ABC:

int main()
{
  Object obj; // not possible because Object is an ABC
}

In order to use an ABC, you must create a subclass of it and implement each and every pure virtual member function in the derived class:

class Foo : public Object
{
public: 
  int my_func() { return 42; } // use of "virtual" is assumed here
};

Now you can create an instance of Foo, while getting a pointer to the base class:

int main()
{
  Object* my_obj = new Foo;
}

The usual disclaimers apply in the above code about using smart pointers etc. I omitted this for clarity, but from now on I'll use shared_ptr.

You can also get an Object reference to Foo without having to fear slicing

int main()
{
  Foo my_foo;
  Object& obj_ref = my_foo; // OK
}

An important note about destructors and ABCs. When you implement an ABC, you often need to have a virtual destructor in the base class (Footnote *2). If you don't implement a virtual destructor in the base class, then when you try to delete an object through the base class pointer, you'll evoke undefined behavior, and this is bad.

   class Object
    {
    public:
      virtual int my_func() = 0;
    };
    class Foo : public Object
    {
    public: 
      int my_func() { return 42; } 
    };

    int main()
    {
      Object* obj = new Foo;
      delete obj;  // Undefined Behavior: Object has no virtual destructor
    }

In fact, in my real-world experience in implementing ABCs I often find that the only member function that I really want to be pure virtual is the destructor. ABCs I design often have many virtual methods that are not pure and then one virtual destructor. IMO (debatable), this is a good starting point when designing an ABC: Make the dtor pure, and keep a minimal number of non-pure virtual member functions in the base class, and provide an implementation for the pure virtual dtor in the base class. As you design this way you'll find things you can't do in your actual code, and that's when you deviate from this design.


Footnotes:


*1 ) Base classes can provide a definition for a pure virtual member function in the base class. But this is not the norm, and the reasons you might do this are somewhat beyond the scope of this post. Note that when you do this there is a special rule in the Standard that says you may not provide the definition along with the declaration; they must be seperate. Like this:

class Object
{
public:
  virtual int my_funky_method() = 0;
  virtual bool is_this_ok() = 0 { return false; } // ERROR: Defn not allowed here
};

int Object::my_funky_method()
{
  return 43;
}

*2) There are exceptions to the rule about having virtual destructors. Beyond the scope of this article, but a better rule of thumb is "A base class destructor should be either public and virtual, or protected and nonvirtual"

Community
  • 1
  • 1
John Dibling
  • 99,718
  • 31
  • 186
  • 324
4

There is no equivalent because Java allocates objects from a managed heap, C++ allocates them in unmanaged memory. Objects in Java are tracked by the JVM for automatic garbage collection using mark-and-sweep, whereas C++ requires explicit release of all memory.

The runtime environments are fundamentally different, drawing analogies due to similar looking syntax is a trap.

Steve Townsend
  • 53,498
  • 9
  • 91
  • 140
  • It isn't true that C++ requires explicit release of all memory. In C++ objects can have automatic, static or dynamic storage duration. Only those with dynamic storage duration require explicit release, the lifetime and memory for objects with automatic and static storage duration is handled automatically. You could even say that `delete x;` explicitly destroys the object and that the memory is released implicitly as part of that. – CB Bailey Nov 21 '10 at 10:17
  • @Charles - thanks for that. Stack-based RAII in C++ is another example. My response was in the context of the question (objects created using `new X;`) and it's overly assertive on the generality of C++ behaviour in the second sentence. – Steve Townsend Nov 21 '10 at 13:56
  • Java uses mark-and-sweep for garbage collection. I'm not sure reference counting would be a good term to use, since it would make people think that it manages memory similarly to Swift, for example. – AteszDude Sep 07 '18 at 12:38
-1
// Test this
namespace external
{
    template <class T>
    struct var
    {
    private:
        T *value;
    public:
        var(T *value)
        {
            this->value = value;
        };

        T operator &() {
            return this-> value;
        };
    };
}
#define var external::var<void *>
/**
  Use:
  var i = 0;
  cout << &i; // Output 0;
*/
  • Thanks for submitting an answer. It might help others to understand your code better if you included a brief explanation of how this solves the original question. – Nick May 30 '21 at 11:50