3

私の大学では、C ++での実用的なプログラミングテストがあります。問題のタスクが有効であり、正しく完了することが可能であるかどうかについて確信が持てない例にとらわれています。

(単純な)タスク:

  • のデストラクタを完了しPersonて、割り当てられたnameものが再び解放されるようにします

  • //???main関数で、以前に割り当てられたメモリを解放するために必要なステートメントに置き換えます

最初は、タスクは私にとっては些細なことのように見えました。デストラクタの場合は、単に記述delete[] nameし、main関数で。を使用しますdelete[] friends。おそらく、それはこの例の作者が私たちに意図したことでもあります。

でも:

このコード例には欠陥があるようです。これにより、メモリリークが発生し、デストラクタが複数回呼び出されます。

personクラスには割り当てがありません。つまり、main関数の配列のスロットに割り当てられているoperator =などの既存のPersonオブジェクトは、内部で割り当てられたsがコピーされません。したがって、2つのオブジェクトが同じ内部ポインタを共有するようになりました。さらに、前述のアレイスロットに以前存在していた名前へのポインタが永久に失われ、避けられないメモリリークが発生します。mariafriendsnamechar*Person

呼び出されるとdelete[] friends;、配列内のオブジェクトが破棄され、デストラクタが呼び出されてnameメンバーが解放されます。ただし、プログラムが終了すると、スコープ内のローカルのPersonオブジェクトmainが破棄されます。もちろん、そのnameメンバーは、以前に解放されたメモリをポイントしています。

実際の質問:

  • このテスト例には欠陥がありますか、それとも何かが足りませんか?
  • 与えられたタスクの実行に完全に固執する場合(デストラクタの実装のみを変更し、メイン関数のコメント部分に新しいコードを挿入する)、上記の問題を修正できますか?

..

#include <iostream>
using namespace std;

int strlen(const char *str) {
    if (str==0) return 0;
    int i=0;
    for (; str[i]; ++i);
    return i;
}

void strcpy(const char *src, char *dest) {
    if (src==0 || dest==0) return;
    int i=0;
    for (; src[i]; ++i) dest[i]=src[i];
    dest[i]=’\0’;
}

class Person {
    char *name;
public:
    Person(const char *str = "Susi") {
        name = new char[strlen(str)+1];
        strcpy(str,name);
    }

    Person(const Person &p) {
        name = new char[strlen(p.name)+1];
        strcpy(p.name,name);
    }

    ~Person() {
        //...
    }

    void change() {
        name[4]='e';
    }

    ostream &print(ostream &o) const {
        o<<name;
        return o;
    }
};

int main() {
    Person maria("Maria"), peter("Peter"), franz("Franz"), luisa("Luisa");
    Person mary(maria);
    Person luise;
    Person p(luise);

    Person *friends= new Person[7];
    friends[0]=maria;
    friends[1]=peter;
    friends[2]=franz;
    friends[3]=luisa;
    friends[4]=mary;
    friends[5]=luise;
    friends[6]=p;
    friends[5]=luisa;
    friends[3].change();
    friends[4].change();

    for (int i=0; i<7; ++i) {
    friends[i].print(cout);
    cout<<endl;
    }

    //???
    return 0;
}
4

3 に答える 3

5

あなたは、絶対に正しい。指定された位置で変更を加えるだけで修正できますが、かなり極端になります。

//...デストラクタの内部を次のように置き換えます。

    delete[] name;
}

Person& operator=(const Person& other)
{
    if (this != &other) {
        delete[] name;  // not completely exception-safe!
        name = new char[strlen(other.name)+1];
        strcpy(other.name,name);
    }
    return *this;

もう1つの重大な問題はstrcpy、引数を並べ替える新しい定義を使用して標準関数()を再定義することです。

(参照:SQLインジェクション攻撃。これにより、既存の構文要素のペア(多くの場合、引用符と括弧)が、挿入された構文要素と再ペアになります)

于 2012-10-08T16:51:52.060 に答える
2
  1. はい、テスト例には欠陥があります。おそらく意識的に行われたものです。クラスPersonには必ず代入演算子が必要です。三つのルールを覚えておいてください。
  2. いいえ、できません。デフォルトのコンパイラ生成代入演算子は、配列内のオブジェクトによって割り当てられたメモリをリークし、自動オブジェクトfriendsによって割り当てられたメモリを二重削除します。Person
于 2012-10-08T16:52:18.627 に答える
-1

新しいものごとにdelete[]が必要です。

于 2012-10-08T16:58:04.563 に答える