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.
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.