New blog location
New blog location
The following simple class will be used to illustrate move semantics and object lifecycle. std::move
creates rvalue reference
Float.h
#pragma once class Float { public: Float(float f); //constructor Float(const Float& f); //copy constructor Float(Float&& f) noexcept; //move constructor Float& operator=(const Float& rhs); //copy assignment operator Float& operator=(Float&& rhs) noexcept; //move assignment operator ~Float(); //destructor float getValue() const; //getter void setValue(float f); //setter friend void swap(Float& first, Float& second) noexcept ; //copy helper function void moveFrom(Float& src) noexcept; //move helper function. Note param is lvalue reference float *m_flt; //pointer to heap based float };
Float.cpp
#include <iostream> #include <utility> #include "Float.h" using namespace std; Float::Float(float f) { m_flt = new float(f); printf("Float(float f) 0x%x\n", this); } Float::~Float() { printf("~Float() 0x%x\n", this); delete m_flt; } Float::Float(const Float& f) { this->m_flt = new float(f.getValue()); printf("Float(const Float& f) 0x%x\n", this); } float Float::getValue() const { return *m_flt; } void Float::setValue(float f) { *m_flt = f; } Float::Float(Float&& f) noexcept { m_flt = f.m_flt; f.m_flt = nullptr; printf("Float(Float&& f) 0x%x\n", this); } Float& Float::operator=(const Float& rhs) { if (this == &rhs) return *this; Float temp(rhs); swap(temp, *this); printf("Float::operator=(const Float& rhs) 0x%x\n", this); return *this; } void swap(Float& first, Float& second) noexcept { using std::swap; swap(first.m_flt, second.m_flt); } Float& Float::operator=(Float&& rhs) noexcept { moveFrom(rhs); printf("Float::operator=(Float&& rhs) 0x%x\n", this); return *this; } void Float::moveFrom(Float& src) noexcept { m_flt = src.m_flt; src.m_flt = nullptr; }
The following is the code that makes use of the Float
class:
#include <iostream> #include "Float.h" using namespace std; void DoIt(Float p) //H { cout << "Entered DoIt" << endl; cout << "Doit: Float is " << p.getValue() << "\n"; } Float add(Float a, Float b) //Float a is C, Float b is D { auto x = std::move(a); //E auto y = std::move(b); //F return (x.getValue() + y.getValue()); //return(...) is G } int main() { Float f(3.0); //A Float g(4.0); //B DoIt(add(f, g)); return 0; }
Output:
Float(float f) 0xac4ff7d0 //ctor A
Float(float f) 0xac4ff7c8 //ctor B
Float(const Float& f) 0xac4ff798 //copy ctor C
Float(const Float& f) 0xac4ff790 //copy ctor D
Float(Float&& f) 0xac4ff7b8 //move ctor E
Float(Float&& f) 0xac4ff7c0 //move ctor F
Float(float f) 0xac4ff7a0 //object G constructed
~Float() 0xac4ff7c0 //F destroyed at end of add
~Float() 0xac4ff7b8 //E destroyed at end of add
~Float() 0xac4ff790 //D destroyed end of add
~Float() 0xac4ff798 //C destroyed, end of add
Entered DoIt
Doit: Float is 7
~Float() 0xac4ff7a0 //object G destroyed
~Float() 0xac4ff7c8 //B destroyed
~Float() 0xac4ff7d0 //A destroyed
Avoiding copies when calling add
. Call std::move
to create rvalue reference when calling add
:
#include <iostream> #include "Float.h" using namespace std; void DoIt(Float p) //Float p is F { cout << "Entered DoIt" << endl; cout << "Doit: Float is " << p.getValue() << "\n"; } Float add(Float a, Float b) //Float a is C, Float b is D { return (a.getValue() + b.getValue()); //E } int main() { Float f(3.0); //A Float g(4.0); //B DoIt(add(std::move(f), std::move(g))); return 0; }
Output:
Float(float f) 0x858ff6f0 //ctor for A
Float(float f) 0x858ff6e8 //ctor for B
Float(Float&& f) 0x858ff6d0 //move ctor for C,
Float(Float&& f) 0x858ff6c8 //move ctor for D
Float(float f) 0x858ff6c0 //temporary for E
~Float() 0x858ff6c8 //destroy D at end of add
~Float() 0x858ff6d0 //destroy C at end of add
Entered DoIt
Doit: Float is 7
~Float() 0x858ff6c0 // destroy temporary for E
~Float() 0x858ff6e8 //destroy B
~Float() 0x858ff6f0 //destroy A
Move assignment operator being used:
#include <iostream> #include "Float.h" using namespace std; void DoIt(Float p) { cout << "Entered DoIt" << endl; cout << "Doit: Float is " << p.getValue() << "\n"; } Float add(Float a, Float b) { return (a.getValue() + b.getValue()); } int main() { Float f(3.0); Float g(4.0); Float k(5.0); g = std::move(k); DoIt(add(std::move(f), std::move(g))); return 0; }
Output:
Float(float f) 0x772ffa68
Float(float f) 0x772ffa58
Float(float f) 0x772ffa60
Float::operator=(Float&& rhs) 0x772ffa58 //g = std::move(k)
Float(Float&& f) 0x772ffa40
Float(Float&& f) 0x772ffa38
Float(float f) 0x772ffa30
~Float() 0x772ffa38
~Float() 0x772ffa40
Entered DoIt
Doit: Float is 8
~Float() 0x772ffa30
~Float() 0x772ffa60
~Float() 0x772ffa58
~Float() 0x772ffa68
Comments
Post a Comment