fredoverflow
fredoverflow

Reputation: 263220

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

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

Upvotes: 8

Views: 13384

Answers (4)

Baokhanhnguyen Dinh
Baokhanhnguyen Dinh

Reputation: 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;
*/

Upvotes: -1

Steve Townsend
Steve Townsend

Reputation: 54168

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.

Upvotes: 4

John Dibling
John Dibling

Reputation: 101476

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"

Upvotes: 10

wheaties
wheaties

Reputation: 35980

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.

Upvotes: 22

Related Questions