【问题标题】:Mechanism of Move Semantic移动语义机制
【发布时间】:2020-03-29 12:41:16
【问题描述】:

我已经开始学习 C++,但我对移动语义有些怀疑。当书中说A的移动构造函数“窃取”了右值B的属性时,是否意味着指向值V的指针从B切换到A?那么这样一来,A就不需要再分配一部分内存了?

如果是,为什么在这段代码中

class CBuffer{
private:
    int size;
    int csize;
    char* ptr;
    char rtp;
public:
    CBuffer(int size): size(size), csize(0){
        ptr = new char[size];
        *ptr = 'a';
    }

    CBuffer(const CBuffer& source): size(3), csize(source.csize) {
        this->ptr = new char[size];
        memcpy(this->ptr, source.ptr, size);
    }

    CBuffer(CBuffer&& source){
        this->ptr = source.ptr;
        this->size = source.size;
        std::cout << &this->ptr << std::endl;
        std::cout << &source.ptr << std::endl;
        source.ptr = nullptr;
    }

};



int main(){
    CBuffer a = CBuffer(1);
    CBuffer b = std::move(a);
    CBuffer c = b;

线条

std::cout << &this->ptr << std::endl;
std::cout << &source.ptr << std::endl;

打印不同的地址?

【问题讨论】:

  • 打印指针的地址,而不是指针的值。
  • &amp;this-&gt;ptr&amp;source.ptr 是指针变量的地址。不是他们指向的对象的地址
  • 为什么 A 在从 B 处“窃取”内存时需要分配更多内存?那只是贪婪。

标签: c++ move-semantics


【解决方案1】:

移动语义意味着我们允许该对象获取该另一个对象内部的任何动态分配的内存,而不是创建另一个对象的副本。

我们通过在两者之间交换任何动态分配的内存来做到这一点,这意味着 (意味着 存储的内存地址在第一个对象中指针的指针)现在将是新对象中指针的,我们放一个nullptr 在右值引用中,所以当它被销毁时,它也会销毁它。

我在代码中添加了一些内容:

  1. 我已经初始化了初始化列表中的所有变量。此外,我还删除了 copy-ctor 中大小的硬编码初始化。
  2. 在复制/移动操作中,其他对象通常使用名称​​other
  3. 错误:必须在 ptr 内放置一个值,不初始化变量是一种不好的做法。实际上,如果我们添加一个 dtor 会导致无效的删除,并在此处使用交换习语(解释如下)。
  4. 最后但并非最不重要的一点是,可以在活动对象上调用 move ctor,而不是删除当前 ptr,而是将 nullptr 放在 other 上,这意味着永远不会删除。例如,如果 main 是:
int main()
{
    CBuffer a{1};
    CBuffer b{2};
    a = std::move(b); // legal, but causes memory leak in your code, the value of a.ptr is never deleted!
}

swap 习语对我们有什么帮助?!

让我们看看我的例子;当我们交换值时(意思是a.ptr = old b.ptrb.ptr = old a.ptr),每当 b 被删除时,现在驻留在 b 中的“旧”a.ptr 将被删除,因为我们只是交换了它们。通常,当我们使用移动语义时,右值很快就会被销毁,我们只是“骑”在它的销毁上以防止内存泄漏。

您的代码应该是这样的(添加了一些打印以便更好地理解):

class CBuffer{
private:
    int size = 0;
    int csize = 0;
    char* ptr = nullptr; // very important!!!
    char rtp = 0;
    int id = 0;

public:
    static int get_current_count() 
    { 
        static int object_cnt = 1;
        return object_cnt++; 
    }

    CBuffer(int size) : 
        size(size), 
        csize(0),
        ptr(new char[size]),
        rtp(0)
    {
        id = get_current_count();
        std::cout << "On ctor of " << id << std::endl;
        ptr[0] = 'a';
    }

    CBuffer(const CBuffer& other) : 
        size(other.size), 
        csize(other.csize), 
        ptr(new char[size]),
        id(get_current_count())
    {
        std::cout << "On copy ctor of " << other.id << " to " << this->id << std::endl;
        std::memcpy(this->ptr, other.ptr, size);
    }

    CBuffer(CBuffer&& other) :
        size(other.size),
        csize(other.csize),
        rtp(other.rtp), 
        id(get_current_count())
    {
        std::cout << "On move ctor of " << other.id << " to " << this->id << std::endl;
        std::swap(this->ptr, other.ptr);
    }

    ~CBuffer()
    {
        std::cout << "On dtor of " << id << std::endl;
        delete[] ptr;
    }
};

int main()
{
    CBuffer a = CBuffer(1);
    CBuffer b = std::move(a);
    CBuffer c = b;
}

输出是:

On ctor of 1
On move ctor of 1 to 2
On copy ctor of 2 to 3
On dtor of 3
On dtor of 2
On dtor of 1

【讨论】:

  • 我认为sourcesrc 也是一个很常见的名称,实际上是对其含义的描述。
  • 我只遇到过其他的,cpp reference也是这样。无论如何,我注意到有些人使用源代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-27
  • 1970-01-01
  • 2013-10-17
  • 2013-11-05
  • 1970-01-01
相关资源
最近更新 更多