156

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

Thing myThing("asdf");

Instead of this:

Thing myThing = Thing("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?

SexyBeast
  • 8,635
  • 25
  • 92
  • 179
Nils
  • 12,129
  • 17
  • 78
  • 103

7 Answers7

169

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
  • 673,544
  • 139
  • 1,186
  • 1,421
  • 8
    I guess these types of actions are optimized and therefore, do not significantly differ in performance aspects. – M. Williams Apr 27 '10 at 16:14
  • 14
    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 Apr 27 '10 at 16:16
  • 1
    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. Apr 27 '10 at 16:16
  • @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 Apr 27 '10 at 16:16
  • @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 Apr 27 '10 at 16:17
  • @JaredPar - as far as I am aware `Class c = Class()` is alternative syntax for `Class c(Class())` (they're essentially identical). – AshleysBrain Apr 27 '10 at 16:18
  • 3
    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 Apr 27 '10 at 16:21
  • 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 Apr 27 '10 at 16:49
  • 3
    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 Apr 27 '10 at 17:02
  • So does this mean if the destructor is user defined, and included something like releasing of a file handle that is opened in the constructor (again, user defined), the second form will create an object with the file handle already released? – SexyBeast Jan 13 '15 at 05:58
33

I assume with the second line you actually mean:

Thing *thing = new Thing("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 (one passed a const char*, the other passed a const Thing&), and then calling the destructor (~Thing()) on the first object (the const char* one).

By contrast, this:

Thing thing("uiae");

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

knittl
  • 197,664
  • 43
  • 269
  • 318
  • 1
    Unfortunately, that is indeed the most common way of creating new dynamic objects instead of using auto_ptr, unique_ptr, or related. – Fred Nurk Jan 18 '11 at 13:18
  • 4
    The OP's question was correct, this answer concerns another issue entirely (see @JaredPar's answer) – Silmathoron Mar 24 '17 at 15:25
22

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); // avoid warnings about unused variables
}

void assignment()
{
    std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
    A a = A(__FUNCTION__);
    static_cast<void>(a); // avoid warnings about unused variables
}

void prove_copy_constructor_is_called()
{
    std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
    A a(__FUNCTION__);
    A b = a;
    static_cast<void>(b); // avoid warnings about unused variables
}

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
Zereges
  • 4,833
  • 1
  • 20
  • 47
Douglas Leeder
  • 49,001
  • 8
  • 86
  • 133
10

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
  • 953
  • 1
  • 7
  • 19
  • For those unfamiliar with the difference between instantiating objects on the stack as opposed to on the heap (that is using _new_ and not using _new_), [here's a good thread.](https://stackoverflow.com/questions/6500313/why-should-c-programmers-minimize-use-of-new) – edmqkk Feb 17 '19 at 16:55
2

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.

Puppy
  • 138,897
  • 33
  • 232
  • 446
  • Can the compiler guarantee that the copy constructor doesn't have side effects (such as I/O)? – Stephen Cross Apr 27 '10 at 16:25
  • @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 Apr 27 '10 at 17:00
  • 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 Apr 27 '10 at 17:44
2

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
  • 12,129
  • 17
  • 78
  • 103
  • 7
    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 Apr 28 '10 at 08:56
  • 1
    And this, folks, is the [most vexing parse](https://en.wikipedia.org/wiki/Most_vexing_parse). – Marc.2377 Mar 30 '18 at 07:54
2

In append to JaredPar answer

1-usual ctor, 2nd-function-like-ctor with temporary object.

Compile this source somewhere here http://melpon.org/wandbox/ with different compilers

// turn off rvo for clang, gcc with '-fno-elide-constructors'

#include <stdio.h>
class Thing {
public:
    Thing(const char*){puts(__FUNCTION__ );}
    Thing(const Thing&){puts(__FUNCTION__ );}   
    ~Thing(){puts(__FUNCTION__);}
};
int main(int /*argc*/, const char** /*argv*/) {
    Thing myThing = Thing("asdf");
}

And you will see the result.

From ISO/IEC 14882 2003-10-15

8.5, part 12

Your 1st,2nd construction are called direct-initialization

12.1, part 13

A functional notation type conversion (5.2.3) can be used to create new objects of its type. [Note: The syntax looks like an explicit call of the constructor. ] ... An object created in this way is unnamed. [Note: 12.2 describes the lifetime of temporary objects. ] [Note: explicit constructor calls do not yield lvalues, see 3.10. ]


Where to read about RVO:

12 Special member functions / 12.8 Copying class objects/ Part 15

When certain criteria are met, an implementation is allowed to omit the copy construction of a class object, even if the copy constructor and/or destructor for the object have side effects.

Turn off it with compiler flag from comment to view such copy-behavior)

Community
  • 1
  • 1
bruziuz
  • 4,177
  • 2
  • 33
  • 37