Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

[C ] Всё ли мы знаем об операторах new и delete?

Anna | 25.06.2014 | нет комментариев
Привет! Ниже речь пойдет об знаменитых каждому операторах new и delete, вернее о том, о чем не пишут в книгах.
На написание данной статьи меня всподвигло Зачастую встречаемое заблуждение по поводу new и delete, которое я непрерывно вижу на форумах и даже(!!!) в книгах для начинающих.
Все ли мы знаем, что такое на самом деле new и delete? Либо только думаем, что знаем?
Эта статья поможет вам разобраться с этим (ну, а те, кто знают, могут покритиковать:))

Note: ниже пойдет речь экстраординарно об операторе new, для других форм оператора new и для всех форм оператора delete все ниженаписанное также является правдой и применимо по аналогии. Выходит, начнем с того, что обыкновенно пишут в книгах для начинающих, когда описывают new (текст взят «с потолка», но вцелом соответствует правде):

Оператор new выделяет память огромнее либо равную требуемому размеру и, в различие от функций языка С, вызывает конструктор(ы) для объекта(ов), под которые память выделена… вы можете перегрузить (где-то пишут реализовать) оператор new под свои нужды.

И для примера показывают простую перегрузку (реализацию) оператора new, прототип которого выглядит так
void* operator new (std::size_t size) throw (std::bad_alloc);

На что хочется обратить внимание:
1. Нигде не разделяют new key-word языка С и оператор new, всюду о них говорят как об одной сущности.
2. Всюду пишут, что new вызывает конструктор(ы) для объекта(ов).
И первое и второе является распространенным заблуждением.

Но не будем верить на книги для начинающих, обратимся к Эталону, а именно к разделу 5.3.4 и к 18.6.1, в которых собственно и раскрывается (вернее приоткрывается) тема данной статьи.

5.3.4
The new-expression attempts to create an object of the type-id (8.1) or new-type-id to which it is applied. /*дальше нам не интересно*/
18.6.1
void* operator new(std::size_t size) throw(std::bad_alloc);
Effects: The allocation function called by a new-expression (5.3.4) to allocate size bytes of
storage suitably aligned to represent any object of that size /*дальше нам не интересно*/

Здесь мы теснее видим, что в первом случае new именуется как expression, а во втором он объявлен какoperator. И это подлинно 2 различные сущности!
Испробуем разобраться отчего так, для этого нам потребуются ассемблерные листинги, полученные позже компиляции кода, использующего new. Ну, а сейчас обо все по порядку.

new-expression — это оператор языка, такой же как ifwhile и т.д. (правда if, while и т.д. все же именуются как statement, но отбросим лирику) Т.е. встречая его в листинге компилятор генерирует определенный код, соответствующий этому оператору. Так же new — это одно из key-words языка С , что еще раз подтверждает его общность с if‘ами, for’ами и т.п. А operator new() в свою очередь — это легко одноименная функция языка С , поведение которой дозволено переопределить. ЗНАчИМО — operator new() НЕ вызывает конструктор(ы) для объекта(ов), под тот, что(ые) выдается память. Он легко выделяет память надобного размера и все. Его различие от сишных функций в том, что он может кинуть исключение и его дозволено переопределить, а так же сделать оператором для отдельно взятого класса, тем самым переопределить его только для этого класса (остальное припомните сами:)).
А вот new-expression как раз и вызывает конструктор(ы) объекта(ов). Правда положительней сказать, что он тоже ничего не вызывает, легко, встречая его, компилятор генерирует код вызова конструктора(ов).

Для полноты картины разглядим дальнейший пример:

#include <iostream>

class Foo
{
public:
    Foo() 
    {
        std::cout << "Foo()" << std::endl;
    }
};

int main ()
{
    Foo *bar = new Foo;
}

позже исполнения данного кода, как и ожидалось, будет напечатано «Foo()». Разберемся отчего, для этого потребуется заглянуть в ассемблер, тот, что я немножко прокомментировал для комфорта.
(код получен компилятором cl, используемым в MSVS 2012, правда в основном я использую gcc, но это к делу не относится)

/Foo *bar = new Foo;
push        1  ; размер в байтах для объекта Foo
call        operator new (02013D4h)  ; вызываем operator new
pop         ecx 
mov         dword ptr [ebp-0E0h],eax  ; записываем указатель, вернувшийся из new, в bar
and         dword ptr [ebp-4],0  
cmp         dword ptr [ebp-0E0h],0  ; проверяем не 0 ли записался в bar
je          main 69h (0204990h)  ; если 0, то уходим отсель (допустимо вообще из main либо в какой-то обработчик, в данном случае неважно)
mov         ecx,dword ptr [ebp-0E0h]  ; кладем указатель на выделенную память в ecx (MSVS неизменно передает this в ecx(rcx))
call        Foo::Foo (02011DBh)  ; и вызываем конструктор
; дальше не увлекательно

Для тех, кто ничего не осознал, вот (примерно) аналог того, что получилось на сиподобном псевдокоде (т.е. не нужно пробовать это компилировать :) )

Foo *bar = operator new (1); // где 1 - требуемый размер
bar->Foo(); // вызываем конструктор

Приведенный код подтверждает все, написанное выше, а именно:
1. оператор (языка) new и operator new() — это НЕ одно и тоже.
2. operator new() НЕ вызывает конструктор(ы)
3. вызов конструктора(ов) генерирует компилятор, встречая в коде key-word «new»

Результат: верю, эта статья помогла вам осознать разницу между new-expressionи operator new() либо даже узнать, что она (эта разница) вообще существует, если кто-то не знал.

P.S. оператор delete и operator delete() имеют схожее отличие, следственно в начале статьи я сказал, что не буду его описывать. Думаю, сейчас вы осознали, отчего его изложение не имеет смысла и сумеете самосильно проверить честность написанного выше для delete.

Update:
Програжитель с ником khim в индивидуальной переписке предложил дальнейший код, тот, что отлично показывает суть написанного выше.

#include <iostream>

class Test {
public:
    Test() {
        std::cout << "Test::Test()" << std::endl;
    }

    void* operator new (std::size_t size) throw (std::bad_alloc) {
        std::cout << "Test::operator new(" << size << ")" << std::endl;
        return ::operator new(size);
    }
};

int main() {
    Test *t = new Test();
    void *p = Test::operator new(1);
}

Данный код выведет следующее

Test::operator new(1)
Test::Test()
Test::operator new(1)

что и следовало ждать.

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB