2017-12-06 11 views
1

私はまだCスタイルのものがたくさんあることがわかりました。私は今、生のポインタの使用量を減らして新しい千年紀を受け入れるよう努めています。私はunique_ptr<BaseClass>というベクトルを持っていて、それぞれが派生クラスのオブジェクトを指しています。私は派生クラスのオブジェクトの1つを参照する良い方法を取得しようとしています。現時点では、.get()関数を使用してこれを派生クラスにキャストします。型キャストでunique_ptrのオブジェクトへの参照を作成する

しかし、私が理解しているように、.get()は、ほとんどが生のポインタを主張するレガシーコードとのインタフェースにあり、その使用は避けるべきです。 unique_ptr内のオブジェクトへの別のポインタを持つことは、避けることができれば素晴らしいスタイルのようには見えません。取得を使用せずに、派生クラスオブジェクトへの参照を取得する方法はありますか?またはオブジェクトを処理する他の便利な方法?

#include <iostream> 
#include <vector> 

class Fruit { 
public: 
    double size = 0; 
    virtual ~Fruit() = default; 
}; 

class Apple : public Fruit { 
public: 
    bool hasLeaf = false; 
}; 

void doAppleStuff(std::vector<std::unique_ptr<Fruit> > &apples) { 

    // assume we know that element [0] exists and it is definitely of type Apple 
    auto apple = static_cast<Apple *> (apples[0].get()); // is there a better option? 

    if (apple->hasLeaf) { 
     std::cout << "We can leaf now" << std::endl; 
    } else { 
     std::cout << "We are pitiably leafless" << std::endl; 
    } 
} 

int main() { 
    std::vector<std::unique_ptr<Fruit> > fruitVec; 
    auto apple = new Apple; 
    apple->hasLeaf = true; 
    std::unique_ptr<Fruit> applePt(apple); 
    fruitVec.push_back(std::move(applePt)); 
    doAppleStuff(fruitVec); 
    return 0; 
} 

(私はそれがC++ 14からmake_uniqueと主な機能を短縮するために、おそらく可能だと思う):

はここで単純化されたコード例です。

このようなことをするのは良いコーディングスタイルですか?

auto &apple = *static_cast<Apple *> (apples[0].get()); 

"Downcasting" unique_ptr<Base> to unique_ptr<Derived>の答えがそれを解放し、派生クラスに新しいunique_ptrを再作成することによってunique_ptrを「キャスト」する方法を概説し、私は本当にしたくないとしてそれは、ここでは該当いないようですユニークなポインタのベクトルを混乱させる(私が何かを見逃していない限り)。あなたは、ポインタが非nullptrであることがわかっている場合は

+1

と呼ば

void doAppleStuff(std::vector<Apple *> & apples); 

doAppleStuffを変更、あなたのことですコードは現在UBです。 – Jarod42

+0

基本クラスへのポインタを使用してクラスを格納することを主張する場合、いくつかの仮想メソッド(特にデストラクタ)を宣言し、アップキャストの代わりに呼び出す必要があります。 – VTT

+0

@ Jarod42優れた点...私はバーチャルデストラクタを追加するコードを編集します –

答えて

1

は、単純なキャストは次のとおりです。

auto& apple = static_cast<Apple&>(*apples[0]); 

この構文はT& operator*() constをサポートするすべてのスマートポインタで動作します(例えばstd::unique_ptrstd::shared_ptr,boost::intrusive_ptr)。後者は常に追加する前に、nullptrを確認しなければならないので、より塩基基準に由来参照をキャスト多重継承を持つ場合には

からベース・ポインタに由来ポインタを鋳造よりも高速でありますポインタへのオフセットを減算すると(nullptrはキャスト後にnullptrになります)、参照はnullptrにはなりません。したがって、実行時のチェックは不要です。

+0

すばらしい、ありがとう、これは私が探していたものです。 –

0

安全な代替が

template <typename T> // 
std::vector<T *> of_type(const std::vector<std::unique_ptr<Fruit>> & fruits) { 
    std::vector<T *> result; 
    for (auto & ptr : fruits) { 
     if (T * item = dynamic_cast<T *>(ptr.get())) { 
      result.push_back(item); 
     }    
    } 
    return result; 
} 

ヘルパー関数を定義し、Fruit`は何の仮想デストラクタを持っていない `のよう

doAppleStuff(as_type<Apple>(fruitVec)); 
関連する問題