Domanda

Attualmente sto soffrendo di una scoreggia cervello.Ho fatto questo prima, ma non ricordo la sintassi esatta e non posso guardare il codice che ho scritto, perché ho lavorato presso un'altra società del tempo.Ho questo accordo:

class P
{
// stuff
};

class PW : public P
{
// more stuff
};

class PR : public P
{
// more stuff
};

class C
{
public:
    P GetP() const { return p; }    
private:
    P p;
};

// ...
    P p = c.GetP( ); // valid
    PW p = c.GetP( ); // invalid
    PR p = c.GetP( ); // invalid
// ...

Ora vorrei fare P intercambiabili con PW e PR (e quindi PW e PR possono essere scambiate).Probabilmente potrei ottenere via con cast, ma questa modifica del codice si è verificato un bel paio di volte in questo modulo solo.Sono abbastanza sicuro che si tratta di un operatore, ma per la vita di me non riesco a ricordare cosa.

Come faccio a P intercambiabili con PW e PR con quantità minima di codice?

Aggiornamento: Per dare un po ' più di chiarimenti.P sta per il Progetto e la R e W sta per lettura e scrittura rispettivamente.Di tutte il Lettore è il codice per il caricamento - n variabili, e lo scrittore ha il codice per la semplice Scrittura.Ha bisogno di essere separati, perché la Lettura e la Scrittura sezioni dispone di varie classi di gestione e le finestre di dialogo che nessuno dei Progetti di reale preoccupazione che è la manipolazione di file di progetto.

Aggiornamento: Ho anche bisogno di essere in grado di chiamare i metodi di P e PW.Quindi, se P è un metodo a() e PW come una chiamata al metodo b() quindi ho potuto :

PW p = c.GetP();
p.a();
p.b();

E ' fondamentalmente per effettuare la conversione trasparente.

È stato utile?

Soluzione

Se vuoi compilare questa parte:



// ...
    P p = c.GetP( ); // valid
    PW p = c.GetP( ); // invalid
    PR p = c.GetP( ); // invalid
// ...

Devi essere in grado di costruire / convertire una P in una PW o in una PR. Devi fare qualcosa del genere:



class PW : public P
{
    PW(const P &);
// more stuff
};

class PR : public P
{
    PR(const P &);
// more stuff
};

O intendevi qualcosa di più simile a:


class P
{
    operator PW() const;
    operator PR() const;
// stuff
};

Altri suggerimenti

Stai cercando di forzare le variabili effettive, piuttosto che i puntatori. Per fare ciò richiederebbe un cast. Tuttavia, se la definizione della tua classe era simile a questa:

class C

    {
        public: 
            P* GetP() const { return p; }
        private:
            P* p;
    }

Quindi, se p fosse un puntatore a una P, una PW o una PR, la tua funzione non cambierebbe e qualsiasi funzione (virtuale) chiamata su P * restituita dalla funzione userebbe l'implementazione da P, PW o PR a seconda dell'elemento p ..

Immagino che la cosa chiave da ricordare sia il Principio di sostituzione di Liskov . Poiché PW e PR sono sottoclassi di P, possono essere trattati come se fossero Ps. Tuttavia, le PW non possono essere trattate come PR e viceversa.

Nel codice sopra, hai l'opposto del problema taglio .

Quello che stai cercando di fare è assegnare da una P a una PW o PR che contenga più informazioni dell'oggetto sorgente. Come fai a fare questo? Supponiamo che P abbia solo 3 variabili membro, ma PW ha 12 membri aggiuntivi - dove vengono i valori per questi quando scrivi PW p = c.GetP()?

Se questo incarico in realtà è valido, il che dovrebbe indicare un qualche tipo di stranezza progettuale, allora implementerei PW::operator=(const P&) e PR::operator=(const P&), PW::PW(const P&) e PR::PR(const P&). Ma quella notte non avrei dormito troppo bene.

Questo tipo di fa qualcosa di sensato, dato che tutto viene passato per valore. Non sono sicuro che sia quello a cui stavi pensando.

class P
{
public:
    template <typename T>
    operator T() const
    {
        T t;
        static_cast<T&>(t) = *this;
        return t;
    }
};

