Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 constructors

The new move-constructor/move-operator allows us to transfer ownership of objects and this way avoiding using (expensive) copy constructor calls. But is it possible to avoid constructing temporary objects (without using return parameters)?

Example: In the code below the constructor is called 4 times - but ideally what I would like to do is to avoid constructing any objects in the cross method. Using return parameters (for example void cross(const Vec3 &b, Vec3& out) would be possible, but is ugly to read. I'm interested in updating an existing variable.

#include <iostream>

using namespace std;

class Vec3{
public:
    Vec3(){
        static int count = 0;
        id = count++;
        p = new float[3];
        cout << "Constructor call "<<id <<" "<<p<< " "<<this<< endl;
    }

    ~Vec3(){
        cout << "Deconstructor call "<<id << " "<<p<<" "<<this<< endl;
        delete[] p;
    }

    Vec3(Vec3&& other)
    : p(nullptr) {
        cout << "Move constructor call "<<id << " "<<p<<" "<<this<< endl;
        p = other.p;
        other.p = nullptr;
    }

    Vec3& operator=(Vec3&& other) {
        cout << "Move assignment operator call from "<<other.id<<" to "<<id << " "<<p<<" "<<this<< endl;
        if (this != &other) {
            p = other.p;
            other.p = nullptr;
        }
        return *this;
    }

    Vec3 cross(const Vec3 &b){
        float ax = p[0], ay = p[1], az = p[2],
            bx = b.p[0], by = b.p[1], bz = b.p[2];
        Vec3 res;
        res.p[0] = ay * bz - az * by;
        res.p[1] = az * bx - ax * bz;
        res.p[2] = ax * by - ay * bx;
        return res;
    }

    float *p;
    int id;
};


int main(int argc, const char * argv[])
{
    Vec3 a,b,c;
    a = b.cross(c);
    return 0;
}
like image 737
Mortennobel Avatar asked Mar 23 '23 09:03

Mortennobel


2 Answers

Another solution is to return an "expression object" from a.cross(b), deffering the calculation until such an object is assigned to c, and then in the operator= you actually do the calculation:

 struct Vec3
 {

      CrossProduct cross(const Vec3& b);

      Vec3& operator=(CrossProduct cp)
      {
          do calculation here putting result in `*this`
      }
 }

and add similar mechanisms for construction and so on.

This is more involved, but a number of C++ math libraries use this design pattern.

like image 189
Andrew Tomazos Avatar answered Apr 05 '23 21:04

Andrew Tomazos


If you assign new value directly:

Vec3 a = b.cross(c);

Then it is possible that RVO will take effect and there is no temporary constructed and moved later. Make sure you are compiling with optimizations. The returned value would be constructed in-place into a.

Also allocating an array of 3 floats on heap seems like a performance killer. Using C-like array float p[3] or std::array<float, 3> should perform much better.

like image 30
Juraj Blaho Avatar answered Apr 05 '23 20:04

Juraj Blaho