tags:

views:

289

answers:

7

I've often seen that people create objects in C++ using

Thing myThing("asdf");

Instead of

Thing myThing = myThing("asdf");

This seems to work (using gcc), at least as long as there are no templates involved. My question now, is the first line correct and if so should I use it?

+15  A: 

Both lines are in fact correct but do subtly different things.

The first line creates a new object on the stack by calling a constructor of the format Thing(const char*).

The second one is a bit more complex. It essentially does the following

  1. Create an object of type Thing using the constructor Thing(const char*)
  2. Create an object of type Thing using the constructor Thing(const Thing&)
  3. Call ~Thing() on the object created in step #1
JaredPar
I guess these types of actions are optimized and therefore, do not significantly differ in performance aspects.
Kotti
I don't think your steps are quite right. `Thing myThing = Thing(...)` does not use the assignment operator, it's still copy-constructed just like saying `Thing myThing(Thing(...))`, and does not involve a default-constructed `Thing` (edit: post was subsequently corrected)
AshleysBrain
So you can say that the second line is incorrect, because it wastes resources for no apparent reason. Of course it is possible that creating of the first instance is intentional for some side effects, but that's even worse (stylistically).
MK
@Kotti, speaking about C++ compiler optimizations is very difficult to do in the general sense because the compilers vary heavily. I try to avoid it because there are so many special cases I am not aware of.
JaredPar
What about templates? I remember some cases where it did not seem to work..
Nils
@AshleysBrain is that guaranteed according to the standard? I admittedly get it wrong on occasion when a copy constructor and assignment constructor and assignment operator are used in this type of scenario.
JaredPar
@JaredPar - as far as I am aware `Class c = Class()` is alternative syntax for `Class c(Class())` (they're essentially identical).
AshleysBrain
No, @Jared, it's not guaranteed. But even if the compiler chooses to perform that optimization, the copy constructor still needs to be accessible (i.e., not protected or private), even if it's not implemented or called.
Rob Kennedy
Does return value optimization (RVO) have any interplay with this chain of events? I would like to think that the copy could be elided, but of course that can only happen if it's side effect free.
dash-tom-bang
It appears the copy can be elided even if the copy constructor has side-effects - see my answer: http://stackoverflow.com/questions/2722879/calling-constructors-in-c-without-new/2723266#2723266
Douglas Leeder
+3  A: 

Yes, the first line is correct. I would use that syntax instead of the second one, because depending on your compiler, the second syntax will construct a temporary myThing and then pass it into your named variable using the copy assignment operator. (Note that it doesn't have to do this, but compilers are allowed to)

Billy ONeal
+2  A: 

Quite simply, both lines create the object on the stack, rather than on the heap as 'new' does. The second line actually involves a second call to a copy constructor, so it should be avoided (it also needs to be corrected as indicated in the comments). You should use the stack for small objects as much as possible since it is faster, however if your objects are going to survive for longer than the stack frame, then it's clearly the wrong choice.

Stephen Cross
+3  A: 

i assume with the second line you actuall mean:

MyThing *myThing = new MyThing("uiae");

which would be the standard way of creating new dynamic objects (necessary for dynamic binding and polymorphism) and storing their address to a pointer. your code does what JaredPar described, namely creating two objects, assigning with operator= and deleting one of them

MyThing myThing("uiae");

creates a static object which is destroyed automatically upon exiting the current scope

knittl
+1  A: 

Ideally, a compiler would optimize the second, but it's not required. The first is the best way. However, it's pretty critical to understand the distinction between stack and heap in C++, sine you must manage your own heap memory.

DeadMG
Can the compiler guarantee that the copy constructor doesn't have side effects (such as I/O)?
Stephen Cross
@Stephen - it doesn't matter if the copy constructor does I/O - see my answer http://stackoverflow.com/questions/2722879/calling-constructors-in-c-without-new/2723266#2723266
Douglas Leeder
Ok, I see, the compiler is allowed to turn the second form into the first and thereby avoids the call to the copy constructor.
Stephen Cross
+5  A: 

The compiler may well optimize the second form into the first form, but it doesn't have to.

#include <iostream>

class A
{
    public:
        A() { std::cerr << "Empty constructor" << std::endl; }
        A(const A&) { std::cerr << "Copy constructor" << std::endl; }
        A(const char* str) { std::cerr << "char constructor: " << str << std::endl; }
        ~A() { std::cerr << "destructor" << std::endl; }
};

void direct()
{
    std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
    A a(__FUNCTION__);
    static_cast<void>(a);
}

void assignment()
{
    std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
    A a = A(__FUNCTION__);
    static_cast<void>(a);
}

void prove_copy_constructor_is_called()
{
    std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
    A a(__FUNCTION__);
    A b = a;
    static_cast<void>(b);
}

int main()
{
    direct();
    assignment();
    prove_copy_constructor_is_called();
    return 0;
}

Output from gcc 4.4:

TEST: direct
char constructor: direct
destructor

TEST: assignment
char constructor: assignment
destructor

TEST: prove_copy_constructor_is_called
char constructor: prove_copy_constructor_is_called
Copy constructor
destructor
destructor
Douglas Leeder
What is the purpose of the static casts to void?
Stephen Cross
@Stephen Avoid warnings about unused variables.
Douglas Leeder
A: 

I played a bit with it and the syntax seems to get quite strange when a constructor takes no arguments. Let me give an example:

#include <iostream> 

using namespace std;

class Thing
{
public:
    Thing();
};

Thing::Thing()
{
    cout << "Hi" << endl;
}

int main()
{
    //Thing myThing(); // Does not work
    Thing myThing; // Works

}

so just writing Thing myThing w/o brackets actually calls the constructor, while Thing myThing() makes the compiler thing you want to create a function pointer or something ??!!

Nils
This is a well known syntactic ambiguity in C++. When you write "int rand()", the compiler cannot know if you mean "create an int and default-initialize it" or "declare function rand". The rule is that it chooses the latter whenever possible.
jpalecek