В чём разница между этими действиями?
static void operator delete (void *p) { ::delete p; }
static void operator delete (void *p) { ::operator delete(p); }
Кажется, что всё работает в обоих случаях: https://ideone.com/cQ8lTJ
#include <iostream>
using namespace std;
struct a { static void operator delete (void *p) { ::delete p; } };
struct b { static void operator delete (void *p) { ::operator delete(p); } };
int main()
{
delete new a();
delete new b();
cout << "Done :)" << endl;
return 0;
}
Но если добраться до предупреждений компилятора https://ideone.com/bN3XOh
prog.cpp: In static member function ‘static void a::operator delete(void*)’:
prog.cpp:5:62: warning: deleting ‘void*’ is undefined [-Wdelete-incomplete]
struct a { static void operator delete (void *p) { ::delete p; } };
^
то возникает ощущение, что он предупреждает о UB в первом варианте.
Действительно ли это UB?
Если да, то почему это всего лишь предупреждение, а не ошибка?
PS: Из похожего нашёл такой вопрос, но там про внутреннее устройство вызова delete
, причём не показывается, почему именно код из моего вопроса неверный.
operator delete
- это функция.
::operator delete(p);
- это вызов этой функции.
Выражение delete p;
- это вызов деструктора, поиск указателя на полный объект и вызов функции operator delete
для этого полного объекта.
Операция delete
для void*
не имеет смысла, о чем и говорит компилятор.
В контексте другой функции operator delete
оно особенно не имеет смысла.
Более подробно про выражение delete
и функции operator delete
можно почитать тут.
В качестве небольшого дополнения к ответу @Abyx:
Согласно стандарту, 8.5.2.5/1:
The operand shall be of pointer to object type or of class type. If of class type, the operand is contextually implicitly converted to a pointer to object type.⁸²
⁸²⁾ This implies that an object cannot be deleted using a pointer of type void*
because void
is not an object type.
Таким образом, использование ::delete p;
(первый вариант) запрещено стандартом, и ведёт таким образом к UB. Почему это объявлено UB, а не ошибкой компиляции, мне сложно судить.
Ещё одна причина, по которой такой вызов проблематичен: (стандарт, 8.5.2.5/1):
In a single-object delete expression, if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.
Немного кода для иллюстрации ответа @Abyx'а
#include <iostream>
#include <string>
using namespace std;
class Foo {
public:
Foo (const std::string &_str): str(_str) {
std::cout << str << "::" << __func__<< '\n';
}
~Foo () { std::cout << str << "::" << __func__<< '\n'; }
static void* operator new (size_t sz) {
std::cout << __func__<< " sz(" << sz << ')' << '\n';
return ::operator new(sz);
}
static void operator delete (void* p) {
std::cout << __func__ << '\n'; return ::operator delete(p);
}
std::string str;
};
int main(int /*argc*/, char* /*argv*/[])
{
Foo *foo = new Foo("foo1");
delete foo;
std::cout << '\n';
foo = new Foo("foo2");
Foo::operator delete(foo);
return 0;
}
Вывод:
operator new sz(32)
foo1::Foo
foo1::~Foo
operator delete
operator new sz(32)
foo2::Foo
operator delete
т.е. Как и ожидается operator delete не вызывает деструктор.
::delete p;
и ::operator delete(p);
- две альтернативных записи вызова глобального оператора delete
.
А предупреждение компилятора справедливо вызвано попыткой удаления указателя на незавершенный тип (incomplete type). Однако неопределенное поведение конкретно в этом случае заключается не в вызове delete
именно для типа void*
(хотя он и является незавершенным), а в том, что тип, на который указывает указатель, не является непосредственно типом или одним из подтипов типа, который был и использован при вызове оператора new
, создавшего этот объект. Причем диагностического сообщения в этом случае вообще не требуется. Скорее всего, в компиляторе еще не реализовали дополнительные диагностики для вызова delete с использованием синтаксиса функции, так как такое встречается сравнительно редко.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Какие существуют виды рекламных бордов и как выбрать подходящий?
Можно ли с помощью DCE/RPC узнать нужные переменные окружения или пути к системных папкам, таким как c:program file или home user directory?
Недавно был вопрос о том, как объявить функцию с n аргументами заданного типаУ меня возник противоположный вопрос
Всем привет, написал код по поиску простых чисел и запоминания этих чисел и их степеней: