7 Replies - 614 Views - Last Post: 25 June 2011 - 05:14 PM Rate Topic: -----

#1 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

C++0x move symantics question(s)

Posted 25 June 2011 - 08:40 AM

I am trying to utilize some C++0x features and I want to make sure my thinking is correct.

Ok lets say I have a class Foo that has some large internal storage. I would like to make an operator+() so that I can do something like:

foo3 = foo1 + foo2;

So the operator would look something like:

Foo Foo::operator+(const Foo& rhs) {
   Foo result;
   //...do whatever here...
   return result;
}


(or should that return a reference? -- I wouldn't think so but at this point I have myself completely confuzzeled)

And then I would also have to implement a move-constructor and move-assignment operator?

// Move constructor.
Foo(Foo&& other)
   : data(NULL)
{
   data = other.data;
   other.data = NULL;
}

// Move assignment operator.
Foo& operator=(Foo&& other)
{
   if (this != &other)
   {
      if(data != NULL) {
          delete[] data;
      }
      data = other._data;
      other._data = NULL;
   }
   return *this;
}



I *think* this is right but I can't be sure.

Second question: if data were a vector rather than an array would I need to worry about the move anyway or would the vector take care of it for me?

Is This A Good Question/Topic? 1
  • +

Replies To: C++0x move symantics question(s)

#2 Martyr2  Icon User is offline

  • Programming Theoretician
  • member icon

Reputation: 4306
  • View blog
  • Posts: 12,079
  • Joined: 18-April 07

Re: C++0x move symantics question(s)

Posted 25 June 2011 - 09:26 AM

Don't doubt yourself. You are right on both cases of the operator overloading. The first one would not return a reference. You would do that if you were doing an assignment operator which you then show correct in the assignment... of course I think you mean something like const Foo& other in the assignment operator function parameter list, not Foo&& other if I remember correctly.

As for your second question, the vector should do it itself. In this case it will call vector's = operator which would be in charge of copying over all elements. At least from what I understand of this situation.

Hope this helps. :)

This post has been edited by Martyr2: 25 June 2011 - 09:27 AM

Was This Post Helpful? 1
  • +
  • -

#3 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: C++0x move symantics question(s)

Posted 25 June 2011 - 09:44 AM

Well my little test is dishartening as the situation I wanted either the move constructor or move assignment called a constructor and assignment operator.

#include <iostream>

using namespace std;

class Integer {
    int iValue;
    static int counter;
    int serial;
    
    public:
    Integer() : iValue(0), serial(counter++) { cout << "ctor: " << serial << endl;  }
    
    explicit Integer(const int& v) : iValue(v), serial(counter++) {
        cout << "ctor(" << iValue << "): " << serial << endl;   
    }
    
    Integer(const Integer& other) : iValue(other.iValue), serial(counter++) { 
        cout << "cctor(" << iValue << "): " << serial <<  " <- Integer: " << other.serial << endl;
    }
    
    Integer(Integer&& other) : iValue(other.iValue), serial(counter++) { 
        cout << "mctor(" << iValue << "): " << serial <<  " <- Integer: " << other.serial << endl;   
    }
    
    Integer& operator=(const Integer& other) {
        cout << "operator=(Integer&): Integer: " << serial << " =  Integer: " << other.serial << endl;
        if(this != &other) {
            iValue = other.iValue;
        }
        return *this;
    }
    
    Integer& operator=(Integer&& other) {
        cout << "operator=(Integer&&): Integer: " << serial << " =  Integer: " << other.serial << endl;
        if(this != &other) {
            iValue = other.iValue;
        }
        return *this;
    }
    
    ~Integer() {
        cout << "destory: " << serial << endl;
    }
    
   
    Integer operator+(const Integer& rhs) const { return Integer(iValue + rhs.iValue); }
    const Integer operator-(const Integer& rhs) const { return Integer(iValue - rhs.iValue); }
    const Integer operator*(const Integer& rhs) const { return Integer(iValue * rhs.iValue); }
    const Integer operator/(const Integer& rhs) const { return Integer(iValue / rhs.iValue); }

    Integer& operator+=(const Integer& rhs) { return (iValue += rhs.iValue, *this);  }
    Integer& operator-=(const Integer& rhs) { return (iValue -= rhs.iValue, *this);  }
    Integer& operator*=(const Integer& rhs) { return (iValue *= rhs.iValue, *this);  }
    Integer& operator/=(const Integer& rhs) { return (iValue /= rhs.iValue, *this);  }

