2017-12-06 4 views
1

Ich finde ich immer noch eine peinlichen große Anzahl von Dingen in c-Stil zu tun, so versuche ich jetzt durch die Reduzierung meiner Verwendung von rohen Zeigern das neue Jahrtausend zu umarmen. Ich habe einen Vektor von unique_ptr<BaseClass>, der auf ein Objekt einer abgeleiteten Klasse zeigt. Ich versuche, einen netten Weg zu finden, auf eines der Objekte der abgeleiteten Klasse zu verweisen. Im Moment mache ich das, indem ich die .get() Funktion benutze und diese auf die abgeleitete Klasse umwandele.erstellen Referenz in unique_ptr zum Objekt mit Typumwandlung

Allerdings, wie ich es verstehe, .get() ist meist da, um mit Legacy-Code, der auf rohen Zeigern besteht, Schnittstelle und seine Verwendung sollte vermieden werden. Einen anderen Zeiger auf ein Objekt in einem unique_ptr zu haben, erscheint nicht als großer Stil, wenn es vermieden werden kann. Gibt es eine Möglichkeit, einen Verweis auf das abgeleitete Klassenobjekt zu erhalten, ohne get zu verwenden? Oder eine andere bequeme Art, mit dem Objekt umzugehen?

Hier ist ein vereinfachtes Codebeispiel:

#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; 
} 

(ich denke, dass es wohl möglich ist, die Hauptfunktion mit make_unique von C++ 14 zu verkürzen).

Wäre es gut Codierungsstil, um so etwas zu tun?

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

Die Antwort auf "Downcasting" unique_ptr<Base> to unique_ptr<Derived> umreißt ein Verfahren zu „werfen“ die unique_ptr durch Loslassen und eine neue unique_ptr der abgeleiteten Klasse neu zu erstellen, aber das hier nicht anwendbar scheint, wie ich wirklich nicht wollen Durcheinander mit dem Vektor der eindeutigen Zeiger (es sei denn, ich vermisse etwas).

+1

genannt ändern Als 'Fruit' keinen virtuellen Destruktor hat, Ihre Code ist derzeit UB. – Jarod42

+0

Wenn Sie darauf bestehen, Klassen mit dem Zeiger auf die Basisklasse zu speichern, müssen Sie einige virtuelle Methoden (insbesondere Destruktoren) deklarieren und sie aufrufen, anstatt sie zu upcasieren. – VTT

+0

@ Jarod42 Ausgezeichneter Punkt ... Ich werde den Code bearbeiten, um einen virtuellen Destruktor hinzuzufügen –

Antwort

1

Wenn Sie wissen, dass der Zeiger nicht nullptr ist, eine einfache Besetzung ist:

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

Diese Syntax mit alle Smart-Pointer arbeitet, die T& operator*() const unterstützen (zB std::unique_ptr, std::shared_ptr, boost::intrusive_ptr).

in Fällen mit Mehrfachvererbung Guss aus-base-Referenz-to-derived-reference ist schneller als Guss aus-base-pointer-to-derived-pointer weil letztere immer nullptr überprüfen muß, bevor die Zugabe oder Subtrahieren eines Offsets zum Zeiger (so dass nullptr nach dem Cast in nullptr wird), wohingegen Referenzen nicht nullptr sein können und daher keine Laufzeitprüfung erforderlich ist.

+0

Toll, danke, das ist, was ich gesucht habe. –

0

Eine sichere Alternative ist eine Hilfsfunktion

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; 
} 

zu definieren und dann doAppleStuff zu

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

als

doAppleStuff(as_type<Apple>(fruitVec)); 
Verwandte Themen