C++ object call optimization

C++ object call optimization

The temporary object is copied to construct a new object, and the temporary object will not be generated ! ! !

Common object call process

The c++ compiler optimizes object construction: when using a temporary object to copy a new object, the temporary object will not be generated, and the new object can be constructed directly .

#include<iostream>
using namespace std;

class Test
{
    
    
public:
    Test(int data = 10) : ma(data), mb(data) {
    
     cout << "Test(int)" << endl; }
    ~Test() {
    
     cout << "~Test()" << endl; }
    Test(const Test &t) {
    
     cout << "Test(&)" << endl; }
    Test &operator=(const Test &t) {
    
     cout << "operator=" << endl; }

private:
    int ma;
    int mb;
};
int main()
{
    
    
    Test t1;//普通构造函数
    Test t2(t1);//拷贝构造函数
    Test t3 = t1; // 拷贝构造函数

    t2 = t3;//拷贝构造函数

    // 这里和Test t4(20)没有区别,并不会产生Test(20)临时对象
    Test t4 = Test(20);//普通构造函数
    cout << "-------------------" << endl;

    //显示调用构造函数生成临时对象
    t4 = Test(30); // 普通构造函数,结束这条语句就会调用析构函数
    t4 = (Test)30; // 普通构造函数,结束这条语句就会调用析构函数

    //隐式调用构造函数生成临时对象
    t4 = 30; // 普通构造函数,结束这条语句就会调用析构函数
    cout << "-----------------" << endl;

    return 0;
}


const Test &t1 = Test(20, 20);//Just call the ordinary constructor, no temporary object will be generated (so there will be no destruction).

Object Creation Order

It should be noted that static Test t4 = Test(30, 30);the compiler will optimize here, and the temporary object will generate a new object, and no temporary object will be generated, which is equivalent to static Test t4(30,30). Test *p3 = &Test(80, 80);, where the statement is destructed after the end, and p3 is a null pointer. const Test &p4 = Test(90, 90);, the reference variable will hold the temporary object, so it will not be destructed. Static objects are destructed after the scope ends.

#include<iostream>
using namespace std;

class Test
{
    
    
public:
    Test(int x = 10, int y = 10) : ma(x), mb(y) {
    
     cout << "Test(int,int)" << endl; }
    ~Test() {
    
     cout << "~Test()" << endl; }
    Test(const Test &t) {
    
     cout << "Test(&)" << endl; }
    Test &operator=(const Test &t) {
    
     cout << "operator=" << endl; }

private:
    int ma;
    int mb;
};
Test t1(10, 10);//1.Test(int,int)
int main()
{
    
    

    Test t2(20, 20);//3.Test(int,int)
    Test t3 = t2;   // 4.Test(&)
    // 这里编译器会优化,临时对象生成新对象,不会产生临时对象,等价于static Test t4(30,30)
    static Test t4 = Test(30, 30); // 5.Test(int,int),
    t2 = Test(40, 40);             // 6.Test(int,int),operator=,~Test()
    t2 = (Test)(50, 50);           // 7.Test(int,int),operator=,~Test()
    t2 = 60;                       // 8.Test(int,int),operator=,~Test()
    Test *p1 = new Test(70, 70);   // 9.Test(int,int)
    Test *p2 = new Test[2];        // 10.Test(int,int),Test(int,int)
    // Test *p3 = &Test(80, 80);      //可能会报错// 11.Test(int,int),~Test(),这里语句结束后就被析构,p3就是空指针
    const Test &p4 = Test(90, 90); // 12.Test(int,int),引用变量就会保存临时对象,所以不会被析构
    delete p1;                     // 13.~Test()
    delete[] p2;                   // 14.~Test()
    return 0;
}
Test t5(100, 100);//2.Test(int,int)
/*
剩下的析构顺序,p4->t3->t2->t4->t5->t1
t4在t3和t2后面析构的原因是它是静态变量,存储在数据段中,所以在后面析构
*/

Problems with function calls

There are many object optimization problems in the process of function calling, and how to optimize them will be introduced in detail later.

#include<iostream>
using namespace std;

