移动构造函数和移动重载赋值运算符的问题? [英] problems with Move constructor and Move overloaded assignment operator?

查看:74
本文介绍了移动构造函数和移动重载赋值运算符的问题?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

主要由 fredoverflow(用户 237K 代表)在他的两个答案中解释的所有内容

Mostly all things explained by fredoverflow(user 237K Rep.) in his Two answers

但是在实现移动构造函数和重载移动赋值运算符(OMAO)(我在整个问题中都使用这些简短形式)时,我遇到了一些问题,我将放在此处.

But while implementing Move constructor and overloaded Move Assignment operator(OMAO)( I am using these short form throughout the question ) I am facing some problem that I will put here.

用户 Greg Hewgill(拥有 826K 代表的用户)还有另一个答案https://stackoverflow.com/a/3106136/11862989his
我引用他的话,

Also there is another answer by user Greg Hewgill (user with 826K Rep.) https://stackoverflow.com/a/3106136/11862989his
I am Quoting him,

假设你有一个返回实体对象的函数,然后是一个普通的 C++编译器将为multiply()的结果创建一个临时对象,调用复制构造函数初始化r,然后析构临时返回值.C++0x 中的移动语义允许移动构造器"被调用以通过复制其内容来初始化 r,以及然后丢弃临时值而不必破坏它.

Suppose you have a function that returns a substantial object then an ordinary C++ compiler will create a temporary object for the result of multiply(), call the copy constructor to initialize r, and then destruct the temporary return value. Move semantics in C++0x allow the "move constructor" to be called to initialize r by copying its contents, and then discard the temporary value without having to destruct it.

我也会参考这个问题.

好的,我要开始了

#include"34_3.h"
#include<iostream>
#include<conio.h>
#include<cstring>

A::A()                                       // O arg ctor
{
    std::cout<<"0 arg constructor\n";
    p=0;
    s=nullptr;
        
}

A::A(int k1,const char *str)                 // 2 arg ctor
{
    std::cout<<"2 arg constructor\n";
    
    p=k1;
    s=new char[strlen(str)+1];
    strcpy(s,str);
    
}

A::A(const A &a)                             // copy ctor
{
    std::cout<<"copy constructor\n";
    
    p=a.p;
    s=new char[strlen(a.s)+1];
    strcpy(s,a.s);
    
}

A::A(A &&a)                                   // Move ctor
{
    std::cout<<"Move constructor\n";
    
    p=a.p;
    s=new char[strlen(a.s)+1];
    strcpy(s,a.s);
    a.s=nullptr;
    
}


A& A::operator=(const A &a)                 // Overloaded assignement opeator `OAO`
{
    std::cout<<"overloade= operator\n";
    
    p=a.p;
    s=new char[strlen(a.s)+1];
    strcpy(s,a.s);
    return *this;
    
}


A& A::operator=(A &&a)                        // `OMAO`
{
    std::cout<<"Move overloade = operator\n";
    
    p=a.p;
    s=new char[strlen(a.s)+1];
    strcpy(s,a.s);
    a.s=nullptr;
    return *this;
    
}

A::~A()                                       // Dctor
{
    delete []s;
    std::cout<<"Destructor\n";
    
}

void A::display()
{
    std::cout<<p<<" "<<s<<"\n";
    
}

.h

#ifndef header
#define header

struct A
{
    private:
        int p;
        char *s;
    public:
        A();                            //  0 arg ctor
        A(int,const char*);             //  2 arg ctor
        A(const A&);                    //  copy ctor
        A(A&&);                         //  Move ctor
        
        A& operator=(const A&);         // `OAO`
        A& operator=(A&&);              // `OMAO`
        
        ~A();                           // dctor
        
        void display(void);
        
};
#endif

我将几个主要函数及其输出放在这里,以便我可以轻松地讨论问题.

I am putting few main functions and their outputs here so I can discuss the problem easily.

A make_A();
int main()
{
    A a1=make_A();
    
    a1.display();
    
}
A make_A()
{
    A a(2,"bonapart");
    return a;
    
}

输出

2 arg constructor
2 bonapart
Destructor

  1. 为什么它不执行 Move 构造函数,但是如果我注释掉 .cpp 文件中的 Move 构造函数定义和 .h 文件中的声明,那么它会给出错误 [Error] no matching function for call to 'A::A(A)' 并且如果我使用这个 A a1=std::move(make_A()); 然后移动构造函数调用,那么为什么会发生这种情况?
  2. 为什么 make_A() 函数中对象 a 的析构函数没有运行?
  1. why it is not executing Move constructor but if I commented out Move constructor definition in .cpp file and declaration in .h file then it give error [Error] no matching function for call to 'A::A(A)' and if I use this A a1=std::move(make_A()); then Move constructor calls, So why this happening ?
  2. Why destructor for object a in make_A() function is not running ?

2_main()

A make_A();
int main()
{
    A a1;
    a1=make_A();
    
    a1.display();
    
}
A make_A()
{
    A a(2,"bonapart");
    return a;
    
}

输出

0 arg ctor
2 arg ctor
Move overloade = operator
copy ctor
Dctor
Dctor
2 bonapart
Dctor

  1. 现在这里复制构造函数和析构函数运行由于从 Move 重载 = 运算符函数返回 *this 而创建的临时对象.根据 Greg Hewgill 声明 C++ 0x 允许调用 Move 构造函数以通过复制其内容来初始化,然后丢弃临时值而不必破坏它.我正在使用 C++11 但仍然通过创建临时对象、复制构造函数来完成初始化.
  2. 我不知道第二个析构函数正在运行哪个对象?
  1. Now here copy constructor and destructor runs for temporary object created due to return *this from Move overload = operator function. According to Greg Hewgill statement C++ 0x allows Move constructor to be called to initialize by copying it's contents and then discard the temporary value without having to destruct it. I am using C++11 but still initializing is done by creating temporary object, copy constructor.
  2. I am not getting for which object that 2nd destructor is running?

