2008-08-14 11 views
22

Qualcuno sa come posso, in codice C++ indipendente dalla piattaforma impedire la creazione di un oggetto sull'heap? Cioè, per una classe di "Pippo", voglio impedire agli utenti di fare questo:Come impedire la creazione di un oggetto sull'heap?

Foo *ptr = new Foo; 

e solo permettere loro di fare questo:

Foo myfooObject; 

Qualcuno ha qualche idea?

Cheers,

+2

Perché vuoi farlo? –

+0

Al contrario, che può anche essere interessante per i lettori: http://stackoverflow.com/questions/124880/is-it-possible-to-prevent-stack-allocation-of-an-object-and-only-allow- futuro – kevinarpe

risposta

24

Nick's answer è un buon punto di partenza, ma incompleta, come è effettivamente necessario sovraccaricare:

private: 
    void* operator new(size_t);   // standard new 
    void* operator new(size_t, void*); // placement new 
    void* operator new[](size_t);  // array new 
    void* operator new[](size_t, void*); // placement array new 

(buona pratica di codifica suggerirebbe si dovrebbe anche sovraccaricare l'eliminazione ed eliminare gli operatori [] - lo farei, ma dal momento che non stanno andando a ottenere chiamato non è davvero necessario.)

Pauldoo è anche corretto che questo non sopravvive aggregando su Foo, anche se non sopravvive che eredita da Foo. Potresti fare qualche modello di meta-programmazione magica per AIUTARE a prevenire questo, ma non sarebbe immune da "utenti malvagi" e quindi probabilmente non vale la complicazione. La documentazione su come dovrebbe essere usata, e la revisione del codice per assicurarsi che sia usata correttamente, sono l'unico modo ~ 100%.

+1

Un costruttore privato combinato con un metodo statico pubblico (return-by-value) può ottenere lo stesso risultato? – kevinarpe

+1

@kevinarpe Dipende da come letteralmente stiamo leggendo la domanda. Il codice esatto 'Foo myfooObject;' dalla domanda non si compilerebbe se lo facessi. Detto questo, preferisco un approccio più simile a quello che stai suggerendo, se sto cercando di controllare come vengono creati gli oggetti. –

-1

Si potrebbe dichiarare una funzione chiamata "operatore nuovo" all'interno della classe Foo, che avrebbe bloccato l'accesso alla forma normale di nuovo.

È questo il tipo di comportamento che si desidera?

7

È possibile sovraccaricare nuovo per Foo e renderlo privato. Ciò significherebbe che il compilatore si lamenterebbe ... a meno che tu non stia creando un'istanza di Foo sull'heap dall'interno di Foo. Per cogliere questo caso, potresti semplicemente non scrivere il nuovo metodo di Foo e quindi il linker si lamenterebbe dei simboli non definiti.

class Foo { 
private: 
    void* operator new(size_t size); 
}; 

PS. Sì, so che questo può essere aggirato facilmente. Non lo raccomando davvero - penso che sia una cattiva idea - stavo solo rispondendo alla domanda! ;-)

-1

Non sono sicuro se questo offre opportunità in fase di compilazione, ma hai visto sovraccaricare il "nuovo" operatore per la tua classe?

6

non so come farlo in modo affidabile e in modo portatile .. ma ..

Se l'oggetto è in pila allora si potrebbe essere in grado di far valere all'interno del costruttore che il valore di ' questo 'è sempre vicino al puntatore dello stack. C'è una buona probabilità che l'oggetto sia in pila, se questo è il caso.

credo che non tutte le piattaforme implementano loro stack nella stessa direzione, così si potrebbe desiderare di fare un test una tantum quando l'applicazione inizia a verificare in che modo la pila cresce .. O fare un po 'fudge:

FooClass::FooClass() { 
    char dummy; 
    ptrdiff_t displacement = &dummy - reinterpret_cast<char*>(this); 
    if (displacement > 10000 || displacement < -10000) { 
     throw "Not on the stack - maybe.."; 
    } 
} 
+0

Approccio interessante! – hackworks

+0

Penso che questo e il manichino saranno sempre vicini l'un l'altro, non importa se sono nell'heap o nello stack – Vargas

+1

@Vargas - Non sono d'accordo. 'dummy' sarà sempre in pila, perché è una variabile locale automatica. Il puntatore 'this' può puntare allo stack (se FooClass è usato come variabile locale) o all'heap (se FooClass è allocato sull'heap o aggregato all'interno di una classe che viene quindi allocata nell'heap). – pauldoo

3

@ Nick

Questo potrebbe essere aggirato creando una classe che deriva da o aggregati Foo. Penso che quello che suggerisco (anche se non robusto) funzionerebbe ancora per le classi derivate e aggregatrici.

es:

struct MyStruct { 
    Foo m_foo; 
}; 

MyStruct* p = new MyStruct(); 

Qui ho creato un'istanza di 'Foo' sul mucchio, bypassando nuovo operatore nascosto di Foo.

+0

+1 Bel trucco! Anche il cast di tipo può essere d'aiuto – Viet

0

È possibile dichiararlo come interfaccia e controllare la classe di implementazione più direttamente dal proprio codice.

0

Perché le intestazioni di debug possono ignorare l'operatore firma nuova, è meglio usare le firme ... come rimedio completo:

private: 
void* operator new(size_t, ...) = delete; 
void* operator new[](size_t, ...) = delete; 
0

questo può essere evitato facendo i costruttori privati ​​e fornendo un membro statico a creare un oggetto nello stack

Class Foo 
{ 
    private: 
     Foo(); 
     Foo(Foo&); 
    public: 
     static Foo GenerateInstance() { 
      Foo a ; return a; 
     } 
} 

questo renderà la creazione dell'oggetto sempre nello stack.