17

現在生のポインターを使用しているコードがいくつかあり、スマートポインターに変更したいと思います。これは、さまざまな方法でコードをクリーンアップするのに役立ちます。とにかく、オブジェクトを返すファクトリメソッドと、オブジェクトを管理する呼び出し元の責任があります。所有権は共有されていないので、私unique_ptrは適切だと思います。私が返すオブジェクトは通常、すべて単一の基本クラスから派生しObjectます。

例えば、

class Object { ... };
class Number : public Object { ... };
class String : public Object { ... };

std::unique_ptr<Number> State::NewNumber(double value)
    {
        return std::unique_ptr<Number>(new Number(this, value));
    }

std::unique_ptr<String> State::NewString(const char* value)
    {
        return std::unique_ptr<String>(new String(this, value));
    }

Object返されるオブジェクトは、タイプのオブジェクト(基本クラス)を操作する別の関数に渡す必要があることがよくあります。スマートポインタがない場合、コードは次のようになります。

void Push(const Object* object) { ... } // push simply pushes the value contained by object onto a stack, which makes a copy of the value
Number* number = NewNumber(5);
Push(number);

このコードを使用するようunique_ptrsに変換すると、ポリモーフィズムの問題が発生しました。当初は定義を変更しPushて使用するunique_ptrsことにしましたが、派生型を使用しようとするとコンパイルエラーが発生します。次のように、オブジェクトを基本タイプとして割り当てることができます。

std::unique_ptr<Object> number = NewNumber(5);

そしてそれらをに渡しPushます-もちろん機能します。ただし、派生型のメソッドを呼び出す必要があることがよくあります。結局、私はPushによって保存されたオブジェクトへのポインタを操作することにしましたunique_ptr

void Push(const Object* object) { ... }
std::unique_ptr<Object> number = NewNumber(5);
Push(number.get());

さて、投稿の理由に。これが私が抱えていた問題を解決する通常の方法であるかどうかを知りたいですか?対オブジェクト自体をPush操作する方が良いですか?unique_ptrもしそうなら、どのようにしてポリモーフィズムの問題を解決しますか?単純にptrsをキャストするだけではうまくいかないと思います。スマートポインターから基になるポインターを取得する必要があるのは一般的ですか?

質問が明確でない場合は申し訳ありません(私に知らせてください)。

編集:私のプッシュ機能は少し曖昧だったと思います。基になる値のコピーを作成し、入力オブジェクトを実際に変更したり、保存したりすることはありません。

4

4 に答える 4

12

最初は単純に Push の定義を unique_ptrs も使用するように変更することにしましたが、派生型を使用しようとするとコンパイル エラーが発生します。

一意性を正しく処理していない可能性があります。

void push(std::unique_ptr<int>);
int main() {
    std::unique_ptr<int> i;
    push(i); // Illegal: tries to copy i.
}

これをコンパイルすると、unique_ptr一方だけunique_ptrがオブジェクトを所有するという の不変条件が自明に破られます。これはi、 とローカル引数の両方が をpush所有するためint、違法です。unique_ptr移動のみで、コピーはできません。unique_ptrこれは、完全に正しく処理される派生から基底への変換とは関係ありません。

pushがオブジェクトを所有している場合は、 を使用std::moveしてそこに移動します。そうでない場合は、生のポインターまたは参照を使用します。これは、非所有のエイリアスに使用するものだからです。

于 2012-11-08T12:12:48.543 に答える
4

さて、あなたの関数が(指された)オブジェクト自体を操作し、そのアドレスを必要とせず、所有権も取らず、私が推測するように、常に有効なオブジェクトが必要な場合( a が渡されると失敗するnullptr)、なぜそれらはポインターを取るのですかまったく?

適切に実行して、参照を取得させます。

void Push(const Object& object) { ... }

次に、呼び出しコードは、生のポインターとスマート ポインターでまったく同じに見えます。

auto number = NewNumber(5);
Push(*number);

編集:しかし、もちろん、参照またはポインターを使用Pushするstd::unique_ptr場合でも、渡されたオブジェクトの所有権を取得しない場合は取得しないでください(渡されたポインターから所有権を盗むことになります)。または、一般に、ポイント先のオブジェクトが所有されない場合は、所有ポインターを使用しないでください。この点で何も違いはなく、所有権が取得されない場合はforのパラメーターstd::shared_ptrと同じくらい悪い選択です。std::unique_ptrPushPush

于 2012-11-08T12:11:12.197 に答える
3

所有権を取得しない場合Pushは、おそらくポインタではなく参照を取得する必要があります。そしておそらくconst1つ。だからあなたは持っているでしょう

Push(*number);

これは明らかに、プッシュがポインタをリターンの先に保持しない場合にのみ有効です。もしそうなら、最初に所有権を再考する必要があると思います。

于 2012-11-08T12:08:51.497 に答える
1

一意のポインターを使用したポリモーフィズムの例を次に示します。

vector<unique_ptr<ICreature>> creatures;

creatures.emplace_back(new Human);
creatures.emplace_back(new Fish);

unique_ptr<vector<string>> pLog(new vector<string>());

for each (auto& creature in creatures)
{
    auto state = creature->Move(*pLog);
}
于 2013-10-10T13:52:12.403 に答える