1. Object reference optimization, right value reference optimization

Time:2022-5-23

This section mainly talks about some precautions in the process of using and calling objects and functions. The more important ones are the right value reference and the last move and forward

What methods are called during the use of the object?

For the following test class, a dozen different definitions are listed

class Test {
public:
    Test(int a = 4, int b = 10) : ma(a), mb(b) {
        cout << "Test()" << endl;
    }

    ~Test() {
        cout << "~Test()" << endl;
    }

    Test(const Test &src) {
        ma = src.ma;
        mb = src.mb;
        cout << "Test(const Test&)" << endl;
    }

    Test &operator=(const Test &src) {
        ma = src.ma;
        mb = src.mb;
        cout << "operator=(const Test&)" << endl;
        return *this;
    }

private:
    int ma;
    int mb;
};

The results are as follows:

There are several noteworthy points:

  • Object assignment will produce temporary objects, which will execute destructors after the end of the statement
  • Implicitly generated temporary objects, such ast2=60, the compiler will find out whether there is an appropriate constructor in the object to generate the object
  • Save the temporary object with a pointer. The temporary object will be destructed after the end of the statement. It is safe to point to objects by reference
  • (50,50) this form is a comma expression, which only looks at the last number when assigning a value

Methods called behind function calls

In the process of function call, the parameter passed to the formal parameter needs to be reinitialized, and the formal parameter object of the function needs to be initialized. In this process, the object will be calledCopy construction method

The object returned inside the function body should also be in the main stack framecopy construction A temporary variable to access this object in the main scope.

After the function body is executed, you need to destruct the object constructed in the function body first, and then destruct the object constructed from the formal parameter list

Three rules of object optimization

  1. In the process of passing function parameters, objects are passed by reference first, not by value.
  2. When a function returns an object, it should give priority to returning a temporary object rather than a defined object
  3. When accepting a function call whose return value is an object, it is preferred to receive it in the way of initialization rather than assignment

The code in the figure above is finally optimized into the following code:

Test GetObject(Test &t){
	int val=t.getData();
	return Test(val);// Defining temporary objects 2 Test()
}
int main(){
	Test t1;//1.Test()
	Test t2=GetObject(t1);// Using temporary object copy to construct new objects of the same type, the compiler will optimize this process, reducing the construction and Deconstruction of temporary objects on the main stack frame
	return 0;
}
//3.~Test()
//4.~Test()

After optimization, there are only four steps left to construct and destruct

Previous problems in string code

class String {

    friend std::ostream &operator<(const String &str) const {
        return strcmp(_pstr, str._pstr) > 0;
    }

    bool operator

There are many temporary objects in the call. To generate a temporary object, you need to copy and assign the original memory on the stack frame, and delete it once, which is very time-consuming

Add copy construction and assignment function with R-value reference parameter

An R-value reference variable itself is an l-value, so a defined R-value reference variable cannot be assigned to an R-value reference

The copy constructor and assignment overloaded function with R-value reference parameters will point to the memory opened up by the temporary object. There will be no invalid memory release and opening up in the whole process, which greatly improves the operation efficiency

The example code is as follows:

//Copy constructor with right value reference
String(String &&src)  noexcept {
    std::cout<

The output results are as follows:

Copy overloaded objects with lvalue references generally have copy overloaded versions with lvalue references.

Application of custom string class in vector

In push_ In the process of back, if an lvalue is passed in, the temporary object with lvalue parameters will be matched. If a temporary object is passed in, the constructor of the temporary object will be called first, and then the copy constructor with lvalue parameters will be called.

Why push_ Will back call the copy constructor with an R-value reference? Look down\(\Downarrow\)

Move semantic and forward type perfect forwarding

Move () converts an lvalue to an lvalue

Forward () refers to the perfect forwarding of types, which can identify left and right value types

If you define a push that supports calling an R-value reference in your defined vector class_ For the back method, first push_ The parameter of back is a type of right value reference

The first way to write:Using function overloading, respectively define a function whose parameter is an lvalue reference and a function whose parameter is an lvalue reference

void push_back(T &val) {
    if (full()) {
        expend();
    }
    //*_last++ = val;
    _alloctor.construct(_last, val);
    _last++;
}

void push_back(T &&val) {
    if (full()) {
        expend();
    }
    _alloctor.construct(_last, std::move(val));
    _last++;
}

Called in the function clock_alloctor.construct(), the function passes the parameter Val, so the function overload is also required to accept right value references and left value references.

Void construct (t * P, const T & VAL) {// responsible for object construction
    new(p) T(val);// Locate new
}

Void construct (t * P, const T & & VAL) {// responsible for object construction
    new(p) T(std::move(val));// Locate new
}

The second way is to use the type deduction and reference folding of function template

First, explain what reference folding means. If the type deduced by the function template is ty & & + & & (& & after + is a required symbol in the parameter), the folded type of reference is ty & &, which is a right value reference; If the type deduced by the function template is ty & + & &, the type after reference folding is ty &, which is an lvalue reference. Use forward to identify the type of left or right value of ty.

template
void push_ Back (TY & & VAL) {// ty identifies whether the incoming parameter is an lvalue or an lvalue, and then folds the reference
    if (full()) {
        expend();
    }
    _ alloctor. construct(_last, std::forward(val));// Convert Val to the type recognized by ty and avoid function overloading
    _last++;
}
template
Void construct (t * P, ty & & VAL) {// ty identifies whether the incoming parameter is an lvalue or an lvalue, and then folds the reference
    new(p) T(std::forward(val));// Convert Val to the type recognized by ty and avoid function overloading
}

Recommended Today

Implementation of firewall technology by iptables

This is my favorite teacher Yao’s experimental class for us. We have learned a lot of dry goods. Write a blog to record an important step forward on the road of information security learning! In fact, I wanted to continue to learn the vulnerability of eternal blue today, but because I haven’t finished my firewall […]