In C ++ viene eseguito il cast per trovare i comportamenti non definiti dei puntatori non validi?

2

Per caso ho scoperto che con un tipo polimorfico che utilizza dynamic_cast di nuovo alla classe derivata genererebbe un errore "cast errato" se il puntatore non è più valido. È un comportamento non definito o potrebbe essere un modo per verificare i puntatori validi senza utilizzare i puntatori intelligenti?

Non sono molto contrario all'uso di puntatori intelligenti perché servono a un'esigenza reale, ma non mi piace la sintassi. Non ho nulla contro i tipi di modelli, ma trovo i puntatori intelligenti per gonfiare il codice e non sono un fan delle macro per cercare di evitarlo. Vorrei che C ++ includesse qualcosa nella sintassi del linguaggio stesso usando la funzione template ma con questa domanda sono più preoccupato di ciò che sta accadendo in basso e se questo è un modo valido e definito per verificare i puntatori validi dato che questo dà un eccezione ogni volta dai miei test.

#include <string>
#include <map>


using namespace std;

class Base
{
public:
    virtual ~Base(){}
};

class Derived : public Base
{
public:
    virtual ~Derived(){}
};

class Storage
{
private:
    map<string, Base*> storage;
public:
    void AddItem(string name, Base* base)
    {
        if (storage.find(name) == storage.end())
        {
            storage[name] = base;
        }
    }

    template <class T>
    T& FindItem(string name)
    {
        if (storage.find(name) != storage.end())
        {
            Base* item = storage[name];

            return dynamic_cast<T&>(*item);
        }

        throw;
    }
};

int main()
{
    Storage store;

    // force 'd' to go out of scope for our test
    {
        Derived d;

        store.AddItem("test", &d);
    }

    // this will throw a bad cast exception
    Derived& test = store.FindItem<Derived>("test");


    return 0;
}
    
posta user441521 19.05.2014 - 16:01
fonte

2 risposte

10

dynamic-cast ha bisogno di deferenza del puntatore per trovare l'offset corretto a cui dovrebbe essere, dereferenziare un puntatore non valido è un comportamento indefinito,

una cosa che potresti vedere:

class Derived2 : public Base
{
public:
    virtual ~Derived2(){}
};

int main()
{
    Storage store;

    {
        Derived d;

        store.AddItem("test", &d);
    }

    {
        Derived2 d2;

        Derived2& test = store.FindItem<Derived2>("test");//might not throw an exception
        //test might refer to d2
    }


    return 0;
}

Avere a che fare con un void* buffer e placement new e destroy mi permetterò di inserire qualcosa lì dentro.

Basta andare con i puntatori intelligenti e imparare ad apprezzarli per la benedizione che sono.

    
risposta data 19.05.2014 - 16:09
fonte
4

Questo è, in effetti, un comportamento indefinito.

dynamic_cast deve cercare il tipo di runtime per il tuo puntatore, che in genere richiede il dereferenziamento, e questo è già UB.

Anche se un'implementazione in qualche modo evita il dereferenziamento del puntatore, ad esempio memorizzando una tabella di ricerca con il tipo di ogni oggetto live, indicizzato per intervallo di indirizzi, il fatto che il tuo oggetto è andato fuori dall'ambito indica che lo stesso indirizzo potrebbe essere riutilizzato da alcuni altro oggetto. Quindi, anche in questo scenario (molto forzato), non otterrai necessariamente il risultato desiderato.

L'unica risposta è gestire correttamente le vite. Come dice Cracket Freak, i puntatori intelligenti sono probabilmente la soluzione migliore qui.

    
risposta data 19.05.2014 - 16:15
fonte

Leggi altre domande sui tag