Рассмотрим следующий код:Как свернуть конструктор с новым (std :: nothrow)?
#include <new>
#include <malloc.h>
#include <stdio.h>
void * operator new(size_t size) {
void *res;
if (size == 1) {
res = NULL;
} else {
res = malloc(size);
}
fprintf(stderr, "%s(%zu) = %p\n", __PRETTY_FUNCTION__, size, res);
if (res == NULL) throw std::bad_alloc();
return res;
}
void * operator new(size_t size, const std::nothrow_t&) {
void *res;
if (size == 1) {
res = NULL;
} else {
res = malloc(size);
}
fprintf(stderr, "%s(%zu) = %p\n", __PRETTY_FUNCTION__, size, res);
return res;
}
void operator delete(void *ptr) {
fprintf(stderr, "%s(%p)\n", __PRETTY_FUNCTION__, ptr);
free(ptr);
}
void operator delete(void *ptr, const std::nothrow_t&) {
fprintf(stderr, "%s(%p)\n", __PRETTY_FUNCTION__, ptr);
free(ptr);
}
class Foo { };
class Bar {
public:
Bar() : ptr(new Foo()) {
fprintf(stderr, "%s: ptr = %p\n", __PRETTY_FUNCTION__, ptr);
}
Bar(const std::nothrow_t&) noexcept : ptr(new(std::nothrow) Foo()) {
fprintf(stderr, "%s: ptr = %p\n", __PRETTY_FUNCTION__, ptr);
}
~Bar() noexcept {
delete ptr;
}
Foo *ptr;
};
class Baz {
public:
Baz() : ptr(new Foo()) {
fprintf(stderr, "%s: ptr = %p\n", __PRETTY_FUNCTION__, ptr);
}
~Baz() {
delete ptr;
}
Foo *ptr;
};
int main() {
Bar *bar = new(std::nothrow) Bar(std::nothrow_t());
if (bar != NULL) {
delete bar;
} else { fprintf(stderr, "bad alloc on Bar(std::nothrow_t())\n"); }
fprintf(stderr, "\n");
try {
bar = new(std::nothrow) Bar();
delete bar;
} catch (std::bad_alloc) { fprintf(stderr, "bad alloc on Bar()\n"); }
fprintf(stderr, "\n");
try {
Baz *baz = new Baz();
delete baz;
} catch (std::bad_alloc) { fprintf(stderr, "bad alloc on Baz()\n"); }
}
Это производит следующий вывод:
void* operator new(size_t, const std::nothrow_t&)(8) = 0x1fed010
void* operator new(size_t, const std::nothrow_t&)(1) = (nil)
Bar::Bar(const std::nothrow_t&): ptr = (nil)
void operator delete(void*)((nil))
void operator delete(void*)(0x1fed010)
void* operator new(size_t, const std::nothrow_t&)(8) = 0x1fed010
void* operator new(std::size_t)(1) = (nil)
void operator delete(void*, const std::nothrow_t&)(0x1fed010)
bad alloc on Bar()
void* operator new(std::size_t)(8) = 0x1fed010
void* operator new(std::size_t)(1) = (nil)
void operator delete(void*)(0x1fed010)
bad alloc on Baz()
Как вы можете видеть выделение первого бара успешно, несмотря на выделение Foo неудачу. Второе распределение Bar и alloaction Baz должным образом происходит с использованием std :: bad_alloc.
Теперь мой вопрос: как сделать «новый (std :: nothrow) Bar (std :: nothrow_t());" освободите память для бара и верните NULL, когда Foo не сможет выделить? Является ли инверсия зависимостей единственным решением?
Должен ли быть синтаксис 'new (std :: nothrow) Bar (std :: nothrow_t());'? Как насчет 'Bar :: create (std :: nothrow_t {})'? Или 'create (std :: nothrow_t {})'? –
Yakk
Вы имеете в виду класс Bar {public: static Bar * create(); private: Bar (Foo * foo); }? Это будет использовать инверсию зависимостей. –
Нет, 'Bar' все еще может создать' Foo'. Вы просто обнаруживаете отказ в своей функции-создателя и освобождаете и возвращаете нуль. Детали менее важны, чем тот факт, что мы больше не делаем это с 'new', и это может быть неприемлемо для вас. – Yakk