class Test
{
    
    
public:
    Test(int data = 10) : ma(data) {
    
     cout << "Test(int)" << endl; }
    Test(const Test& t) {
    
     cout << "Test(&)" << endl; }
    Test& operator=(const Test& t) {
    
     cout << "operator=" << endl; return *this; }
    ~Test() {
    
     cout << "~Test()" << endl; }
    int getData() {
    
     return ma; }

private:
    int ma;
};
Test getObj(Test t)
{
    
    
    int val = t.getData();
    Test temp(val);
    return temp;
}
int main()
{
    
    
    Test t1;
    Test t2;

    cout << "------------------" << endl;
    t2 = getObj(t1);
    cout << "------------------" << endl;
    Test t3 = getObj(t2);
    cout << "------------------" << endl;

    return 0;
}


Function object call process graph.

Function optimization: Try to pass references instead of passing values; try to return temporary objects instead of constructing objects inside the function. The optimized code looks like this:

Test getObj(Test& t)
{
    
    
    int val = t.getData();
    //Test temp(val);
    return Test(val);
}

It can reduce the copy construction from actual parameters to formal parameters, as well as the construction and destruction of objects inside functions.

Function call optimization summary! ! !

During function passing, objects are passed by reference first, not by value .

When the function returns an object, try to return a temporary object instead of returning a defined object .

When the return value of receiving is an object, try to receive it in the form of initialization rather than in the form of assignment .

Specifically, you can explain and analyze the existing problems of function calls! ! !

String rvalue reference copy construction and operator overloading

#include<iostream>
#include<string.h>

using namespace std;

class String
{
    
    
public:
    String(const char* p = nullptr)
    {
    
    
        if (p != nullptr)
        {
    
    
            _pstr = new char[strlen(p) + 1];
            strcpy(_pstr, p);
        }
        else
        {
    
    
            _pstr = new char[1];
            *_pstr = '\0';
        }
        cout << "String(const char*)" << endl;
    }
    ~String()
    {
    
    
        cout << "~String()" << endl;
        delete[] _pstr;
        _pstr = nullptr;
    }
    String(const String& src)
    {
    
    
        cout << "String(const String&)" << endl;
        _pstr = new char[strlen(src._pstr) + 1];
        strcpy(_pstr, src._pstr);
    }
    String(String&& src)
    {
    
    
        //因为是临时对象,所以就没有用const修饰
        cout << "String(const String&&)" << endl;
        _pstr = src._pstr;
        src._pstr = nullptr;
    }
    String& operator=(const String& src)
    {
    
    
        cout << "String& operator=(const String&)" << endl;
        if (this == &src)
            return *this;
        delete[] _pstr;
        _pstr = new char[strlen(src._pstr) + 1];
        strcpy(_pstr, src._pstr);
        return *this;
    }
    String& operator=(String&& src)
    {
    
    
        //右值引用的操作符重载函数
        cout << "String& operator=(String&&)" << endl;
        if (this == &src)
            return *this;
        delete[] _pstr;
        _pstr = src._pstr;
        src._pstr = nullptr;
        return *this;
    }
    bool operator>(const String& src) const
    {
    
    
        return strcmp(_pstr, src._pstr) > 0;
    }
    bool operator==(const String& src) const
    {
    
    
        return strcmp(_pstr, src._pstr) == 0;
    }
    bool operator<(const String& src) const
    {
    
    
        return strcmp(_pstr, src._pstr) < 0;
    }
    int length()const
    {
    
    
        return strlen(_pstr);
    }
    char& operator[](int index) {
    
     return _pstr[index]; }
    const char& operator[](int index) const {
    
     return _pstr[index]; }
    const char* c_str() const {
    
     return _pstr; }

private:
    char* _pstr;
    friend String operator+(const String& lsrc, const String& rsrc);
    friend ostream& operator<<(ostream& out, const String& src);
    friend istream& operator>>(istream& in, String& src);
};

String operator+(const String& lsrc, const String& rsrc)
{
    
    
    char* temp = new char[strlen(lsrc._pstr) + strlen(rsrc._pstr) + 1];
    strcpy(temp, lsrc._pstr);
    strcat(temp, rsrc._pstr);
    String s(temp);
    delete[] temp;
    return s;
}
ostream& operator<<(ostream& out, const String& src)
{
    
    
    out << src._pstr;
    return out;
}
istream& operator>>(istream& in, String& src)
{
    
    
    in >> src._pstr;
    return in;
}

