Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: Ondřej Novák 12. 10. 2011, 15:20:02
-
Programování zdar.
Řeším tu jednu nekonzistenci okolo přetěžování new a delete. Chtěl bych si ujistit, že jsem to pochopil dobře, a že mi nic neuniklo.
Mohu si u vlastní třídy přetížit operator new s placementem
void *operator new(size_t sz, void *a, char *c,...)
Pokud chci objekt té třídy dealokovat nějakým specifickým způsobem, musím tam mít něco takového
void operator delete(void *ptr, size_t sz)
To je výborný, protože hodnotu sz nemusím nikam ukládat. Jenže je zde ještě jedna specifická a povinná funkce
void operator delete(void *ptr, void *a, char *c,...)
která se volá v případě, že konstruktor hodí výjimku. Zkoušel jsem tam narvat size_t sz, a vypadá to, že to sebralo jen GCCčko, ale moc si s tím nejsem jist, protože jsem to netestoval. Rozhodně mi u toho MSVC hodí warning, protože nedokáže spárovat placement new s ...
void operator delete(void *ptr, size_t sz, void *a, char *c,...)
Chápu to tedy dobře tak, že si velikost alokované paměti stále musím ukládat někam vedle? A že se jedná o nekonzistenci v normě, nebo jen v MSVC?
-
Tak si odpovím sám, dá se to obejít pomocí pomocného objektu, který si velikost zapamatuje. Vyzkoušený příklad v MSVC i v GCC
Objekt Allocator zde představuje placeholder, pro nějaký objekt provádějící alokace kdesi a předpokládá se, že místo standardních new a delete se bude volat on s parametry velikost, potažmo pointer a velikost. Alokace je zde provedena přes new a delete. To co je třeba sledovat je jakým způsobem se zjišťuje velikost alokované paměti při delete bez nutnosti tuto informaci ukládat spolu s alokovaným blokem
#include <iostream>
#include <stdexcept>
#include <memory>
class Allocator {
public:
};
class AllocatorHelper {
public:
AllocatorHelper(Allocator &hlp):rsz(0),hlp(hlp) {
std::cout << "Allocator helper construction. " << std::endl;
}
~AllocatorHelper() {
std::cout << "Allocator helper destructor. " << std::endl;
}
void rememberSize(size_t sz) const {
rsz = sz;
std::cout << "Allocator remembering size: " << sz << std::endl;
}
size_t getSize() const {return rsz;}
protected:
mutable size_t rsz;
Allocator &hlp;
};
class TestClass {
int a;
public:
TestClass(int a):a(a) {}
virtual ~TestClass() {}
void *operator new(size_t sz, const AllocatorHelper &alc) {
std::cout << "Allocating: " << sz << std::endl;
alc.rememberSize(sz);
return ::operator new(sz);
}
void operator delete(void *ptr, const AllocatorHelper &alc) {
std::cout << "In-exception deallocating: " << alc.getSize() << std::endl;
::operator delete(ptr);
}
void operator delete(void *ptr, size_t sz) {
std::cout << "Normal deallocating: " << sz << std::endl;
::operator delete(ptr);
}
};
class TestClass2: public TestClass {
double z;
public:
TestClass2(int a, double z):TestClass(a) {throw std::runtime_error("nejde to");}
};
int main(int argc, char * argv[])
{
try {
Allocator hlp;
std::auto_ptr<TestClass> a(new(hlp) TestClass(10));
std::auto_ptr<TestClass> b(new(hlp) TestClass2(10,12.0));
return 0;
} catch (std::exception &e) {
std::cout << e.what() << std::endl;
}
}
-
Hmm, vypadá, že toho si nikdo ani při revizi ISO C++ nějak nevšiml.
Ve standardu se totiž píše, že funkce operator delete může mít druhý parametr s velikostí, ale zároveň se u placement delete funkce píše, že má totožné argumenty jako placement new funkce s výjimkou toho prvního, který je void* místo std::size_t. Zřejmě to tedy závisí na implementaci a navíc to asi může mít fatální následky, pokud v parametru placement new použijete std::size_t.
-
Stene, to je dobra pripominka, vim, ze jsem nekde mel kod, kde se do new daval extra parametr urcujici o kolik vetsi ma byt alokovany prostor pro objekt, do ktereho jsem pak ukladal nejaka variabilni data. Zrovna placement delete v tomhle tvaru (se size_t) jsem tam mel. Nastesti se to uz nikde nepouziva