0

有効な C++ の項目 28 は言うavoid returning "handles" to object internalsこの質問は、クラスの内部を誤って公開することを避けるために、カプセル化について考えることによって、コードを正確に設計する方法を示しています。

私の例にはデータ配列が含まれており、メモリは使用を避けたい問題であるためstd::vector(および Boost ライブラリ)。

ここで配列を使用すると、私のコードの非常に単純化されたバージョンになります。

class Foo {
public:
    Foo(int size) : data_(new int[size]) {
        // Populate with data just for the example
        for(int i = 0; i < size; i++) {
            data_[i] = i;
        }
    }

    // I know that this isn't good practice
    int* const get_data() const {
        return data_;
    }

    ~Foo() {
        delete[] data_;
    }

private:
    int* data_;
};

int main(void) {
    Foo* foo = new Foo(10);
    int* const data = foo->get_data();
    delete foo;
    // data now dangles which is bad!
    return 0;
}

constwith を使用しget_data()ても安全ではないことを理解しています。ベクトルを使用している場合は、上記の質問の例のようにコピーできますが、これを避けたいので、この潜在的に危険な状況を回避するためにクラスを設計する最善の方法を考えていましたか?

4

5 に答える 5

1

あなたの状況での 1 つの方法は、スマート ポインターを使用することです。私はshared_ptrを好みます。発生する問題を回避するためにクラスを変更する方法は次のとおりです。

class Foo {
public:
    Foo(int size) : data_(new int[size]) {
        // Populate with data just for the example
        for(int i = 0; i < size; i++) {
            data_[i] = i;
        }
    }

    // I know that this isn't good practice
    std::shared_ptr<int> const get_data() const {
        return data_;
    }

    ~Foo() {
        //delete[] data_;
    }

private:
    std::shared_ptr<int> data_;
};

int main(void) {
    Foo* foo = new Foo(10);
    std::shared_ptr<int> data = foo->get_data();
    delete foo;
    // data still good )) // --now dangles which is bad!--
    return 0;
}
于 2013-04-15T08:29:06.070 に答える
0

配列が実際に に含まれていたとしても、ポインターを返すことが完全に受け入れられるケースはたくさんありstd::vectorます。たとえば、外部 API を使用している場合や、データをコピーする場合は歓迎されません。

あなたはサル用にプログラミングしていないので、危険はわかりません。クラスのユーザーは知っているはずです。しかし、データのサイズを照会する関数を追加しました。それは安全のためです。

extern "C"{
void foobar( const int *data, unsigned int size );
}
class Foo {
public:
    Foo(unsigned int size) : size_(size), data_(new int[size]) {
        // Populate with data just for the example
        for(unsigned int i = 0; i < size; i++) {
            data_[i] = i;
        }
    }

    unsigned int get_size() const{return size_;}
    int* const get_data() const {
        return data_;
    }

    ~Foo() {
        delete[] data_;
    }

private:
    int* data_;
    unsigned int size_;
};

int main(void) {
    Foo* foo = new Foo(10);
    int* const data = foo->get_data();
    foobar( data, foo->get_size() );
    delete foo;
    return 0;
}
于 2013-04-15T09:29:30.320 に答える
0

get_native_handle()標準ライブラリにもクラスがあることを考えると、std::thread標準ライブラリと EC++ のどちらが間違っていたのだろうか!

効果的な C++ は一連のガイドラインであり、どのコンテキストにもすべてを適用することはできません。「道徳」の本です。それを「合法」として扱わないでください。データを公開する必要がある場合は、それを実行してください。

内部データへのポインタを (getter を介して取得した後に) 削除する人も、「悪いこと」をしていることになります。

于 2013-04-15T09:37:59.767 に答える
0

本当に std::vector の使用を避けたい場合は、一部の winapi 関数で使用されるものと同様のアプローチを実装できます。

size_t get_data(int* data_ptr, size_t& buffer_size)
{
if(buffer_size < sizeof (data_)
{
buffer_size = sizeof(data);
return 0;
}
//You could you memcpy for ints here
        for(int i = 0; i < size; i++) {
            data_ptr[i] = i;
        }
return sizof(data_);
}

したがって、呼び出し元がデータを格納するためのメモリを提供し、そのメモリの割り当てに注意する必要があります。
これにより、 std::vector がすでに行っていた安全性の低下を再発明する必要があります。また、クラスをより広範囲に使用し始めるときに問題を回避するために、Rule of threeをチェックする必要があります。

于 2013-04-15T08:41:14.260 に答える