If readers think my article is good, I hope you can give me more support. The article can be forwarded, but the original source and original author’s signature must be retained. For more content, please follow my wechat official account: CPP Artisan

So let’s see if the two pieces of code are the same, right?

// A series of oa operations...
OptimizationA GetOpt(a)
{
    OptimizationA oa;
    // A series of oa operations...
    return oa;
}

void GetOpt(OptimizationA &_result)
{
    // result of a series of operations...
    return;
}
Copy the code

Consider: Is efficiency the same? If not, how? So how do we do it more efficiently?

Transformation of program semantics

We write our own code, which is one thing to see for ourselves, but from the compiler’s point of view is a landscape. So let’s take a look at how the compiler translates our code in terms of initialization, optimization, and member list initialization.

1. Perform initialization

A. Perform explicit initialization

OptimizationA oe;
OptimizationA of(oe);
OptimizationA og = oe;
OptimizationA oh = OptimizationA(oe);
// From the compiler's point of view, there are two steps,
// The first step is to define the variable (no initialization operation is called). The second step is to call the copy construct
// 1.OptimizationA of (note that the default OptimizationA constructor is not called at this time)
/ / 2. Of. OptimizationA: : OptimizationA (oe) copy constructor (call)
/ / 3. Og. OptimizationA: : OptimizationA (oe) copy constructor (call)
/ / 4. Oh. OptimizationA: : OptimizationA (oe) copy constructor (call)
Copy the code

B. Initialize parameters

void Parameter(OptimizationA oa)
{
}

{
OptimizationA tempoa;
Parameter(tempoa);
} 

// Code generated by the compiler
OptimizationA _tempObj<font>;
// tempObj calls the copy constructor
tempObj.OptimizationA::OptimizationA(tempoa);
Parameter(tempObj);
// tempObj calls the destructor to destroy the object
tempObj.OptimizationA::~OptimizationA();
Copy the code

C. Initialize the return value

OptimizationA GetOpt(a)
{
    OptimizationA oa;
    return oa;
}

// This is a compiler generated function, divided into two steps
// Step 1: Rewrite the above function into the following form with reference arguments
void GetOpt(OptimizationA &_result)
{
    OptimizationA oa;
    // A series of oa operations......
// Step 2: Call the result copy constructor before return
    result::OptimizationA::OptimizationA(oa);
    return;
}
// Here is the calling code generated by the compiler
// 1
OptimizationA result;
GetOpt(result);

// 2. If the user calls a class member function
GetOpt().GetHello();
// The compiler converts to this
(GetOpt(result), result).GetHello();

// 3. If the function pointer is defined by the user
OptimizationA (*pf)();
pf = GetOpt; // No arguments
// The compiler converts to this
void (*pf)(OptimizationA &);
(pf(result), result).GetHello();
Copy the code

2. Optimize

A. User-level optimization

// The programmer is not optimized
OptimizationA GetOpt(const T &y, const T &x)
{
    OptimizationA oa(x, y);
    // oa Other operations
    return oa;
}
// Tuning options need to be turned off when testing on Linux
// Create a temporary object tempobj, then call the tempobj copy constructor to copy oa data to
// tempobj, then call the destructor of oa.
// This process consumes a copy constructor and destructor of tempobj

// The programmer optimizes, so that one less temporary object is generated and destroyed
OptimizationA GetOpt(const T &x, const T &y)
{
    return OptimizationA(x, y);
}
Copy the code
Unoptimized code Optimize the code
Results of disabling optimization options on Linux:

compiler:1 level:2 call ctor

compiler:2 level:3 call copy ctor

compiler:1 level:2 call dtor

compiler:3 level:4 call copy ctor

compiler:2 level:3 call dtor

compiler:3 level:4 call dtor

Linux does not turn off optimization options:

compiler:1 level:2 call ctor

compiler:1 level:2 call dtor

Windows:

compiler:1 level:2 call ctor

compiler:2 level:3 call copy ctor

compiler:1 level:2 call dtor

compiler:2 level:3 call dtor
Linux:

compiler:1 level:2 call ctor

compiler:1 level:2 call dtor

On Windows:

compiler:1 level:2 call ctor

compiler:1 level:2 call dtor

B. Compiler optimization

// Code written by programmers
OptimizationA GetOpt(a)
{
    OptimizationA oa;
    return oa;
}

// Named return value (NRV)
// There are two steps
// Step 1: Rewrite the above function into the following form with reference arguments
void GetOpt(OptimizationA &_result)
{
    OptimizationA oa;
    // A series of oa operations...

    // Step 2: Call the __result copy constructor before return
    __result::OptimizationA::OptimizationA(oa);

    return;
}
Copy the code

3. Initialize the member list

Let’s start with this code:

class InitialzationB
{
public:
// InitialzationB()
//  {}

    InitialzationB(int value):  m_IA(value), m_a(value), m_b(value)
        /* Put it in the initialization column... 1. If you are in the member list initialization, stand in the perspective of the compiler m_IA. InitialzationA: : InitialzationA (value) * /
    {
        /* put it in the constructor... . m_IA = value; InitialzationA oc: InitialzationA oc: InitialzationA oc; oc.InitialzationA::InitialzationA(value); B. in m_IA copy ctor m_IA. InitialzationA: : InitialzationA (oc); Oc.initialzationa ::~InitialzationA(); So initialization of member variables is efficient, but only for class type variables, and has no effect on primitive types. Do not use a class member variable to initialize another member variable */ in the initializer list
    }

private:
    InitialzationA m_IA; // Custom class
    int m_a;
    int m_b;
};
Copy the code

A. Member list initialization meaning

InitialzationB(int Value): m_IA(value), m_a(value), m_b(value) this is the call method for initializing the list

B. Why is the initialization list needed and when is the initialization list invoked

Simply for the efficiency of initializing an object. See that line 7 of the code above is placed in the initialization column, which from the compiler’s point of view calls the InitialzationA constructor directly. But if you put it on line 16, then from the compiler’s point of view, Mr. InitialzationA becomes an InitialzationA temporary object, calls the copy constructor of m_IA, and then calls the destructor for the temporary object’s demise. So the painstaking construction of objects leads to a decrease in efficiency. Call timing: The compiler inserts an extra bit of code, known as the Initialization List, before the constructor. Then execute the user-written code.

C. Precautions

A. Four conditions must be placed in the initialization list
1. Member variables are references
2. Member variables are of const type
3. Member variables are constructor class types with arguments
4. Base classes have constructors that take arguments
B. Initialize the initialization sequence of the initialization list

The order of initialization is determined by the order in which the class is declared. So the class initialization list is copied exactly in the order declared in the class.

Such as:

class InitialzationB
{
public:
// InitialzationB()
//  {}

// InitialzationB(int value): m_IA(value) , m_b(value), m_a(m_b)
// The original method
InitialzationB(int value): m_IA(value), m_a(value), m_b(value)
{
}

private:
    InitialzationA m_IA; 
int m_b;
    int m_a;
};
Copy the code
C. Call member functions in the initialization list

Do not call a member function from an initializer, because you do not know how dependent the function will be on the current object.

Conclusion:

The first method consumes at least one cTOR, copy ctor, dTOR, and the compiler implementation needs to be considered. It may also generate a Temp object and add another copy ctor, dTOR. And the other way around, method two only consumes CTOR, dTOR. Efficiency is definitely higher than method one. You know what the compiler does and how it does it. This will help you better understand the implementation details behind the C++ language so that you can write efficient programs. It also shows that c++ does a lot of things behind the scenes that we don’t know about in order to pursue efficiency. Finally, if we were a compiler, how would we generate code? That’s something to think about.