4

新しい move-constructor/move-operator を使用すると、オブジェクトの所有権を譲渡できるため、(高価な) コピー コンストラクター呼び出しを使用する必要がなくなります。しかし、(戻りパラメータを使用せずに) 一時オブジェクトの構築を回避することは可能ですか?

例: 以下のコードでは、コンストラクターが 4 回呼び出されていますが、理想的には、クロス メソッドでオブジェクトを構築しないようにしたいと考えています。戻りパラメータを使用する(たとえばvoid cross(const Vec3 &b, Vec3& out)、可能ですが、読みにくいです。既存の変数を更新することに興味があります。

#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;
}
4

4 に答える 4

2

新しい値を直接割り当てる場合:

Vec3 a = b.cross(c);

その後、RVOが有効になり、後で構築および移動される一時的なものがない可能性があります。最適化してコンパイルしていることを確認してください。戻り値は、その場で a に構築されます。

また、ヒープに 3 つの float の配列を割り当てると、パフォーマンスが低下するようです。Cライクな配列を使用するfloat p[3]std::array<float, 3>、はるかに優れたパフォーマンスを発揮するはずです。

于 2013-06-26T11:25:48.067 に答える
0

これはあなたの質問に対する直接的な回答ではありません。現在の回答は重要な点をカバーしているため、私はほとんど貢献できませんが、選択したヒープベースの設計の欠点に注意を向けたいと思います。

3d-Vector が単独で使用されることはめったにありません。

もちろん、実行する必要がある移動の数とベクトルに対して実行する操作の数に応じて、トレードオフになります。

いくつかの単一ベクトルのみを使用し、多くのコピー/移動を行う場合は、ヒープベースの設計に固執できます。ただし、複数のベクトル (ベクトルや配列など) があり、それらを操作したい場合、パフォーマンスが心配な場合は、ヒープ割り当てを行わないことをお勧めします。

std::vector<Vec3> a(20);
class con_Vec3 { double x, y, z; };
std::vector<con_Vec3> b(20);

ベクターaは、float へのポインターの連続ブロックを維持します。ここで、float 値はメモリ内の別の場所に存在します。これは、値が実際にメモリ内に散らばっていることを意味します。対照的に、 Vectorbには 60double秒の連続したブロックがすべて 1 か所に含まれます。

そのようなものを移動するコストはstd::vector両方のケースで等しくなります (それぞれ準スワップ) が、それらをコピーすると、21 の割り当てと 20 のコピーが行われるため、ヒープベースのソリューションは遅くなりますが、ヒープ以外のソリューションではベクトル全体をカバーする 1 つの割り当てと 20 のコピーがあります。

于 2013-06-26T12:26:32.847 に答える