Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to understand C++ right value reference and mobile construction

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)05/31 Report--

This article mainly explains "how to understand C++ right value reference and mobile construction", interested friends may wish to have a look. The method introduced in this paper is simple, fast and practical. Now let the editor take you to learn "how to understand C++ right reference and mobile construction"!

The right value reference solves the problem of resource ownership transfer of objects in various situations.

Before September 11, the lack of mobile semantics was one of the most criticized problems on C++. Take a chestnut:

Question 1: how to put an elephant in the refrigerator?

The answer is well known. First of all, you need to have a special refrigerator, which is made to hold elephants. You open the refrigerator door, put the elephant in the refrigerator, and then close the refrigerator door.

Question 2: how to transfer an elephant from one refrigerator to another?

General answer: open the refrigerator door, take out the elephant, close the refrigerator door, open another refrigerator door, put in the elephant, close the refrigerator door.

2B answer: activate the quantum replication system in the second refrigerator, clone an identical elephant, and then turn on a high-energy laser to vaporize and disappear the elephant in the first refrigerator.

Wait, this 2B solution sounds familiar. Isn't that what you do when you want to move an object on C++?

"Mobile" is a concept understood by a three-year-old child. Moving elephants (resources) from one refrigerator to another is so natural that no one would use the bizarre method of copying an elephant and then destroying it. C++ designs the concept of copy / replication for the class through the copy constructor and the copy assignment operator, but in order to move the resource, the caller must copy first and then destruct. Otherwise, you need to implement the interface of the mobile resource yourself.

In order to achieve mobile semantics, the first problem that needs to be solved is how to identify that the resources of objects can be moved. This mechanism must be implemented in a way that has the lowest cost and works for all classes. C++ designers note that in most cases, the objects contained in the right value can be moved safely.

The right value (corresponding to the left value) is a concept that has existed since the design of the C language, but because it is so basic, it is also a concept that is often ignored. Not strictly speaking, the left value corresponds to the location of the corresponding variable, while the right value corresponds to the value of the variable itself (which can also be understood as whether it is persisted or not). The right value in C++ can be assigned to the left value or bound to a reference. The right value of the class is a temporary object that is discarded at the end of the expression if it is not bound to a reference. So we can remove its resources for waste utilization before the right value is discarded, so as to avoid meaningless replication. The right value of the removed resource becomes an empty shell when it is discarded, and the overhead of destructing is reduced.

The feature that the data in the right value can be safely removed makes the right value used to express mobile semantics. When you construct an object with a right value of the same type, you need to pass in parameters as a reference. As the name implies, the right value reference is specifically used to refer to the right value, and the left value reference and the right value reference can be overloaded respectively, ensuring that the left and right values are called to both the semantic implementation of the copy and the move, respectively. For a left value, if we explicitly relinquish ownership of its resources, we can convert it to a right value reference through std::move (). Std::move () is actually a simple wrapper for static_cast ().

Right-value references can at least solve the problem of missing mobile semantics in the following scenarios:

Pass in parameters by value

Passing parameters according to value is the most in line with human thinking. The basic idea is that if you pass in parameters to give the resource to the recipient of the function, you should pass the parameters by value. At the same time, passing parameters by value can be compatible with any left or right value of cv-qualified, which is the best way of compatibility.

Class People {

Public:

People (string name) / / pass in a string by value, and you can receive left and right values. Copy when receiving the left value and move when receiving the right value

: name_ (move (name)) / / explicitly move the construct to move the incoming string into the member variable

{

}

String name_

}

People a ("Alice"); / / Mobile Construction name

String bn = "Bob"

People b (bn); / / copy and construct name

When constructing a, the constructor of the string and the movement constructor of the string are called once. If you use const string& name to receive parameters, there will be a constructor and a copy construct, as well as a destruction of non-trivial. Although it seems painful, and although the compiler has optimizations, semantically passing parameters by value is the best way.

If you want to receive the std::shared_ptr in the constructor and deposit the members of the class (which is very common), then passing in by value is the best choice. Copying a std::shared_ptr requires thread synchronization, and moving a std::shared_ptr is very easy and enjoyable by comparison.

Return by value

Like receiving input parameters, the return value is returned by value is the most in line with human thinking. There were countless functions that had to be written like this in order to return the container.

Void str_split (const string& s, vector* vec); / / A string splitting function defined by value semantics. The delimiter is not considered here, it is assumed that the delimiter is fixed.

This requires that the vec be constructed externally, and the size of the vec is not known at this time. Even if there is a way within the function to predict the size of the vec, because the function is not responsible for constructing the vec, you will probably still need the resize. It is even more painful to make nested calls to such functions, who knows who uses them.

With mobile semantics, it can be written like this

Vector str_split (const string& s) {

Vector v

/ /...

Return v; / / v is the left value, but moves first, and can still be copied when moving is not supported.

}