3_main

fredoverflow(用户 237K 代表)保留了 Move 重载运算符 A& 的返回类型,但我认为这是错误的.

3_main

fredoverflow (user 237K Rep.) kept return type of Move overloaded operators A& but I think it is wrong.

A make_A();
int main()
{
    A a1,a2;
    a2=a1=make_A();
    
    a1.display();
    a2.display();
    
}
A make_A()
{
    A a(2,"bonapart");
    return a;
    
}

输出

[Error] prototype for 'A& A::operator=(A&&)' does not match any in class 'A'

所以我觉得返回类型应该是 A&&AA&& 也给出错误 [ERROR] 不能将左值绑定到 a&&

so I feel return type should be A&& or A but A&& too give error [ERROR] can't bind a lvalue to a&&

所以返回类型必须是A,对吗?

so return type must be A, am I right ?

在 Move 构造函数和 Move 重载 = 操作符中我使用了 as=nullptr; 这个语句总是在 Move 语义中使用 fredoverflow(user) 解释了类似现在源不再拥有它的对象"的内容;但我不明白.因为如果我不写这个语句仍然没有问题一切正常.请解释这一点

In Move constructor and Move overloaded = operator I used a.s=nullptr; This statement is always used in Move semantics fredoverflow(user) explained something like "now the source no longer owns the object it" but I am not getting it. Because if I did not write this statement still no problem everything works fine. please explain this point

推荐答案

你的班级A有几个问题:

  • 您的赋值运算符不处理自赋值和泄漏:

  • Your assignment operator don't handle self assignment and leak:

A& A::operator=(const A& a)
{
    std::cout<<"overload operator=\n";
    if (this != &a)
    {
        p = a.p;
        delete[] s;
        s = new char[strlen(a.s) + 1];
        strcpy(s, a.s);
    }
    return *this;
}

  • 你的动作不是移动而是复制:

  • Your move doesn't move but copy:

    A::A(A&& a) : p(a.p), s(a.s)
    {
        a.s = nullptr;
        std::cout << "Move constructor\n";
    }
    
    A& A::operator=(A&& a)
    {
        std::cout << "Move overload operator=\n";
    
        if (this != &a) {
            p = a.p;
            delete [] s;
            s = a.s;
            a.s = nullptr;
        }
        return *this;
    }
    

    现在,关于

    A make_A()
    {
        A a(2,"bonapart"); // Constructor
        return a;
    }
    

    由于潜在的复制省略(NRVO),有几种情况(gcc 有标记为 -fno-elide-constructors 来控制)

    There are several scenario because of potential copy elision (NRVO) (gcc has flag as -fno-elide-constructors to control that)

    如果 NRVO 适用,则 a 是构造就地";所以不会发生额外的破坏/移动;

    if NRVO apply, then a is construct "in-place" so no extra destruction/move happens;

    否则有一个移动构造函数和a的销毁.

    else there is a move constructor and the destruction of a.

    A make_A()
    {
        A a(2,"bonapart"); // #2 ctor(int const char*)
        return a; // #3 move (elided with NRVO)
    } // #4 destruction of a, (elided with NRVO)
    
    int main()
    {
        A a1; // #1: default ctor
        a1 = // #5: Move assignment (done after make_A)
          make_A(); // #6: destructor of temporary create by make_A
    
        
        a1.display();
    } // #8: destructor of a1
    

    使用 NRVO

    default ctor
    ctor(int const char*)
    move assignment
    destructor
    display
    destructor
    

    没有 NRVO (-fno-elide-constructors)

    default ctor
    ctor(int const char*)
    move ctor
    destructor
    move assignment
    destructor
    display
    destructor
    

    演示

    为了

    A a1,a2;
    a2 = a1 = make_A();
    

    a1 = make_A(); 使用移动赋值.a2 = (a1 = make_A()) 使用复制赋值作为移动赋值返回(正确)A&

    a1 = make_A(); use move assignment. a2 = (a1 = make_A()) use copy assignment as move assignment returns (correctly) A&

    4在 Move 构造函数和 Move 重载 = 操作符中,我使用了 a.s=nullptr; 这个语句总是在 Move 语义中使用 fredoverflow(user) 解释了类似现在源不再拥有它的对象"之类的东西.但我不明白.因为如果我不写这个语句仍然没有问题一切正常.请解释这一点

    4 In Move constructor and Move overloaded = operator I used a.s=nullptr; This statement is always used in Move semantics fredoverflow(user) explained something like "now the source no longer owns the object it" but I am not getting it. Because if I did not write this statement still no problem everything works fine. please explain this point

    你的问题是你复制而不是移动.

    Your issue is that you do copy instead of move.

    如果你做 s = a.s; 而不是复制

    If you do s = a.s; instead of the copy

    s = new char[strlen(a.s) + 1];
    strcpy(s, a.s);
    

    那么 this->sas 都指向相同的数据,thisa> 会在它们的析构函数中释放(相同的)内存 ->双重免费错误.

    then both this->s and a.s would point of same data, and both this and a would free the (same) memory in their destructor -> double free error.

    a.s = nullptr; 可以解决这个问题.

    这篇关于移动构造函数和移动重载赋值运算符的问题?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