    Integer& operator+=(const int& rhs) { return (iValue += rhs, *this);  }
    Integer& operator-=(const int& rhs) { return (iValue -= rhs, *this);  }
    Integer& operator*=(const int& rhs) { return (iValue *= rhs, *this);  }
    Integer& operator/=(const int& rhs) { return (iValue /= rhs, *this);  }

    Integer& operator++() { ++iValue; return *this; }
    Integer& operator--() { --iValue; return *this; }
    
    //post increment, return a copy of *this before increment
    const Integer operator++(int) { 
        Integer retVal(iValue);
        ++iValue; 
        return retVal; 
    }

    const Integer operator--(int) { 
        Integer retVal(iValue);
        --iValue; 
        return retVal; 
    }


    const Integer operator=(const int& rhs) {
        iValue = rhs;
        return *this;
    }
    
    operator int() const {
        return iValue;
    }
    
    friend ostream& operator<<(ostream& out, const Integer& rhs);
};

int Integer::counter = 0;

ostream& operator<<(ostream& out, const Integer& rhs) { return out << rhs.iValue; }


int main() {
    Integer i0 = Integer(5);
    i0 = Integer(10); //Move
    
    Integer i2 = i0;
    Integer i3;
    i3 = i0 + i2;
    cout << "ending" << endl;
    return 0;
}

Was This Post Helpful? 0
  • +
  • -

#4 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2085
  • View blog
  • Posts: 3,170
  • Joined: 21-June 11

Re: C++0x move symantics question(s)

Posted 25 June 2011 - 09:47 AM

View PostMartyr2, on 25 June 2011 - 06:26 PM, said:

of course I think you mean something like const Foo& other in the assignment operator function parameter list, not Foo&& other if I remember correctly.


