Cum de a preveni crearea unui obiect pe halda?

Știe cineva cum pot, în codul C ++ independent de platformă, să împiedice crearea unui obiect pe morman? Adică, pentru o clasă "Foo", vreau să împiedic utilizatorii să facă acest lucru:

Foo *ptr = new Foo;

și le permiteți doar să facă acest lucru:

Foo myfooObject;

Are cineva idei?

Noroc,

0
fr hi bn
Reversul, care ar putea fi, de asemenea, interesant pentru cititori: stackoverflow.com/questions/124880/…
adăugat autor kevinarpe, sursa
De ce ai vrea să faci asta?
adăugat autor David Rodríguez - dribeas, sursa

9 răspunsuri

Ați putea declara o funcție denumită "operator nou" în cadrul clasei Foo, care ar bloca accesul la forma normală de noi.

Este acest tip de comportament dorit?

0
adăugat

Ați putea să o declarați ca o interfață și să controlați clasa de implementare mai direct din propriul cod.

0
adăugat

Nu știu cum să o fac în mod fiabil și într-un mod portabil .. dar ..

Dacă obiectul se află pe stivă, atunci este posibil să afirmați în interiorul constructorului că valoarea "acestui" este întotdeauna aproape de stivă. Există o șansă bună ca obiectul să fie în stack, dacă este cazul.

Cred că nu toate platformele implementează stack-urile în aceeași direcție, așa că poate doriți să faceți un test unic atunci când aplicația începe să verifice în ce mod crește stiva .. Sau faceți niște fudge:

FooClass::FooClass() {
    char dummy;
    ptrdiff_t displacement = &dummy - reinterpret_cast(this);
    if (displacement > 10000 || displacement < -10000) {
        throw "Not on the stack - maybe..";
    }
}
0
adăugat
@Vargas - Nu sunt de acord. "dummy" va fi întotdeauna în stack, deoarece este o variabilă locală automată. Pointerul this poate indica fie stiva (dacă FooClass este folosită ca variabilă locală), fie halda (dacă FooClass este alocată pe heap sau agregând într-o clasă care este apoi alocată pe heap ).
adăugat autor pauldoo, sursa
Cred că acest lucru și manechinul vor fi mereu aproape una de cealaltă, indiferent dacă sunt în morman sau în stack
adăugat autor Vargas, sursa
Ai dreptate, am greșit codul dummy pentru o variabilă membru ... îmi pare rău
adăugat autor Vargas, sursa
O abordare interesantă!
adăugat autor hackworks, sursa

@Nick

Acest lucru ar putea fi eludat prin crearea unei clase care derivă din sau agregă Foo. Cred că ceea ce sugerez (în timp ce nu este robust) ar continua să funcționeze pentru clasele derivate și agregate.

De exemplu:

struct MyStruct {
    Foo m_foo;
};

MyStruct* p = new MyStruct();

Aici am creat un exemplu de "Foo" pe morman, ocolind noul operator ascuns al lui Foo.

0
adăugat
+1 Trick frumos! De asemenea, turnarea de tip poate ajuta
adăugat autor Viet, sursa

Nu sunteți sigur dacă acest lucru oferă oportunități de compilare, însă ați analizat supraîncărcarea operatorului "nou" pentru clasa dvs.?

0
adăugat

Nick's answer is a good starting point, but incomplete, as you actually need to overload:

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

(Bună practica de codificare ar sugera că ar trebui să supraîncărcați operatorii de ștergere și ștergere [] - aș vrea, dar din moment ce nu vor fi chemați, nu este necesar într-adevăr ).

Pauldoo is also correct that this doesn't survive aggregating on Foo, although it does survive inheriting from Foo. You could do some template meta-programming magic to HELP prevent this, but it would not be immune to "evil users" and thus is probably not worth the complication. Documentation of how it should be used, and code review to ensure it is used properly, are the only ~100% way.

0
adăugat
Un constructor privat, combinat cu o metodă de fabricare a unui static public (return-by-value), va realiza același lucru?
adăugat autor kevinarpe, sursa
@kevinarpe Depinde de cât de literal citim întrebarea. Codul exact Foo myfooObject; din întrebare nu s-ar compila dacă ați făcut acest lucru. Asta a spus că prefer o abordare mai asemănătoare cu ceea ce sugerați, dacă încerc să controlez cum sunt create obiectele.
adăugat autor Patrick Johnmeyer, sursa

Ai putea supraîncărca noul pentru Foo și fă-l privat. Acest lucru ar însemna că compilatorul ar stârni ... cu excepția cazului în care creați o instanță de Foo pe heap din Foo. Pentru a prinde acest caz, nu puteți pur și simplu să scrieți noua metodă a lui Foo și apoi linkerul să strige despre simbolurile nedefinite.

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

PS. Da, știu că acest lucru poate fi eludat cu ușurință. Chiar nu o recomand - cred că este o idee proastă - am răspuns doar la întrebare! ;-)

0
adăugat

Deoarece antetele de depanare pot suprascrie semnătura operatorului, este mai bine să utilizați semnăturile ca o soluție completă:

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

acest lucru poate fi prevenit făcând constructorii privați și furnizând un membru static pentru a crea un obiect în stivă

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

aceasta va face crearea obiectului întotdeauna în stivă.

0
adăugat