tags:

views:

250

answers:

6

This came up recently in a class for which I am a teaching assistant. We were teaching the students how to do copy constructors in c++, and the students who were originally taught java asked if you can call one constructor from another. I know the answer to this is no, as they are using the pedantic flag for their code in class, and the old standards do not have support for this. I found on Stackoverflow and other sites a suggestion to fake this using new (this) such as follows

class MyClass
{
    private:
        int * storedValue;
    public:
        MyClass(int initialValue = 0)
        {
            storedValue = new int(initialValue);
        }

        ~ MyClass()
        {
            delete storedValue;
        }

        MyClass(const MyClass &b)
        {
            new (this) MyClass(*(b.storedValue));
        }

        int value() {
            return *storedValue;
        }
};

This is really simple code, and obviously does not save any code by reusing the constructor, but it is just for example.

My question is if this is even standard compliant, and if there are any edge cases that should be considered that would prevent this from being sound code?

Edit: I should note that this seems very dangerous to me, but that is more from a view point of I don't really understand it more than knowing how it can go bad. I just wanted to make sure that if asked about it by students that I can direct them towards why the can or shouldn't do it. I already suggested to them for all practical purposes to use a shared initialization method. This is more of a teaching question than for a practical project.

+2  A: 

Unless you're trying to call a parent's constructor, I'd suggest making a private initialization method. There's no reason you couldn't call a shared initializer across your constructors.

Pestilence
This is how I normally do my code. Using the method I described above seems dangerous to me, but I didn't want to just tell students "don't do that cause I don't like the looks of it". But, yes in practice, this is how I normally deal with this type of situation and was the first suggestion I made to students.
Brandon Bodnár
A: 

This does not work if you have a constructor like that:

class MyClass {
public:
    MyClass( const std::string & PathToFile )
    : m_File( PathToFile.c_str( ) )
    {
    }
private:
    std::ifstream m_File;
}

The original argument cannot be recovered, so you cannot call that constructor from a copy-constructor.

Space_C0wb0y
wouldn't you have the same problem in a java example? (ie a java constructor that takes another MyClass can't call the constructor that takes String if MyClass doesn't have a String.
tony
You are right, it would not work there either. Guess that makes my example beside the point.
Space_C0wb0y
+8  A: 

The members and base classes will be initialised before entering the constructor body, then initialised again when you call the second constructor. In general this will lead to memory leaks and possibly undefined behaviour.

So the answer is "no, this is not sound code".

Mike Seymour
So this will case additional memory allocations for the base class? I was under the assumption that it reuses the space from the current allocation when you do `new (this) Something()`. If this assumption is wrong than this is an obvious problem with the above code. Thanks.
Brandon Bodnár
Ahh. @tony's answer helped clarify what you meant in my mind. I understand now.
Brandon Bodnár
What if the base class allocated something.class Base { char * ptr; Base() {ptr = new char[1000]; } };
tony
Or for that matter what if one of the class' *members* allocated something? e.g. a `std::vector`. Overwriting an object's memory with placement new will not call *any* destructors, for the class or its bases or its members. A bad, bad idea this is.
Tyler McHenry
+7  A: 

C++0x will introduce syntax to allow constructors to call other constructors.

Until then, new(this) works in some cases, but not all. In particular, once in the constructor, your base class(es) are already fully constructed. Reconstructing via new(this) re-calls the base constructors without calling the base destructors, so expect problems if the base classes weren't expecting this kind of hackery - and they probably weren't.

An example for clarity:

class Base
{
public:
   char *ptr;
   MyFile file;
   std::vector vect;
   Base()
   {
       ptr = new char[1000];
       file.open("some_file");
   }
   ~Base()
   {
       delete [] ptr;
       file.close();
   }
};

class Derived : Base
{
    Derived(Foo foo)
    {
    }
    Derived(Bar bar)
    {
       printf(ptr...);  // ptr in base is already valid
       new (this) Derived(bar.foo); // ptr re-allocated, original not deleted
       //Base.file opened twice, not closed
       // vect is who-knows-what
       // etc
    }
}

or as they say 'hilarity ensues'

tony
+2  A: 

Here is what C++ FAQ Lite has to say about it, question 10.3, “Can one constructor of a class call another constructor of the same class to initialize the this object?”:

BTW do NOT try to achieve this via placement new. Some people think they can say new(this) Foo(x, int(x)+7) within the body of Foo::Foo(char). However that is bad, bad, bad. Please don't write me and tell me that it seems to work on your particular version of your particular compiler; it's bad. Constructors do a bunch of little magical things behind the scenes, but that bad technique steps on those partially constructed bits. Just say no.

Nate
A: 

As that exact code is written, it should work -- though I can't imagine exactly why you'd write code like that. In particular, it depends on the fact that all the pointer is only ever used to refer to a single int. That being the case, why didn't they just put an int in the object, instead of using a pointer and allocating the int dynamically? In short, what they have is long and inefficient, but not significantly different from:

class MyClass {
    int v;
public:
    MyClass(int init) : v(init) {}
    int value() { return v; }
};

Unfortunately, the minute you try to get some real use from the pointer (e.g., allocating different amounts of memory in different objects) the "trick" they using with placement new quits working -- it depends completely on the fact that every object is allocating exactly the same amount of memory. Since you're limited to exactly the same allocation in each, why put that allocation in the heap instead of making it part of the object itself?

Truthfully, there are circumstances when it would make sense. The only one I can think of right off, however, is that the allocation is large and you're running in an environment where there's a lot more heap space than stack space.

The code works, but it's only useful under fairly narrow, specific circumstances. It doesn't strike me as something I'd recommend as an example of how to do things.

Jerry Coffin
"but not significantly different" - well, his version has a broken assignment operator and yours doesn't. So there is some difference ;-)
Steve Jessop
@Steve:Okay, I'll accept that. I probably should have said "not significantly different from what he apparently intended to do."
Jerry Coffin