181

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
  • 7,913
  • 28
  • 108
  • 196
Nils
  • 13,319
  • 19
  • 86
  • 108
  • 32
    Either form is without new. – Daniel Daranas Apr 27 '10 at 16:12
  • 15
    The second form will use the copy constructor so no, they're not equivalent. – Edward Strange Apr 27 '10 at 16:27
  • 1
    I played a bit with it, the first way seems to fail sometimes when templates are used with parameterless constructors.. – Nils Apr 27 '10 at 20:29
  • 2
    Ouh and I got the "Nice Question" badge for that, what a shame! – Nils Jul 20 '12 at 11:43
  • I think The second form will use the move constructor @Edward Strange – Fady Hany Sep 05 '22 at 02:30
  • @Edward Strange Do you agree with me ? ( please answer me ) – Fady Hany Sep 08 '22 at 03:54
  • @FadyHany - yeah, but keep in mind that my comment was pre-11. Move constructor did not exist. At that time the c++ tag vs c++0x tag or whatever made clear that we were in 03 standard land. Since then that other tag was deleted and everything moved so that information is gone now. Since c++11 you are probably right but I'm not 100% c++11 allowed it. C++11 had a lot of mistakes that were fixed in 14 and 17 and this might have been one of them. I think that now my whole distinction there is false and actually neither copy nor move need exist/be available for this syntax to function. – Edward Strange Sep 09 '22 at 15:50
  • @EdwardStrange So you said that my thought about the second form is totally correct , right ? And in the second half of your last comment you talk about the Optimizations like ( for the example ) Copy elision , right ? – Fady Hany Sep 10 '22 at 16:23
  • @EdwardStrange Please answer my two questions – Fady Hany Sep 19 '22 at 12:02

7 Answers7

196

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
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 10
    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
  • 18
    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
  • I think The second point in " The second one is a bit more complex. It essentially does the following " must be " 2. Create an object of type Thing using the constructor `Thing(Thing&&)` " @JaredPar – Fady Hany Sep 05 '22 at 01:20
  • @JaredPar if you agree with me please change your answer. ( please answer me ) – Fady Hany Sep 08 '22 at 03:54
36

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
  • 246,190
  • 53
  • 318
  • 364
  • 2
    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
  • 6
    The OP's question was correct, this answer concerns another issue entirely (see @JaredPar's answer) – Silmathoron Mar 24 '17 at 15:25
24

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
  • 5,139
  • 1
  • 25
  • 49
Douglas Leeder
  • 52,368
  • 9
  • 94
  • 137
12

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
  • 1,003
  • 1
  • 8
  • 19
  • 1
    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
4

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
  • 13,319
  • 19
  • 86
  • 108
  • 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
  • 2
    And this, folks, is the [most vexing parse](https://en.wikipedia.org/wiki/Most_vexing_parse). – Marc.2377 Mar 30 '18 at 07:54
  • 1
    The solution to this problem is to call f.e.: `Thing myThing{};` as explained in @Marc.2377's Wiki-Article. – GURKE May 30 '22 at 20:50
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
  • 144,682
  • 38
  • 256
  • 465
  • 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

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
Konstantin Burlachenko
  • 5,233
  • 2
  • 41
  • 40