2

次のように定義されたクラスがあります (Accelerated C++ を読んだことがある人は、このクラスになじみがあるかもしれません)。

class Student_info{
public:
    Student_info() : midterm(0.0), final(0.0) {};
    Student_info(std::istream& is){read(is);};

    Student_info(const Student_info& s);

    ~Student_info();

    Student_info& operator=(const Student_info& s);

    //Getters, setters, and other member functions ommited for brevity

    static int assignCount;
    static int copyCount;
    static int destroyCount;

private:
    std::string name;
    double midterm;
    double final;
    double finalGrade;
    std::vector<double> homework;

};

typedef std::vector<Student_info> stuContainer;


bool compare(const Student_info& x, const Student_info& y);

関数calculator()は、このタイプのオブジェクトを利用します。関数の一部として、(既に宣言されている) Student_info オブジェクトのベクトルは、ライブラリの汎用ソート関数を使用してソートされます。私のプログラムはこの時点を過ぎても進行しません (ただし、NetBeans によると、例外はスローされず、プログラムは正しく終了します)。

sort 関数は、コンテナーに保持されている型の代入演算子を多用しますが、定義したものの何が問題なのかを見つけることができないようです (定義する前に、プログラムは適切に機能していました)。Accelerated C++ によると (または、少なくともこれが私が解釈した方法です)、代入演算子が機能するはずの適切な方法は、最初に左のオペランドを破棄し、次に右のオペランドと等しい値で再度構築することです。したがって、これは私のオーバーロードされた operator= 定義です。

Student_info& Student_info::operator=(const Student_info& s)
{
    if(this != &s)
    {
        this->~Student_info();
        destroyCount++;

        *this = s;
    }

    return *this;
}

ご覧のとおり、以下で定義されている Student_info コピー コンストラクターを呼び出します。

Student_info::Student_info(const Student_info& s)
{
    name = s.name;
    midterm = s.midterm;
    final = s.final;
    finalGrade = s.finalGrade;
    homework = s.homework;

    copyCount++;
}

sort ステートメントを省略すると、プログラムが正しく機能し、copyCount (コピー コンストラクターと operator= でのみ変更される) が 0 より大きいため、コピー コンストラクターは正しく機能します。

では、代入演算子の何が問題なのですか? 呼び出し元の Student_info オブジェクトの破棄と関係がありますが、破棄しない以外に修正する方法がわかりません。

(ちなみに、コピー コンストラクタ、デストラクタ、および代入演算子の作成は、Accelerated C++ の演習で必要になります...これらの関数の合成バージョンが私のクラスには明らかに十分であることを認識しています)

4

3 に答える 3

6

ダメダメダメ。それはまったくそのように機能するはずではありません。現在の代入演算子は、呼び出されたオブジェクトを破棄してから、破棄されたオブジェクト(ああ、未定義の動作)に対して自分自身を呼び出します(ああ、無限再帰)。既存のオブジェクトを破棄することは想定されていません。まったく。そして、このコード*this = sはコンストラクターをまったく呼び出しません。代入演算子を呼び出します。これは、定義しているものです。コピーコンストラクターの呼び出しは、のようになりますnew (this) Student_info(s);。これは既知のパターンであり、多くの点でひどいものです。あなたがそれを推薦している本を持っているならば、それをゴミ箱に捨ててください。

代入演算子は、データを右側から左側にコピーすることになっています。ほとんどの場合、これを行う最も簡単な方法は、各データメンバーをコピーすることです。この演算子のセマンティクスには、何も破棄することは含まれません。この演算子を使用する人は誰でも、Student_infoオブジェクトが破壊されていないことを期待する権利があります。

メンバーの既存の代入演算子を呼び出してから、必要な追加のロジックを実装するだけです。

于 2011-01-26T16:19:14.203 に答える
2
*this = s;

これは無限の再帰です。コピーコンストラクタではなく、代入演算子です

于 2011-01-26T16:19:07.783 に答える
1
struct Student_info {
  Student_info& operator=(Student_info other) {
    swap(*this, other);
    return *this;
  }

  friend void swap(Student_info &a, Student_info &b) {
    using std::swap;
    #define S(N) swap(a.N, b.N);
    S(name)
    S(midterm)
    S(final)
    S(finalGrade)
    S(homework)
    #undef S
  }

private:
  std::string name;
  double midterm;
  double final;
  double finalGrade;
  std::vector<double> homework;
};

簡潔にするために、ゲッター、セッター、およびその他のメンバー関数は省略されています

単なるボイラープレートである public の getter と setter がある場合は、代わりに対応するデータ メンバーを public としてマークすることを検討してください。

于 2011-01-26T16:41:18.497 に答える