Я подозреваю, что Вы неправильно понимаете, как именно работает компилятор. Во-первых, после первичного этапа разбора программы становится совершенно безразлично, стоял ли оператор или перегруженная функция. С этой точки зрения совершенно равноправны оператор
new и функция
new<T>().
Во-вторых,
new, несомненно, генерируется в вызов некоторого кода. Этот код может состоять из двух вызовов (выделение памяти и конструктор) или из одного (которой вызовет и выделение памяти и конструктор). Я бы, как разработчик компилятора, предпочёл бы первый путь. Хотя бы потому, что не у всех типов есть конструкторы, а городить лишний уровень вызова — на хрен надо. Но, тем не менее, второй путь, по-видимому, законен. Передать тип параметром я, разумеется, не могу, но могу передать указатель на некоторый статический дескриптор типа.
В-третьих, компилятор совершает промежуточные шаги, о которых Вы говорите (
sizeof(), например) в любом случае, в том числе и для переопределённого
new. Так что их наличие ничего не доказывает. В этом легко убедиться, посмотрев синтаксис определения
new: он не получает параметром тип, только размер размещаемой памяти.
В целом оператор
new/
delete — это мелкое мошенничество авторов языка. При описании этих операторов мы пишем только управление памятью, но при вызове происходит ещё и вызов конструкторов/деструкторов.
Поэтому, для int будет сгенерирован вызов некоторой функции
operator new(size_t), по умолчанию находящейся в библиотеке. Если я определю её в своей программе (определив
new), вызовется моя, а не стандартная.
e2e4 писал(а):
Что, new/delete у нас находятся в какой-нибудь внешней библиотеке? Нет
Думаю, что да. И Вы можете это легко проверить, написав тест.
e2e4 писал(а):
я хотел бы иметь некоторое стандартное в рамках данного компилятора и ОС поведение new/delete для базовых классов. И если вдруг кто-то обоснованно или нет, поменял это поведение для своих классов, которыя я хочу использовать в своей программе, то у меня все равно остается возможность использовать стандартные new/delete. В случае с malloc это требует плясок с бубном на этапе компоновки.
Я сомневаюсь, что такая возможность у Вас есть. И контексте, когда кто-то
обоснованно поменял, Ваше поведение («хочу использовать стандартный») само требует обоснования. Вы уверены, что Вы этим не сломаете всю систему? Как мы уже договорились, меняли-то обоснованно.
Приведу простейший пример. Стандартная система управления памятью плохо работает в параллельной среде. Архитектор принял решение — использовать отдельные пулы памяти для каждого процесса. Всё хорошо, никаких блокировок и инверсий приоритетов. И тут приходите Вы…
e2e4 писал(а):
free() получает указатель только для того, видимо, чтобы по его значению узнать, с какого адреса, и сколько (внутренние таблицы malloc?) байтов пометить как свободные.
delete же, будучи оператором языка, а не библиотечной функцией, совершенно точно знает тип, на который указывает указатель, sizeof этого типа, адрес деструктора этого типа, и действует соответственно этим знаниям. С помощью delete Вы не сможете удалить объект типа, скажем, int, как объект типа, скажем, float.
По поводу операторности
delete — см. выше. В языке нет способа передать тип параметром.
e2e4 писал(а):
Однако, тот пример, что привели Вы, является известным узким местом языка Си++, и заключается в том, что указателю на класс-наследник можно свободно присвоить адрес класса-родителя. Этот недостаток, видимо, чем-то обусловлен, но я уже забыл, чем именно. В любом случае, такие фокусы - на совести программиста, он сознательно обошел механизм проверки типов, и флаг ему в руки тогда, компилятор удаляется.
Не хило! Это же основа основ так называемого ООП!
Я не обхожу проверку типов. Я сознательно создаю тип-наследник, и присваиваю его указателю на тип-родитель. С ним можно делать всё, что можно с родителем. Только вот... освобождать нельзя. Потому, что
delete не получает информацию о типе объекта, как Вы говорите. Он получает информацию только о типе указателя, что не одно и тоже.
А вот информацию о размере объекта он и вовсе не получает. Как и в случае с
free(), она хранится в самом объекте, обычно в префиксе перед возвращаемым Вам указателем.
Более того, как только Вы сделаете деструктор виртуальным, всё заработает правильно.