1

2 つの動的コンテナーに対して「+」演算子をオーバーロードする必要があります。

Occurance Occurance::operator +(const Occurance& occ) const {
    Occurance* result = new Occurance;
    Iterator i1(head);
    Iterator i2(occ.head);
    while( !(i1.isNULL() && i2.isNULL()) ) {
        if(i1.getCount() >= i2.getCount()) {
            result->add(i1.getFile());
            result->tail->count = i1.getCount();
            ++i1;
        }
        else {
            result->add(i2.getFile());
            result->tail->count = i2.getCount();
            ++i2;
        }
    }
    return *result;
}

私がする時:

Occurance occ = occ1+occ2;

リストの先頭へのポインタは正しくコピーされ、すべて正常に動作しますが、結果への参照が失われています。occ デストラクタが呼び出されると、リスト全体が破棄されますが、結果の最初の要素は参照ではなくコンテンツをコピーしただけなので、破棄されません。

戻り値の型を参照に変更すると、同じことが発生しますが、割り当て中に発生します。

別のアイデアは、「結果」を動的に作成しないことです。そのため、関数の最後で自動的に破棄されますが、リスト全体を破棄するデストラクタを呼び出しています。

このような構造を作成し、このメモリリークなしで返す簡単で「適切な」方法はありますか? もちろん、「+」演算子から期待されるように、返される型はオブジェクトまたは参照でなければなりません。

デストラクタで関数へのポインタを変更することを含む厄介なハックを見つけましたが、非常に単純なものが欠けているだけでしょうか?

編集: もちろん、クラスは 3 のルールに従います。割り当ては次のとおりです。

Occurance& Occurance::operator =(const Occurance& occ) {
    destruct();
    head = occ.head;
    current = occ.current;
    tail = occ.tail;
    return *this;
}

Occurance::Occurance(const Occurance& occ) {
    head = occ.head;
    current = occ.current;
    tail = occ.tail;
}

Occurance::~Occurance() {
    destruct();
}

destruct は、'head' で始まるリストを破棄するだけです。

クラス宣言:

class Occurance {
private:
    class Node {
    public:
        Node* next;
        Node* prev;
        int count;
        const File* file;

        Node(const File& a_file, Node* a_prev);
    };

    Node* head;
    Node* tail;
    Node* current;
    void destruct();
public:
    class Iterator {
    private:
        Node* node;
    public:
        Iterator();
        Iterator(Node* a_node);
        void operator ++();
        const File& getFile();
        int getCount();
        bool isNULL();
    };

    Occurance();
    Occurance(const Occurance& occ);
    void add(const File& a_file);
    Occurance& operator =(const Occurance& occ);
    Occurance operator +(const Occurance& occ) const;   //dodaje listy zachowując sortowanie
    Iterator begin() const;
    virtual ~Occurance();
};
4

3 に答える 3

0

コピー コンストラクターと代入演算子が壊れています。リストのディープ コピーを作成するか、何らかの共有セマンティクス (参照カウントなど) を実装する必要があります。リンクされたリストがあるように見え、頭と尾のポインターをコピーしているだけです。したがって、コピーを作成して一方が破棄されると、もう一方のリストも破棄されます。

デフォルトのコンストラクターおよび/またはadd関数がノードの動的割り当てを行うと想定しています。次に、コピー コンストラクターと代入演算子も、コピーされるオブジェクトのノードから完全に独立したノードを動的に割り当てる必要があります。C++11 を使用できる場合は、移動コンストラクターと移動代入演算子の実装も検討する必要があります。

これらの関数がすべて正しくなると、operator+ は次のようになります。

Occurance Occurance::operator +(const Occurance& occ) const {
    Occurance result; // no dynamic allocation

    // operate on result

    return result;
}
于 2013-10-26T13:29:19.427 に答える
0

別のアイデアは、「結果」を動的に作成しないことです。そのため、関数の最後で自動的に破棄されますが、リスト全体を破棄するデストラクタを呼び出しています。

コピーコンストラクターについて読む必要があります。返されたオブジェクトのデストラクタが呼び出される前に、そのオブジェクトから occ1+occ2 操作の結果を保持する一時オブジェクトにデータをコピーするコピー コンストラクタが呼び出されます。動的に割り当てられたデータを指すメンバーがあると仮定します。この場合、コピー コンストラクターが呼び出されると、新しいメモリを割り当ててデータをコピーする代わりに、ポインターを一時オブジェクトに割り当てます。自分でコーディングする必要があります。読むことをお勧めします:http://www.cplusplus.com/articles/y8hv0pDG/

また、この割り当てを実行する場合は、同じ方法で operator = をオーバーロードする必要があることに注意してください。

occ = occ1+occ2

編集:申し訳ありませんが、コメントできません。クラス宣言もコピーしていただけますか?

于 2013-10-26T13:12:18.383 に答える
0

C++ では、一般的な原則として、ヒープに割り当てられたオブジェクトではなく、スタックにコピーを返します。したがって、この場合、次のようにするだけです。

Occurance Occurance::operator +(const Occurance& occ) const 
{
    Occurance result;
    // do whatever
    return result; 
}

そしてそれを呼び出します:

Occurance occ = occ1+occ2;

コンパイラは、コピーを作成するのではなく、返されるオブジェクトを再利用することを理解するのに十分スマートです (これは、戻り値の最適化またはRVOと呼ばれます)。

なんらかの理由でオブジェクトを関数内で作成されたものとまったく同じにする必要がある場合は、スマート ポインターを返す ( shared_ptrを検索する) か、新しい C++11 move operatorを使用できます。

于 2013-10-26T13:27:28.827 に答える