int main()
{
    
    
    String str1;
    String str2 = "aaa";
    String str3 = "bbb";
    String str4 = str2 + str3;
    String str5 = str2 + "ccc";
    String str6 = "ddd" + str2;
    
    cout << "----------------------------------" << endl;
    String str7 = String("hello,world");//C++会优化,直接调用构造函数,不会产生临时对象
    str6 = String("hello") + String(",world");//先调用两个构造函数,在调用operator+重载,
    cout << "----------------------------------" << endl;

    return 0;
}

Case without rvalue operator and copy constructor:


Copy construction and operator overloading functions with rvalue references are set.


It can be seen that all the functions in the original object generation using temporary objects are replaced by copy constructors with rvalue references, and assignment operator overloading is also replaced by copy constructors with rvalue references. The advantage of using rvalue references is mainly to save resource creation and release during copy construction and operator overloading .

The use of String on vector

move && forward

Because an rvalue reference variable is itself an lvalue , problems can arise when a function is passed an rvalue reference variable, which can be shown as follows:

void construct(T* p, const T& val)//对象创建
{
    
    
    // 更多new初始化方法 https://blog.csdn.net/qq_45041871/article/details/132251733
    new (p) T(val);//使用的是定位new,将val传到地址为p的空间去,p地址指向val
}
void construct(T* p, T&& val)//对象创建
{
    
    
    // 更多new初始化方法 https://blog.csdn.net/qq_45041871/article/details/132251733
    //这里虽然val是一个右值引用变量,但是还是会调用左值的拷贝构造函数
    new (p) T(val);//使用的是定位new,将val传到地址为p的空间去,p地址指向val
}
void push_back(T&& val)
{
    
    
    if (full())
        expand();
    //虽然传入的val是一个右值引用变量,但是它本身是一个左值,
    //所以还是会调用void construct(T* p, const T& val)而不是带右值引用的函数
    _allocator.construct(_last, val);
    _last++;
}

move: move semantics, coercion to obtain an rvalue type. It can be realized to convert an lvalue into an rvalue (this value may actually be an rvalue reference variable).
forward: Perfect conversion of types, capable of recognizing lvalue and rvalue types. If it turns out to be an lvalue, then it's still an lvalue; if it turns out to be an rvalue, then it's passed as an rvalue.

String& &&val->String&One citation plus two citations is still one citation.
String&& &&val->String&Two citations plus two citations or two citations.
String&& &val->String&Two references plus one reference is still a reference, because an rvalue reference is itself an lvalue.

The following val is actually the formal parameter type in the function passing process.

So the above code can be considered to be changed to the following two types:

Method 1: move forced conversion

void construct(T* p, const T& val)//对象创建
{
    
    
    // 更多new初始化方法 https://blog.csdn.net/qq_45041871/article/details/132251733
    new (p) T(val);//使用的是定位new,将val传到地址为p的空间去,p地址指向val
}
void construct(T* p, T&& val)//对象创建
{
    
    
    // 更多new初始化方法 https://blog.csdn.net/qq_45041871/article/details/132251733
    new (p) T(std::move(val));//使用的是定位new,将val传到地址为p的空间去,p地址指向val
}
void push_back(const T& val)
{
    
    
    if (full())
        expand();
    //虽然传入的val是一个右值引用变量,但是它本身是一个左值,
    //所以还是会调用void construct(T* p, const T& val)而不是带右值引用的函数
    _allocator.construct(_last, val);
    _last++;
}
void push_back(T&& val)
{
    
    
    if (full())
        expand();
    _allocator.construct(_last, std::move(val));
    _last++;
}

Method 2: forward perfect transformation

//不管val传递的是左值还是右值,T&& val都能接收
//如果是左值,则接收后还是左值;反之右值,则还是右值
void construct(T* p, T&& val)//对象创建
{
    
    
    // 更多new初始化方法 https://blog.csdn.net/qq_45041871/article/details/132251733
    new (p) T(std::forward(val));//使用的是定位new,将val传到地址为p的空间去,p地址指向val
}
void push_back(T&& val)
{
    
    
    if (full())
        expand();
    //虽然传入的val是一个右值引用变量,但是它本身是一个左值,
    //所以还是会调用void construct(T* p, const T& val)而不是带右值引用的函数
    _allocator.construct(_last, std::forward(val));
    _last++;
}

Guess you like

Origin blog.csdn.net/qq_45041871/article/details/132499166
Recommended