If the function returns by value, and the return statement directly returns a left-value object on the stack (except for input parameters), the standard requires that the mobile constructor be called first, and the copy constructor be called if it does not match. Although v is a left value, mobile semantics will still be preferred, and the return vector becomes light and light. In addition, compiler optimization is still applicable, whether moved or copied, where possible, but the semantics are not affected.

For std::unique_ptr, this is a blessing.

Unique_ptr create_obj (/ *... * /) {

Unique_ptr ptr (new SomeObj (/ *... * /))

Ptr- > foo (); / / some possible initialization

Return ptr

}

/ / of course there are simpler forms.

Unique_ptr create_obj (/ *... * /) {

Return unique_ptr (new SomeObj (/ *... * /))

}

Such semantics are very common in factory classes. Returning the unique_ptr makes it clear that the ownership of the constructed object is transferred; in particular, the return value of such a factory class can be ignored without causing a memory leak. The above two forms return the left and right values on the stack, respectively, but both apply mobile semantics (unique_ptr does not support copying).

Receive right value expression

When there is no mobile semantics, initializing the object with the value of the expression (for example, a function call) or assigning a value to the object is as follows:

Vector str_split (const string& s)

Vector v = str_split ("1Jing 2je 3"); / / the returned vector is used to copy the construction object v. Request heap memory for v, copy the data, and then destruct temporary objects (free heap memory).

Vector v2

The returned vector is copied to the object v (copy assignment operator). V2 = str_split ("1Magazine 2J 3"). You need to clean up the original data in v2, copy the data in the temporary object to v2, and then destruct the temporary object.

Note: the copy construction call to v may be optimized, although there is still a copy operation semantically.

/ / the same code is better in a world that supports mobile semantics.

Vector str_split (const string& s)

Vector v = str_split ("1Jing 2je 3"); / / the returned vector is used to move the construction object v. V directly removes the heap memory of temporary objects without the need for a new application. After that, the temporary object becomes an empty shell, no longer has any resources, and there is no need to free heap memory when destructing.

Vector v2

The returned vector is moved to the object v (the mobile assignment operator). V2 = str_split ("1Magazine 2p3"). First release the v2 original data, then take the data directly from the return value, and then the return value is destructed.

/ Note: the mobile construction call to v is likely to be optimized, although there is still a move operation semantically.

Needless to say, we know how common and natural the above form is. And there is no explicit use of right-value references here, but the performance improvement is quietly achieved.

Object stored in the container

This problem is similar to the previous constructor parameter passing. The difference is that the parameters are passed separately according to two references. See the push_back function of std::vector.

Void push_back (const T & value); / / (1)

Void push_back (tipped & value); / / (2)

Needless to say, naturally, the left value calls 1 and the right value calls 2. If you want to put a very large object in the container, then version 2 is not a choice.

Vector vv

Vector v = {"123,456"}

V.push_back (789); / / the right value of the temporarily constructed string type is moved into the container v

Vv.push_back (move (v)); / / explicitly move v into vv

Have you washed away the unspeakable secrets that have plagued you for years?

The growth of std::vector

Another covert optimization. When the storage capacity of vector needs to grow, it usually re-applies for a piece of memory and copies the original contents one by one and deletes them. Yes, copy and delete, and move is enough.

For containers like vector, mobile semantics can lead to quiet and beautiful optimization if frequent insertions result in an inevitable increase in storage capacity.

Std::unique_ptr into the container

Once upon a time, because objects were copied as vector grew, non-replicable objects like std::unique_ptr could not be put into the container. But in fact, vector doesn't copy objects, they just "move" them. So with the introduction of mobile semantics, it became a matter of course for std::unique_ptr to put into std::vector.

There are too many benefits to storing std::unique_ptr in a container. Everyone must have written code like this:

MyObj::MyObj () {

For (...) {

Vec.push_back (new T ())

}

/ /...

}

MyObj::~MyObj () {

For (vector::iterator iter = vec.begin (); iter! = vec.end (); + + iter) {

If (* iter) delete * iter

}

/ /...

}

Tedious aside, abnormal safety is also a big problem. With vector, there is no need for explicit destructing at all, and unqiue_ptr takes care of everything. You don't have to write destructors at all. Do you make them?

Unique_ptr is a very lightweight package with storage space equivalent to bare pointers, but it has been secure for a century. In practice, there are few objects (pointers) that need to share ownership, but it is very common that ownership needs to be transferred. The failure of auto_ptr lies in its tedious operation of transferring ownership. Unique_ptr combined with mobile semantics can easily solve the problem of ownership transfer.

Note: if shared ownership is really needed, shared_ptr based on reference counting is a good choice. Shared_ptr can also be moved. Because there is no need for thread synchronization, mobile shared_ptr is lighter than replication.

Transmission of std::thread

Thread is also a typical non-replicable resource, but ownership can be transferred by movement. Similarly, the multithreaded classes such as std::future std::promise std::packaged_task can not be copied and can be passed on movably.

At this point, I believe you have a deeper understanding of "how to understand C++ right reference and mobile construction". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report