If he used a const-reference, he wouldn't be allowed to set other's data to null (which would cause the memory to be freed when other's destructor runs, which would leave him with a dead pointer). Foo&& other is exactly what he wants - this is precisely the situation for which rvalue-reference were invented.
Was This Post Helpful? 1
  • +
  • -

#5 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: C++0x move symantics question(s)

Posted 25 June 2011 - 09:50 AM

wait... I had the operator+ wrong... It should have been:
    Integer operator+(const Integer& rhs) const { 
        Integer retVal;
        retVal.iValue = iValue + rhs.iValue;
        return retVal;
    }



but now I am getting more objects created... output is:
> "C:\CProjects\Forum Help\movesymanticsTest.exe " 
ctor(5): 0
ctor(10): 1
operator=(Integer&&): Integer: 0 =  Integer: 1
destory: 1
cctor(10): 2 <- Integer: 0
ctor: 3
ctor: 4
mctor(20): 5 <- Integer: 4
destory: 4
operator=(Integer&&): Integer: 3 =  Integer: 5
destory: 5
ending
destory: 3
destory: 2
destory: 0


ps. destory should be destroy but whatever.

Quote

If he used a const-reference, he wouldn't be allowed to set other's data to null (which would cause the memory to be freed when other's destructor runs, which would leave him with a dead pointer).
exactly. The move constructor and move assignment operator can't accept constant references because they must modify the "other" object.
Was This Post Helpful? 0
  • +
  • -

#6 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2085
  • View blog
  • Posts: 3,170
  • Joined: 21-June 11

Re: C++0x move symantics question(s)

Posted 25 June 2011 - 09:57 AM

View PostNickDMax, on 25 June 2011 - 06:44 PM, said:

Well my little test is dishartening as the situation I wanted either the move constructor or move assignment called a constructor and assignment operator.


I'm not quite sure what you find disheartening here. If I run your code, the move assignment operator is called twice. Once for i0 = Integer(10) and once for i3 = i0 + i2;. Is this not the result you want?
Was This Post Helpful? 1
  • +
  • -

#7 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: C++0x move symantics question(s)

Posted 25 June 2011 - 10:00 AM

So while the results were not quite what I expected (I don't think I really expected BOTH the move constructor and the move assignment to be called in one return statement) I can visualize what is happening and that is ok.

retVal in operator+() is Integer 4 and is created with a basic ctor
Integer 5 is a temp value created to "return" on the stack -- it is created with the mctor
Then Integer 5 is moved to Integer 3 (i3 in main())

So yea my thinking was correct.

As for the second question. I am just not sure. Can anyone think of a way I can prove it to myself. Maybe if I look at &vec[0] as it moves around...

View Postsepp2k, on 25 June 2011 - 12:57 PM, said:

View PostNickDMax, on 25 June 2011 - 06:44 PM, said:

Well my little test is dishartening as the situation I wanted either the move constructor or move assignment called a constructor and assignment operator.


I'm not quite sure what you find disheartening here. If I run your code, the move assignment operator is called twice. Once for i0 = Integer(10) and once for i3 = i0 + i2;. Is this not the result you want?


No it is what I wanted, I just didn't expect the move constructor on return.
Was This Post Helpful? 0
  • +
  • -

#8 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: C++0x move symantics question(s)

Posted 25 June 2011 - 05:14 PM

Maybe I am doing something wrong but the vectors in my test do not seem to be moving rather they are copying. (sorry about all the un-related code

#include <iostream>
#include <vector>

using namespace std;

class Integer {
    int iValue;
    static int counter;
    int serial;
    vector<int> data;
    public:
    Integer() : iValue(0), serial(counter++) { 
        cout << "ctor: " << serial << endl;
        data.resize(1000);
    }
    
    explicit Integer(const int& v) : iValue(v), serial(counter++) {
        cout << "ctor(" << iValue << "): " << serial << endl;   
        data.resize(1000);
    }
    
    Integer(const Integer& other) : iValue(other.iValue), serial(counter++) { 
        cout << "cctor(" << iValue << "): " << serial <<  " <- Integer: " << other.serial << endl;
        cout << "cctor: " << serial << " before assign data.getAddy() = " << &data[0] << endl;
        data = other.data;
        cout << "cctor: " << serial << " after assign data.getAddy() = " << &data[0] << endl;
    }
    
    //Integer(Integer&& other) : iValue(other.iValue), serial(counter++) { 
    //    cout << "mctor(" << iValue << "): " << serial <<  " <- Integer: " << other.serial << endl;   
    //}
    
    const int* getAddy() const { return &data[0]; }
    
    Integer& operator=(const Integer& other) {
        cout << "operator=(Integer&): Integer: " << serial << " =  Integer: " << other.serial << endl;
        if(this != &other) {
            iValue = other.iValue;
            cout << "assign: " << serial << " before assign data.getAddy() = " << &data[0] << endl;
            data = other.data;
            cout << "assign: " << serial << " after assign data.getAddy() = " << &data[0] << endl;            
        }
        
        return *this;
    }
    
    //Integer& operator=(Integer&& other) {
    //    cout << "operator=(Integer&&): Integer: " << serial << " =  Integer: " << other.serial << endl;
    //    if(this != &other) {
    //        iValue = other.iValue;
    //    }
    //    return *this;
    //}
    //
    ~Integer() {
        cout << "destroy: " << serial << endl;
    }
      
   
    Integer operator+(const Integer& rhs) const { 
        Integer retVal;
        retVal.iValue = iValue + rhs.iValue;
        return retVal;
    }

};

int Integer::counter = 0;

int main() {
    Integer i0 = Integer(5);
    i0 = Integer(10); //Move
    
    Integer i2 = i0;
    Integer i3;
    cout << "i3.getAddy() = " << i3.getAddy() << endl;
    i3 = i0 + i2;
    cout << "i3.getAddy() = " << i3.getAddy() << endl;
    cout << "ending" << endl;
    return 0;
}


output:
> "C:\CProjects\Forum Help\movesymanticsTest2.exe " 
ctor(5): 0
ctor(10): 1
operator=(Integer&): Integer: 0 =  Integer: 1
assign: 0 before assign data.getAddy() = 00568D10
assign: 0 after assign data.getAddy() = 00568D10
destroy: 1
cctor(10): 2 <- Integer: 0
cctor: 2 before assign data.getAddy() = 00000000
cctor: 2 after assign data.getAddy() = 0045D838
ctor: 3
i3.getAddy() = 00569CB8
ctor: 4
cctor(20): 5 <- Integer: 4
cctor: 5 before assign data.getAddy() = 00000000
cctor: 5 after assign data.getAddy() = 0056BC08
destroy: 4
operator=(Integer&): Integer: 3 =  Integer: 5
assign: 3 before assign data.getAddy() = 00569CB8
assign: 3 after assign data.getAddy() = 00569CB8
destroy: 5
i3.getAddy() = 00569CB8
ending
destroy: 3
destroy: 2
destroy: 0

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1