我研究了 c++ 11 move 语义,我有这样一个问题。

例如:

如果我们有一个

vector<T> vt; // also assume that T have pointers on data in separate memory
vt.push_back(...);

假设 vt 缺少未使用的容量。然后在c++ 98中,它将为每个带有它们指向的数据的T对象分配更多内存和复制(调用构造函数复制)。

例如:

t1 -> t1 数据 => 将被复制 t_cop1 -> (t1_cop1 数据)
t2 -> t2 数据 => t_cop2 -> (t2_cop2 数据)

C++ 11 move 语义允许 move 所有 T 对象(只复制一个指针,但不要通过调用 move 构造函数复制单独内存中的数据)。

所以问题是为什么我们不能在 C++98 中做同样的事情,为什么我们不能实现 push_back 只是为了复制内存(其中包含指针 t1、t2,所以 t_cop1 和 t2_cop2 将使用相同的指针创建)然后释放它(例如使用 free(void*) 的 t1 和 t2)?

UPD:好的,我会试着用更简单的例子来解释我的问题:

例如,如果我们有一个包含指向某个数据结构 Data 的指针的类 A。
我们实现了一个复制构造函数来复制指针“数据”
class A {
public:
    A() {};
    A(const A& a) {
        data = a.data;
    }
private:
    struct Data{};
    Data* data;
};

// And I want to implement "move semantics" by calling the copy constructor (that copies just pointer)

A* a = new A();
A* b(a);

// and then somehow free "a" object (but not call the destructor)
free(a); // or a = NULL

据我了解,这就是 move 语义几乎要做的事情(但保持 a 处于某种一致状态)。所以我认为我们实际上有能力在旧的 C++ 中实现 move 。不是吗?

我们也可以在前面的例子中用 vector 做同样的事情

最佳答案

“move 语义”为编译器提供了一种方法来传递对构造函数的引用,这些构造函数可以区分为左值引用(你习惯的那种,在 C++98 中找到)和右值引用(C++11 的新特性) ,使用 && 表示法,它们指的是临时和中间的对象,并且可能不会持续到当前语句之后)。

所以现在我们可以编写一个复制构造函数(“使新对象成为这个对象的拷贝”)和一个不同的 move 构造函数(“使新对象成为这个对象的拷贝,顺便说一下,源对象是一个临时对象接下来将被销毁,因此如果您想以一种不保留源对象完整的方式进行优化,请继续”)。

因此,例如,在容器类中,您可以使用复制构造函数复制容器类,新拷贝将包含其所有内容的完整拷贝(这意味着将发生内容的逐个元素复制构造之类的事情)。但是 move 构造函数可以跳过所有这些工作并简单地与源容器交换内部结构,只需接管源容器的内容并将源容器留空。 (稍后源容器被销毁,这应该是微不足道的,因为它是空的。)这通常可以在恒定时间内完成,而不是 O(N) 或更糟。

你不能在 C++98 中做等效的构造函数(因此使用临时对象),因为它只知道如何制作拷贝。有一种方法可以通过 swap() 函数(和某些类中的方法)实现类似的效果,但必须明确编写。 (基本上,要将容器 a 的内容 move 到新构造的空容器 b 中,您需要调用 swap(a, b); ,然后销毁 a ,然后该容器将为空。)

在 C++98 中你不能做正确事情的一个例子是用返回 vector 的函数的结果初始化 vector :

vector<int> fibonacci(int n) {
    vector<int> result;
    if(n <= 0) return result;
    result.push_back(0);
    if(n <= 1) return result;
    result.push_back(1);
    for(int i = 1; i < n; ++i)
        result.push_back(result[i] + result[i - 1]);
    return result;
}

vector<int> datavect = fibonacci(10);

C++98 和 C++11 都会省略函数返回拷贝(在存储返回值的位置创建 result),但 C++98 只能调用 datavect 的复制构造函数,因为没有 move 语义。 C++11 可以调用 move 构造函数,它可以将存储从临时返回值 move 到 datavect 。在 C++98 中没有真正的方法可以在不涉及 operator newoperator delete 并返回指针而不是对象的情况下做到这一点,在(非确定性时间)免费存储上无利可图。

关于C++ 11 move 语义与 C++ 98,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24423438/

10-11 23:03
查看更多