2009-10-03 8 views
7

Desidero accedere a un membro di dati privato in una classe. Non esiste alcuna funzione membro nella classe per accedere al membro dati privato. È privatoPosso trasformare un oggetto e accedere ai membri dati privati ​​in C++?

Voglio prendere la lezione e un po 'come aprirla. Un metodo consisteva nel copiare la dichiarazione della classe, rendere pubblico il membro privato e chiamare la nuova classe classe something_else. Poi faccio un reinterpretare il cast e copiare l'oggetto originale. Questo funziona. Ma voglio qualcosa di più elegante ... o forse generico ... o semplicemente un altro modo.

Quali opzioni ci sono? Posso usare void *? Posso memcpy la classe in un'altra classe vuota? Quali sono i modi per farlo ??

%

+3

Come modificare la fonte? '// private:' funzionerà bene;) – LiraNuna

+0

Penso che voglia tenere gli utenti della sua classe fuori dai guai (quindi i privati) e ha bisogno di implementare qualcosa che non rientri nel design attuale. – jdehaan

+0

Penso che tu non abbia il codice, no? –

risposta

18

Io parto dal presupposto che

  1. sei già stato attraverso la "rottura incapsulamento è male" stadio,
  2. esaurito le altre possibili soluzioni,
  3. non possono cambiare di classe intestazione.

Esistono alcuni modi per sovvertire l'accesso ai membri privati ​​di una classe, come dimostrato in GotW #76.

  1. Duplicare una definizione di classe e aggiungere una dichiarazione friend.
  2. Utilizzare macro malvagie: #define private publicprima di includere l'intestazione della classe.
  3. Scrivere una definizione di classe con un layout binario identico e utilizzare reinterpret_cast per passare dalla classe originale a una versione falsa.
  4. Specificare una funzione membro modello se ce n'è una (l'unica soluzione portatile).
+2

+1. Articolo MERAVIGLIOSO. – LiraNuna

+0

Ama i macro maligni menzionati;) – Calyth

1

Si può, ma non si dovrebbe. Gli oggetti sono solo memoria. Puoi certamente lanciare il puntatore in una classe equivalente che ha gli stessi membri ma dove tutto è pubblico. Ma perché vuoi farlo? Avete il codice di qualcun altro con cui dovete lavorare? Prendili per aggiungere metodi appropriati di accesso. Hai davvero bisogno di trattarli come membri pubblici? Cambia la classe.

Non sono proprio sicuro di cosa stai provando a fare, ma probabilmente è un errore.

0

Sono d'accordo con il commento "modifica la fonte", ma penso che dovresti aggiungere un metodo, non solo commentare il 'privato'.

Devi avere la dichiarazione della classe, quindi presumibilmente hai l'intestazione ma probabilmente non il file .cpp/qualunque. Aggiungi una funzione membro inline alla classe in una copia dell'intestazione e includi questa intestazione invece dell'originale. Dovresti comunque essere in grado di collegarti al file oggetto per il codice sorgente inaccessibile.

Naturalmente ciò conta come un attacco malvagio, aggirando le protezioni integrate nel linguaggio piuttosto che lavorare con esse. Ecco perché suggerisco l'hack minimamente malvagio - non rendere tutto privato, e se riesci a farla franca con un getter (ma senza setter) fallo. Ovviamente il vero male minimo non è farlo, se esiste un modo per evitarlo.

Ricorda, se questa è un'altra classe con cui stai lavorando, la prossima versione potrebbe essere implementata in modo diverso e potrebbe non avere quel membro.

+0

Il mio commento stava anche ritraendo che c'è un problema di progettazione da qualche parte ... – LiraNuna

3

Con l'idea suggerita nella domanda, non è necessario copiare l'oggetto originale. Se scrivi la tua variazione "tutto pubblico" della dichiarazione di classe reale, quindi lanci un puntatore a quel nuovo tipo, puoi accedere direttamente all'oggetto attraverso di esso.

Il motivo per cui nessuna di queste è una buona idea è semplice. Devi manipolare oggetti di una classe di cui non controlli l'origine (altrimenti sarai in grado di modificare la fonte per darti l'accesso di cui hai bisogno). Ma se non controlli la fonte, allora cosa succede se i manutentori cambiano il layout della loro classe? La tua versione duplicata non corrisponderà più e non ci sarà modo per il compilatore di rilevare questa discrepanza. Il risultato sarà probabilmente corruzione della memoria in fase di esecuzione.

3

Dal momento che è capito male, devo chiarire. Tutte le seguenti soluzioni non richiedono di ricompilare l'oggetto. Per usare una classe nel tuo codice, se è compilata in un file oggetto, dovresti includere il file di intestazione con la dichiarazione di quella classe.

#include <class.h> 
ObjectFoo instance; 

