Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

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

c++ - problems with Move constructor and Move overloaded assignment operator?

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

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.

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

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.

I will also refer this in question.

okay Now I will start

Code

.cpp

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

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

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

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

A::A(A &&a)                                   // Move ctor
{
    std::cout<<"Move constructor
";
    
    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
";
    
    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
";
    
    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
";
    
}

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

.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.

1_main

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

Output

2 arg constructor
2 bonapart
Destructor
  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;
    
}

Output

0 arg ctor
2 arg ctor
Move overloade = operator
copy ctor
Dctor
Dctor
2 bonapart
Dctor
  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 (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;
    
}

Output

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

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

so return type must be A, am I right ?

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

See Question&Answers more detail:os

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

1 Answer

0 votes
by (71.8m points)

Your class A has several issues:

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

    A& A::operator=(const A& a)
    {
        std::cout<<"overload operator=
    ";
        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
";
}

A& A::operator=(A&& a)
{
    std::cout << "Move overload operator=
";

    if (this != &a) {
        p = a.p;
        delete [] s;
        s = a.s;
        a.s = nullptr;
    }
    return *this;
}

Now, about

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

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

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

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

With NRVO

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

without NRVO (-fno-elide-constructors)

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

Demo

For

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

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

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.

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

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

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; would fix that issue.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...