Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
170 views
in Technique[技术] by (71.8m points)

c++ - 什么是移动语义?(What is move semantics?)

I just finished listening to the Software Engineering radio podcast interview with Scott Meyers regarding C++0x .

(我刚刚结束了对Scott Meyers进行的有关C ++ 0x的Software Engineering广播播客采访 。)

Most of the new features made sense to me, and I am actually excited about C++0x now, with the exception of one.

(大多数新功能对我来说都是有意义的,除了一个功能,我现在对C ++ 0x感到非常兴奋。)

I still don't get move semantics ... What is it exactly?

(我仍然没有移动语义 ……这到底是什么?)

  ask by dicroce translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I find it easiest to understand move semantics with example code.

(我发现用示例代码理解移动语义最简单。)

Let's start with a very simple string class which only holds a pointer to a heap-allocated block of memory:

(让我们从一个非常简单的字符串类开始,该类仅持有指向堆分配的内存块的指针:)

#include <cstring>
#include <algorithm>

class string
{
    char* data;

public:

    string(const char* p)
    {
        size_t size = std::strlen(p) + 1;
        data = new char[size];
        std::memcpy(data, p, size);
    }

Since we chose to manage the memory ourselves, we need to follow the rule of three .

(由于我们选择自己管理内存,因此我们需要遵循三个规则 。)

I am going to defer writing the assignment operator and only implement the destructor and the copy constructor for now:

(我将推迟编写赋值运算符,现在仅实现析构函数和复制构造函数:)

    ~string()
    {
        delete[] data;
    }

    string(const string& that)
    {
        size_t size = std::strlen(that.data) + 1;
        data = new char[size];
        std::memcpy(data, that.data, size);
    }

The copy constructor defines what it means to copy string objects.

(复制构造函数定义复制字符串对象的含义。)

The parameter const string& that binds to all expressions of type string which allows you to make copies in the following examples:

(绑定到string类型的所有表达式的参数const string& that可以在以下示例中进行复制:)

string a(x);                                    // Line 1
string b(x + y);                                // Line 2
string c(some_function_returning_a_string());   // Line 3

Now comes the key insight into move semantics.

(现在是对移动语义的关键了解。)

Note that only in the first line where we copy x is this deep copy really necessary, because we might want to inspect x later and would be very surprised if x had changed somehow.

(请注意,仅在复制x的第一行中才真正需要此深层复制,因为我们可能想稍后再检查x并且如果x有所更改会感到非常惊讶。)

Did you notice how I just said x three times (four times if you include this sentence) and meant the exact same object every time?

(您是否注意到我只是说了三遍x (如果包括这句话,四遍),并且每次都表示完全相同的对象吗?)

We call expressions such as x "lvalues".

(我们称x表达式为“左值”。)

The arguments in lines 2 and 3 are not lvalues, but rvalues, because the underlying string objects have no names, so the client has no way to inspect them again at a later point in time.

(第2行和第3行中的参数不是左值,而是右值,因为基础字符串对象没有名称,因此客户端无法在以后的时间再次检查它们。)

rvalues denote temporary objects which are destroyed at the next semicolon (to be more precise: at the end of the full-expression that lexically contains the rvalue).

(rvalues表示在下一个分号处销毁的临时对象(更精确地说:在词法上包含rvalue的完整表达式的末尾)。)

This is important because during the initialization of b and c , we could do whatever we wanted with the source string, and the client couldn't tell a difference !

(这很重要,因为在bc的初始化过程中,我们可以对源字符串做任何想做的事情,而客户端无法分辨 !)

C++0x introduces a new mechanism called "rvalue reference" which, among other things, allows us to detect rvalue arguments via function overloading.

(C ++ 0x引入了一种称为“ rvalue引用”的新机制,该机制除其他外,使我们能够通过函数重载来检测rvalue参数。)

All we have to do is write a constructor with an rvalue reference parameter.

(我们要做的就是编写一个带有右值引用参数的构造函数。)

Inside that constructor we can do anything we want with the source, as long as we leave it in some valid state:

(在该构造函数中,我们可以对源执行任何操作 ,只要将其保持在某个有效状态即可:)

