Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does whether a struct's destructor runs depend on the type of a member variable?

Tags:

c++

I'm fairly new to C++ and when wandering around the constructor and destructor behavior I found this problem:

#include <iostream>

struct Student {
    std::string a;
    ~Student() {
        std::cout << "Destructor called\n";
    }
} S;

int main() {
    std::cout << "Before assigning to S\n";
    S = {""};
    std::cout << "After assigning to S\n";
}

When I compile the code above with g++ and run it, it prints:

Before assigning to S
Destructor called
After assigning to S
Destructor called

But when I change std::string a; to const char *a;, then it prints:

Before assigning to S
After assigning to S
Destructor called

Can anyone explain why this change makes the destructor run one fewer time?

like image 205
Thinh Tran Avatar asked Mar 29 '21 03:03

Thinh Tran


People also ask

In which case is it mandatory to provide a destructor in a class?

Destructor function is called automatically when the object goes out of scope. When a class contains dynamic object then it is mandatory to write a destructor function to release memory before the class instance is destroyed this must be done to avoid memory leak.

When an object is destroyed or goes out of scope What type of member?

The destructor is only one way to destroy the object create by constructor. Hence destructor can-not be overloaded. Destructor neither requires any argument nor returns any value. It is automatically called when object goes out of scope.

What is the order of execution of destructor in following statement?

The body of an object's destructor is executed, followed by the destructors of the object's data members (in reverse order of their appearance in the class definition), followed by the destructors of the object's base classes (in reverse order of their appearance in the class definition).

Is the destructor automatically in C++?

A destructor is a member function that is invoked automatically when the object goes out of scope or is explicitly destroyed by a call to delete .

What is the difference between an instance variable and a destructor?

An instance variable or an object is eligible for destruction when it is no longer reachable. A Destructor is unique to its class i.e. there cannot be more than one destructor in a class. A Destructor has no return type and has exactly the same name as the class name (Including the same case).

Do I need a destructor if I use a struct?

Whether you need a destructor is NOT determined by whether you use a struct or class. The deciding factor is whether the struct / class has acquired resources that must be released explicitly when the life of the object ends. If the answer to the question is yes, then you need to implement a destructor. Otherwise, you don't need to implement it.

What are the arguments of a destructor in C++?

The destructor does not have arguments. It has no return type not even void. An object of a class with a Destructor cannot become a member of the union. A destructor should be declared in the public section of the class.

What is the difference between destructor and constructor?

A Destructor is unique to its class i.e. there cannot be more than one destructor in a class. A Destructor has no return type and has exactly the same name as the class name (Including the same case). It is distinguished apart from a constructor because of the Tilde symbol (~) prefixed to its name.


2 Answers

You are seeing Copy Elision. This is an optimization that will bypass construction of temporary objects in certain cases.

In GCC, that can be disabled by passing the following compiler flag (although it's generally not recommended):

-fno-elide-constructors

In your case, I believe the optimization is triggering due to the struct becoming a trivial type, therefore it's being treated as POD (plain old data) and the memory is simply modified without constructing an object.

Live demo showing the effect of -fno-elide-constructors: https://godbolt.org/z/c3qrMEr9s

like image 176
paddy Avatar answered Oct 24 '22 08:10

paddy


First, in either case, Student is aggregate. Now, we examine the relevant rule for S = {""};

A braced-init-list may appear on the right-hand side of

  • an assignment to an object of class type, in which case the initializer list is passed as the argument to the assignment operator function selected by overload resolution

Since there is no suitable built-in candidate for this case, hence the only viable candidate is the implicitly-declared copy assignment operator, which has the form:

Student& Student::operator=(Student const&)

The corresponding argument is {""}. Argument passing will initiate copy-initialization, which means the parameter will copy-initialized from {""}; It's a list-initialization, and the following rule will apply here

Otherwise, if T is a reference type, a prvalue is generated. The prvalue initializes its result object by copy-list-initialization. The prvalue is then used to direct-initialize the reference. The type of the temporary is the type referenced by T, unless T is “reference to array of unknown bound of U”, in which case the type of the temporary is the type of x in the declaration U x[] H, where H is the initializer list.

That means the reference will be bound to the temporary which is copy-initialized by {""}. That's an aggregate initialization since Student is an aggregate. And the lifetime of the temporary will be ended at the full-expression, which is ruled by the following rule:

Temporary objects are destroyed as the last step in evaluating the full-expression ([intro.execution]) that (lexically) contains the point where they were created.

Hence, the correct output should be

  Before assigning to S
  Destructor called // for the temporary 
  After assigning to S
  Destructor called  // for the object S

Hence, GCC is wrong here. Since its behavior does not conform to what the standard states. As a contrast, Clang implements the right behavior.

like image 42
xmh0511 Avatar answered Oct 24 '22 08:10

xmh0511