Per rendere il PW e PR utilizzabile tramite un P è necessario utilizzare i riferimenti (o puntatori).Così si ha realmente bisogno di t cambiare l'interfaccia del C quindi restituisce un riferimento.

Il problema principale del vecchio codice che copia di un P in PW o un PR.Questo non è andare a lavorare come la PW e PR potenzialmente avere più informazioni rispetto a P e da un tipo di prospettiva di un oggetto di tipo P non è un PW o un PR.Se PW e PR sono entrambe P.

Modificare il codice per questo e per compilare:Se si desidera restituire diversi oggetti derivati da un P di classe in fase di runtime, quindi la classe C deve essere potenzialmente in grado di memorizzare tutti i diversi tipi di aspettare e di essere specializzata in fase di runtime.Così, nella classe sotto vi permetterà di specializzarsi passando un puntatore a un oggetto che dovrà essere restituito dal riferimento.Per assicurarsi che l'oggetto è eccezione sicuro ho avvolto il puntatore a un puntatore intelligente.

class C
{
    public:
        C(std::auto_ptr<P> x):
            p(x)
        {
            if (p.get() == NULL) {throw BadInit;}
        }
        // Return a reference.
        P& GetP() const { return *p; }        
    private:
        // I use auto_ptr just as an example
        // there are many different valid ways to do this.
        // Once the object is correctly initialized p is always valid.
        std::auto_ptr<P> p;
};

// ...
P&  p = c.GetP( );                   // valid
PW& p = dynamic_cast<PW>(c.GetP( )); // valid  Throws exception if not PW
PR& p = dynamic_cast<PR>(c.GetP( )); // valid  Thorws exception if not PR
// ...

Forse intendi l'operatore dynamic_cast ?

Non sono completamente intercambiabili.PW è un P.PR è una P.Ma P non è necessariamente un PW, e non è necessariamente un PR.È possibile utilizzare static_cast cast di puntatori da PW * a P *, o da PR * a P *.Non si deve usare static_cast il cast di oggetti reali alla loro super-classe a causa di "affettare".E.g.se lanci un oggetto di PW per P la roba extra in PW verrà "tagliata" fuori.È inoltre possibile utilizzare static_cast per il cast da P * a PW *.Se proprio devi farlo, utilizzare dynamic_cast, che consente di verificare in fase di esecuzione se l'oggetto è in realtà di destra sotto-classe, e vi darà un errore di run-time se non è.

Non sono sicuro di cosa significhi esattamente, ma abbi pazienza.

Lo sono già. Basta chiamare tutto P e puoi fingere che PR e PW siano P. PR e PW sono comunque diversi.

Rendere tutti e tre equivalenti comporterebbe problemi con il principio di Liskov . Ma allora perché dovresti dare loro nomi diversi se sono veramente equivalenti?

Il secondo e il terzo non sarebbero validi perché è un upcast implicito, che è una cosa pericolosa in C ++. Questo perché la classe a cui stai eseguendo il casting ha più funzionalità rispetto alla classe a cui viene assegnata, quindi a meno che tu non lo espliciti espressamente, il compilatore C ++ genererà un errore (almeno dovrebbe). Ovviamente, questo sta semplificando leggermente le cose (puoi usare RTTI per certe cose che possono essere correlate a ciò che vuoi fare in sicurezza senza invocare l'ira della cattiva oggettività) - ma la semplicità è sempre un buon modo per affrontare i problemi.

Ovviamente, come indicato in alcune altre soluzioni, puoi aggirare questo problema, ma penso che prima di provare a aggirare il problema, potresti voler ripensare il design.

Usa un riferimento o un puntatore a P anziché a un oggetto:

class C
{
 public:
  P* GetP() const { return p; }
 private:
  P* p;
};

Ciò consentirà a un PW * o un PR * di essere associato a C.p. Tuttavia, se devi passare da una P a una PW o PR, devi utilizzare dynamic_cast & Lt; PW * & Gt; (p), che restituirà la versione PW * di p o NULL di p non è un PW * (ad esempio perché è un PR *). Dynamic_cast ha comunque un certo sovraccarico ed è meglio evitarlo se possibile (usa virtuals).

Puoi anche usare l'operatore typeid () per determinare il tipo di runtime di un oggetto, ma ha diversi problemi, incluso che devi includere e che non è in grado di rilevare derivazioni extra.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top