I have the following class in C++
class.h
typedef std::complex<float> fcomp;
class wf {
public:
int nx, ny, nf; //dimensions
size_t wfSize;
fcomp * val; //data
//constructors
wf(int nx, int ny, int nf);
wf(int nx, int ny, int nf, fcomp * data);
//copy constructor
wf(const wf & wfOther);
//copy assignment operators
wf & operator=(wf & wfOther);
wf & operator=(const wf & wfOther);
//move constructor
wf(wf && wfOther);
//move assignment operator
wf & operator=(wf && wfOther);
//destructor
~wf();
void dispInfo();
inline wf & operator+=(const wf & wfOther);
};
wf operator+(const wf & a, const wf & b);
and all the definitions in the script file class.c.
#include "class.h"
//----------------------------------------------------------------------------
wf::wf(int nx, int ny, int nf)
: nx(nx), ny(ny), nf(nf) {
#ifdef debugClasses
std::cout << "wf: constructor 1" << std::endl;
#endif
wfSize = nf*nx*ny;
val = new fcomp[wfSize];
}
//----------------------------------------------------------------------------
wf::wf(int nx, int ny, int nf, fcomp * data)
: nx(nx), ny(ny), nf(nf){
#ifdef debugClasses
std::cout << "wf: constructor 2" << std::endl;
#endif
wfSize = nf*nx*ny;
val = new fcomp[wfSize];
for(unsigned int i=0; i<wfSize; i++)
val[i] = data[i];
}
//----------------------------------------------------------------------------
wf::wf(const wf & wfOther)
: nx(wfOther.nx), ny(wfOther.ny), nf(wfOther.nf),
wfSize(wfOther.wfSize) {
#ifdef debugClasses
std::cout << "wf: copy constructor" << std::endl;
#endif
val = new fcomp[wfSize];
memcpy(val, wfOther.val, wfSize*sizeof(fcomp));
}
//----------------------------------------------------------------------------
wf & wf::operator=(const wf & wfOther){
#ifdef debugClasses
std::cout << "wf: copy assignment operator 1" << std::endl;
#endif
nx = wfOther.nx;
ny = wfOther.ny;
nf = wfOther.nf;
wfSize = wfOther.wfSize;
val = new fcomp[wfSize];
memcpy(val, wfOther.val, wfSize*sizeof(fcomp));
return *this;
}
//----------------------------------------------------------------------------
wf & wf::operator=(wf & wfOther){
#ifdef debugClasses
std::cout << "wf: copy assignment operator 2" << std::endl;
#endif
nx = wfOther.nx;
ny = wfOther.ny;
nf = wfOther.nf;
wfSize = wfOther.wfSize;
val = new fcomp[wfSize];
memcpy(val, wfOther.val, wfSize*sizeof(fcomp));
return *this;
}
//----------------------------------------------------------------------------
wf::wf(wf && wfOther)
: nx(wfOther.nx), ny(wfOther.ny), nf(wfOther.nf),
wfSize(wfOther.wfSize) {
#ifdef debugClasses
std::cout << "wf: move constructor" << std::endl;
#endif
val = wfOther.val;
wfOther.val = nullptr;
wfOther.nx = 0;
wfOther.ny = 0;
wfOther.nf = 0;
wfOther.wfSize = 0;
}
//----------------------------------------------------------------------------
wf & wf::operator=(wf && wfOther){
#ifdef debugClasses
std::cout << "wf: move assignment operator" << std::endl;
#endif
if(this != &wfOther){
delete [] val;
val = wfOther.val;
nx = wfOther.nx;
ny = wfOther.ny;
nf = wfOther.nf;
wfSize = wfOther.wfSize;
wfOther.val = nullptr;
wfOther.nx = 0;
wfOther.ny = 0;
wfOther.nf = 0;
wfOther.wfSize = 0;
}
return *this;
}
//----------------------------------------------------------------------------
wf::~wf(){
#ifdef debugClasses
std::cout << "wf: destructor" << std::endl;
#endif
delete [] val;
}
//----------------------------------------------------------------------------
void wf::dispInfo(){
std::cout << "nx = " << nx << std::endl;
std::cout << "ny = " << ny << std::endl;
std::cout << "nf = " << nf << std::endl;
std::cout << "wfSize = " << wfSize << std::endl;
}
//----------------------------------------------------------------------------
inline wf & wf::operator+=(const wf & wfOther){
this->nx += wfOther.nx;
this->ny += wfOther.ny;
this->nf += wfOther.nf;
this->wfSize += wfOther.wfSize;
return *this;
}
//----------------------------------------------------------------------------
wf operator+(const wf & a, const wf & b){
#ifdef debugClasses
std::cout << "wf: classes addition" << std::endl;
#endif
wf c(a.nx,a.ny,a.nf);
if( a.nx != b.nx || a.ny != b.ny || a.nf != b.nf ){
std::cout << "wf: invalid classes addition\n";
throw "invalid-operation";
exit(1);
}
for(unsigned int i=0; i<c.wfSize; i++)
c.val[i] = a.val[i] + b.val[i];
return c;
}
Since my class has a pointer variable *val
, I define explicitly my constructors, the destructor and also the copy/move constructor and copy/move assignment operators.
I have the following piece of code in my main program:
main.c
int main(int argc, char * argv[]){
wf wfA(8,8,4);
for(unsigned int i=0; i<wfA.wfSize; i++){
float fl = (float)(i);
wfA.val[i] = fcomp(fl,0.0);
}
wf wfB(wfA);
wf wfC = std::move(wfA + wfB);
wfC.dispInfo();
for(unsigned int i=0; i<5; i++)
std::cout << wfC.val[i] << " ";
return 0;
}
so essentially I create a class object wfA
using constructor 1
and then create an object wfB
using the copy constructor. Finally, I add these to classes
and create a new object wfC
from their addition. To make this optimal (at least according to my understanding) I use std::move().
The output of the above main program is:
wf: constructor 1
wf: copy constructor
wf: classes addition
wf: constructor 1
wf: move constructor
wf: destructor
nx = 8
ny = 8
nf = 4
wfSize = 256
(0,0) (2,0) (4,0) (6,0) (8,0)
wf: destructor
wf: destructor
wf: destructor
this result is what I expected.
i.e. there are two calls to contructor 1, one for the obect wfA
and one for the object wfC
, I have one call to the copy constructor for object wfB
, and then the move constructor is called to move the semantics of rvalue(temporary) object wfA + wfB
to wfC
.
Finally, the 4 destructors account for wfA
, wfB
, wfC
and the temporay object wfA + wfB
(right?)!
I think so far so good! (correct me if it is not totally okay!)
Now, I throw away the std::move() statement, so my main program becomes:
int main(int argc, char * argv[]){
wf wfA(8,8,4);
for(unsigned int i=0; i<wfA.wfSize; i++){
float fl = (float)(i);
wfA.val[i] = fcomp(fl,0.0);
}
wf wfB(wfA);
wf wfC = wfA + wfB;
wfC.dispInfo();
for(unsigned int i=0; i<5; i++)
std::cout << wfC.val[i] << " ";
std::cout << std::endl;
return 0;
}
the output now becomes:
wf: constructor 1
wf: copy constructor
wf: classes addition
wf: constructor 1
nx = 8
ny = 8
nf = 4
wfSize = 256
(0,0) (2,0) (4,0) (6,0) (8,0)
wf: destructor
wf: destructor
wf: destructor
I see that the move constructor has not been called now so I expected that I would have a call to the copy assignement operator, or even the copy constuctor since the operator+
returns the object by value, right?.
But, I cannot see any such call...
So the question is: What is really happening now in my code?
Is there any copy happening but cannot see it, or maybe the compiler sees what I am trying to do and thus optimize it in advance by creating the wfC
using 1 constructor call?
Also, which is the optimal way to do this operation, using std::move() or without using it?
Note: I compiled the code using -O3 and -O0 but the output did not change.