E è possibile (ma pericoloso se non si sta attenti) per modificare l'intestazione (a) o copiare l'intestazione in un altro luogo e comprendono che intestazione (b), senza dover ricompilare la classe stessa .

#include <class_fixed.h> 
ObjectFoo instance; 

Il codice, in cui è incluso il nuovo intestazione sarà solo pensare che all'interno del file oggetto (che non si è ricompilato!) Che troverà attuazione della classe dichiarata come in class_fixed.h. Mentre persiste la classe dichiarata come in class.h. Se modifichi gli offset dei membri (aggiungi nuovi membri per esempio) nella nuova intestazione, sei morto e il codice non funzionerà correttamente. Ma cambiare semplicemente l'accesso funziona bene. Il codice compilato non conosce l'accesso, questo è importante solo per la compilazione strana.

Questo non è sempre dannoso. Nella vita di tutti i giorni si verifica un tale cambiamento quando si installa una nuova versione di una libreria nel proprio sistema e non si ricompilano tutti i programmi che dipendono da esso. Ma dovrebbe essere gestito con cura


Ci sono diverse soluzioni.

  1. memcpy()
    non lo fanno! Non memcpy poiché la copia di oggetti a volte viene sottoposta a una politica specifica imposta dal progettista della classe. Ad esempio, auto_ptrs non può essere solo memcopied: se si memcopy il auto_ptr e quindi viene eseguito il distruttore per entrambi, si tenterà di liberare la stessa memoria due volte e il programma si bloccherà.

  2. Change private:-public: nell'intestazione o con macro
    Se la licenza lo consente, si può risolvere il problema modificando il file di intestazione che viene fornito con l'implementazione della classe. Indipendentemente dal codice sorgente dell'implementazione (ad es.cpp-file della classe) è sotto il tuo controllo non importa: la modifica privata del pubblico per i membri dei dati (nell'intestazione) è sufficiente e funziona bene anche se ti viene data una libreria solo binaria che contiene la definizione della classe. (Per le funzioni di membro cambiando l'accesso a volte cambia il suo nome interno, ma per MSVS e GCC è ok.)

  3. Aggiunta di una nuova funzione di getter
    Mentre cambiare private-public è quasi sempre bene (a meno che non si basano su specifiche in fase di compilazione controlla che dovrebbe interrompere la compilazione se la classe ha un determinato membro accessibile), l'aggiunta di una nuova funzione getter dovrebbe essere eseguita con attenzione. La funzione getter deve essere in linea (e quindi definita nel file di intestazione della classe).

  4. reinterpret_cast
    Il cast funziona bene se non sei lanciare un puntatore alla classe base dinamica (mezzi dinamici "con funzioni virtuali o basi") la cui istanza effettiva al momento del getto possono essere derivate dalla classe al particolare pezzo di codice.

  5. protected:
    E solo nel caso si è dimenticato. Il C++ può dichiarare membri protected:, cioè accessibile solo alle classi derivate dal dato. Questo potrebbe soddisfare le tue esigenze.

0

Grazie ... Volevo mostrare il codice per la mia correzione originale. La ragione per cui qualcuno si è alleato è che non posso cambiare il codice originale ... quindi devo fare un'interruzione carceraria.


#include<iostream> 
using namespace std; 

// Class Objectfoo 
// Pretend Objectfoo lives somewhere else ... I cannot open him up 

class ObjectFoo 
{ 
    private: 
    int datax; 
    public: 
    ObjectFoo() { datax = 100; } 
    void get() { cout << datax << endl;} 
}; 

// Class ObjectBar 
class ObjectBar 
{ 
    public: 
    int datax; 
}; 

ObjectFoo FOOEY; 

ObjectBar* touch_foo(int x, ObjectFoo* foo , ObjectBar* bar) 
{ 
bar = reinterpret_cast<ObjectBar*>(foo); 
bar->datax = x; 
return bar; 
} 

int main() 
{ 
    ObjectBar* bar; 

    cout << "Displaying private member in ObjectFoo i.e. ObjectFoo.datax" << endl; 
    FOOEY.get(); 

    cout << "Changing private member " << endl; 
    bar = touch_foo(5, &FOOEY, bar); 

    cout << "bar->datax = " << bar->datax << endl; 

    cout << "Displaying private member in ObjectFoo i.e. ObjectFoo.datax" << endl; 
    FOOEY.get(); 

    return 0; 
} 

Questo funziona ... ma penso che voglio qualcosa di più generico ... o più flessibile.

%

+0

Molte risposte qui presumono che non è possibile modificare il codice '.cpp' originale, mentre è possibile modificare - o semplicemente copiare , rinomina e reinclude - il file ** header **. Rileggi le risposte con questa intuizione. –

+0

È molto meglio aggiungere questa informazione alla domanda originale piuttosto che aggiungerla come risposta. – KeithB

Problemi correlati