    string(string&& that)   // string&& is an rvalue reference to a string
    {
        data = that.data;
        that.data = nullptr;
    }

What have we done here?

(我们在这里做了什么?)

Instead of deeply copying the heap data, we have just copied the pointer and then set the original pointer to null (to prevent 'delete[]' from source object's destructor from releasing our 'just stolen data').

(我们没有深度复制堆数据,而是只复制了指针,然后将原始指针设置为null(以防止源对象的析构函数中的'delete []'释放我们的“被盗数据”)。)

In effect, we have "stolen" the data that originally belonged to the source string.

(实际上,我们已经“窃取”了最初属于源字符串的数据。)

Again, the key insight is that under no circumstance could the client detect that the source had been modified.

(同样,关键的见解是,在任何情况下客户都无法检测到源已被修改。)

Since we don't really do a copy here, we call this constructor a "move constructor".

(由于我们实际上并未在此处进行复制,因此我们将此构造函数称为“移动构造函数”。)

Its job is to move resources from one object to another instead of copying them.

(它的工作是将资源从一个对象移动到另一个对象,而不是复制它们。)

Congratulations, you now understand the basics of move semantics!

(恭喜,您现在已经了解了移动语义的基础!)

Let's continue by implementing the assignment operator.

(让我们继续实现赋值运算符。)

If you're unfamiliar with the copy and swap idiom , learn it and come back, because it's an awesome C++ idiom related to exception safety.

(如果您不熟悉复制和交换的习惯用法 ,请学习并回来,因为它是与异常安全性相关的很棒的C ++习惯用法。)

    string& operator=(string that)
    {
        std::swap(data, that.data);
        return *this;
    }
};

Huh, that's it?

(恩,就是这样吗?)

"Where's the rvalue reference?"

(“右值参考在哪里?”)

you might ask.

(你可能会问。)

"We don't need it here!"

(“我们在这里不需要它!”)

is my answer :)

(是我的答案:))

Note that we pass the parameter that by value , so that has to be initialized just like any other string object.

(请注意,我们通过参数that 按值 ,因此that必须要像任何其他字符串对象初始化。)

Exactly how is that going to be initialized?

(究竟如何that要被初始化?)

In the olden days of C++98 , the answer would have been "by the copy constructor".

(在过去的C ++ 98中 ,答案应该是“通过复制构造函数”。)

In C++0x, the compiler chooses between the copy constructor and the move constructor based on whether the argument to the assignment operator is an lvalue or an rvalue.

(在C ++ 0x中,编译器根据赋值运算符的参数是左值还是右值,在复制构造函数和move构造函数之间进行选择。)

So if you say a = b , the copy constructor will initialize that (because the expression b is an lvalue), and the assignment operator swaps the contents with a freshly created, deep copy.

(所以,如果你说a = b拷贝构造函数将初始化that (因为表达b是一个左值),并赋值运算符互换与刚创建,深拷贝的内容。)

That is the very definition of the copy and swap idiom -- make a copy, swap the contents with the copy, and then get rid of the copy by leaving the scope.

(这就是复制和交换惯用法的确切定义-制作一个副本,将内容与该副本交换,然后通过保留范围来摆脱该副本。)

Nothing new here.

(这里没有新内容。)

But if you say a = x + y , the move constructor will initialize that (because the expression x + y is an rvalue), so there is no deep copy involved, only an efficient move.

(但如果你说a = x + y此举构造函数初始化that (因为表达式x + y是一个右值),所以没有深拷贝参与进来,只有一个有效举措。)

that is still an independent object from the argument, but its construction was trivial, since the heap data didn't have to be copied, just moved.

(从参数that它仍然是一个独立的对象,但是它的构造很简单,因为不必复制堆数据,只需移动它即可。)

It wasn't necessary to copy it because x + y is an rvalue, and again, it is okay to move from string objects denoted by rvalues.

(不必复制它,因为x + y是一个右值,并且再次可以从以右值表示的字符串对象中移动。)

To summarize, the copy constructor makes a deep copy, because the source must remain untouched.

(总而言之,复制构造函数会进行深层复制,因为源必须保持不变。)

The move constructor, on the other hand, can just copy the pointer and then set the pointer in the source to null.

(另一方面,move构造函数可以只复制指针,然后将源中的指针设置为null。)

It is okay to "nullify" the source object in this manner, because the client has no way of inspecting the object again.

(可以用这种方式“无效化”源对象,因为客户端无法再次检查对象。)

I hope this example got the main point across.

(我希望这个例子能说明重点。)

There is a lot more to rvalue references and move semantics which I intentionally left out to keep it simple.

(右值引用和移动语义还有很多,我有意省略以保持简单。)

If you want more details please see my supplementary answer .

(如果您需要更多详细信息,请参阅我的补充答案 。)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...