4

ニューラル ネットワーク プログラムとオーバーロードされた算術演算子用のカスタム マトリックス ライブラリがあります。クラス宣言は次のとおりです。

class Matrix{
public:
int m;
int n;
double **mat;
Matrix(int,int);
Matrix(int);
Matrix(const Matrix& that):mat(that.mat),m(that.m),n(that.n)
    {
        mat = new double*[m];
        for(int i = 0;i<m;i++)mat[i] = new double[n];
    };
~Matrix();
friend istream& operator>>(istream &in, Matrix &c);
friend ostream& operator<<(ostream &out, Matrix &c);
Matrix operator+(const Matrix& other);
};

+ 操作の関数定義は次のとおりです。

 Matrix Matrix::operator+(const Matrix& other)
    {
        Matrix c(m,n);
        for(int i=0;i<m;i++)
        {
           for(int j = 0; j<n;j++)
               c.mat[i][j] = mat[i][j] + other.mat[i][j];
        }
        return c;
    }

私はあらゆる方法でそれを実装しようとしましたが、エラーは同じです...ここにインスタンスがあります

Matrix x(m,n); //m and n are known
x = a+b; // a and b are also m by n matrices

ブレークポイントを使用してコードをデバッグしましたが、ここにエラーがあります... 演算子関数のローカル行列 'c' は、返される前に破棄されるため、x に割り当てられているのはガベージ ポインターです。

私に何か提案してください...

4

6 に答える 6

2

クラスのコピー コンストラクターを定義する必要があります。コピー コンストラクターは、メモリを割り当てmatてデータのコピーを作成する必要があります。

これがないと、 のときにreturn cと同じ値を持つ新しいオブジェクトが構築さmatcます。その後対象外となった場合cは削除しc.matます。その結果、 のコピーにcはダングリング ポインターが残ります。

これを行った後、代入演算子も実装する必要があります。

于 2013-02-21T14:46:01.787 に答える
2

返された値はテンポラリを初期化するために使用され、返された値が破棄された、このテンポラリが結果にコピーされます。これは正常な動作です (NRVO のために呼び出しが省略されない限り)。

ただし、クラスには明示的に定義されたコピー コンストラクターがないため、暗黙的に生成されたコンストラクターが呼び出さmatれ、返されたオブジェクトのデストラクタによって割り当てが解除されたものへのポインター ( ) がコピーされるだけです。

これは、いわゆる3 つの規則 (クラスでコピー コンストラクター、代入演算子、またはデストラクターを明示的に定義するときはいつでも、それらすべてを定義する必要があるというプログラミングのベスト プラクティス) に違反しています。それらの 1 つを定義するクラスは、何らかのリソースを管理している可能性が高く、リソースの解放/取得ロジックを正しく処理するには、これらの 3 つの特別なメンバー関数がすべて必要であるという理論的根拠があります。

C++11 では、ポインターを割り当てて移動元のオブジェクトを無効にするだけで、 のコンテンツの転送を実行できる移動コンストラクターも使用できることに注意してください。Matrix

Matrix(Matrix&& m)
{
    mat = m.mat;
    m.mat = nullptr;
}

もちろん、ムーブ コンストラクターを導入する場合は、それに応じてクラス デストラクタを変更して、割り当てられたメモリを本当に解放する必要があるかどうかを確認する必要があります。

~Matrix()
{
    if (m.mat == nullptr)
    {
        return;
    }

    ...
}
于 2013-02-21T14:47:43.023 に答える
0

コンパイラで生成された関数ではできないため、オブジェクトのディープ コピーを作成するクラスのコピー コンストラクタと代入演算子が必要です。

コンパイラによって生成されたコピー コンストラクターと代入演算子は、クラスに含まれるオブジェクトを単純にコピーします。あなたの場合、これらは POD であるため、自動生成された関数は単純にビットごとのコピーを行います。の場合double**、これは、ポイント先の値ではなく、ポインター値のコピーになります。その結果、Matrixデストラクタが敷物を下から引き出す直前に、2 つのオブジェクトが同じ基になるデータを指すことになります。

于 2013-02-21T14:51:14.957 に答える
0

Matrix クラスには生のポインター メンバーがあり、おそらくそのコンストラクターにメモリを割り当てますが、コピー コンストラクターやコピー代入演算子はありません。

また、デストラクタはありますが、コピー コンストラクタやコピー代入演算子はありません。これは 3 つのルールに違反しています。

于 2013-02-21T14:47:04.360 に答える
0

YourMatrix cはローカル変数です。そのため、作成されたメソッドが終了すると破棄されます。C++ では、この望ましくない状況は通常、オブジェクトをコピーすることで解決されます。コピー コンストラクターと代入演算子 = を同じ機能で定義できます。コピーの問題は遅いことです。そのため、より高速にしたい場合は、コピーを使用しない別のアプローチを使用する必要があります。たとえば、呼び出し元が結果を格納する既存のマトリックス オブジェクトへの参照を渡すメソッドにパラメーターを追加できます。

于 2013-02-21T14:47:20.370 に答える
-1

Matrix オブジェクトではなく、Matrix * を返すようにコードを変更する必要があります。このようにして、Matrix オブジェクトが関数の後に確実に存在するようにすることができます。(現在のコードは Matrix オブジェクトを関数変数にするため、関数の終了後に削除されます)。

コードは次のようになります。

Matrix *Matrix::operator+(const Matrix& other)
{
    Matrix *c = new Matrix(m,n);
    for(int i=0;i<m;i++)
    {
       for(int j = 0; j<n;j++)
           c->mat[i][j] = mat[i][j] + other.mat[i][j];
    }
    return c;
}

編集:明らかにこれは悪い習慣です。今日も何かを学んだと思います:)

于 2013-02-21T14:49:05.880 に答える