Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ construction: "MyClass c" is bad, "MyClass c = MyClass()" is slow, I want "MyClass c()"

Here's some code:

class MyClass
{
public:
    int y;
};

int main()
{
    MyClass item1;
    MyClass item2 = MyClass();
}

When I run this, I receive the following values:

item1.y == [garbage]
item2.y == 0

Which, well, surprises me.

I expected item1 to be default-constructed and item2 to be copy-constructed off an anonymous default-constructed instance of MyClass, resulting in both equaling 0 (since default-constructors initialize members to default values). Examining the assembly:

//MyClass item1;
//MyClass item2 = MyClass();
xor         eax,eax  
mov         dword ptr [ebp-128h],eax  
mov         ecx,dword ptr [ebp-128h]  
mov         dword ptr [item2],ecx  

Shows item2 being constructed by writing a '0' value somewhere temporary and then copying it into item2, as expected. However, there's NO assembly for item1.

So, the program has memory for item1 in the stack but it never constructs item1.

I can appreciate wanting that behavior for speed purposes, but I want the best of both worlds! I want to know item1.y == 0 (it gets constructed), but I don't want to waste time on default-construct-anonymous-instance-then-copy-construct like item2 does.

Frustratingly, I can't force a default-construct by saying MyClass item1(); since that is interpreted as a function prototype.

So... if I want to use the default constructor on item1 without also copy-constructing, how the heck do I do that?

Side-note: it looks like if I declare a constructor for MyClass, item1 is constructed as usual. So this behavior only applies to compiler-generated constructors.

like image 690
Ben Walker Avatar asked Oct 28 '13 02:10

Ben Walker


People also ask

What are copy constructors in C++?

Copy Constructor in C++ A copy constructor is a member function that initializes an object using another object of the same class. In simple terms, a constructor which creates an object by initializing it with an object of the same class, which has been created previously is known as a copy constructor.

Does C++ create copy constructor automatically?

In C++, the compiler automatically generates the default constructor, copy constructor, copy-assignment operator, and destructor for a type if it does not declare its own. These functions are known as the special member functions, and they are what make simple user-defined types in C++ behave like structures do in C.

What is implicit copy constructor?

An implicitly defined copy constructor will copy the bases and members of an object in the same order that a constructor would initialize the bases and members of the object.

Why is it recommended to use a default constructor while creating a fragment?

This is because the system would need to reinitialize it under certain circumstances like configuration changes and the app's process recreation. If it weren't for the default constructor restriction, the system wouldn't know how to reinitialize the Fragment instance.


2 Answers

Make your class look like this:

class MyClass 
{
public:
    MyClass() : y(0) 
    {
    }

public:
    int y;
};

and both examples will work fine. Your problem is that if no constructor is supplied, the basic type members will not be initialized. So y is representing whatever random data happens to be on the stack in the spot where item1 is residing.

Explicitly implementing a constructor addresses this.

The behavior exists because of C++'s "you only pay for what you use" mentality. Basic types don't have a "default value" as that would make it unnecessarily (slightly) more costly to allocate something and then fill it in later due to the values effectively being set twice. Once for the "default value", once for the value you actually wanted.

like image 141
Evan Teran Avatar answered Sep 17 '22 11:09

Evan Teran


In C++ you only pay for what you ask to be done. The ability to have types that do not initialize their data can be an important performance boost: annoyingly, this is also the default behavior, because it was inherited from C.

You can fix this by creating a constructor that zero initializes the int, by using {} uniform initialization syntax, or by using the new default syntax int y = 0; in the type declaration (the last requires C++11, the second requires C++11 if the type is non-POD).

Your performance concerns about Foo f = Foo(); are mislead by examining debug build assembly. Copy elision in such a trivial case is supported by every non brain dead compiler on the market, and is legal even if the ctor has side effects.

like image 24
Yakk - Adam Nevraumont Avatar answered Sep 16 '22 11:09

Yakk - Adam Nevraumont