Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > вопрос про деструкторы!


Автор: yull 24.11.2011, 20:55
Объясните пожалуйста,почему деструктор не передает параметры?
И второй вопрос:  почему не может быть два деструктора?

Автор: borisbn 24.11.2011, 21:02
Цитата(yull @  24.11.2011,  20:55 Найти цитируемый пост)
почему деструктор не передает параметры

Цитата(yull @  24.11.2011,  20:55 Найти цитируемый пост)
почему не может быть два деструктора?

по стандарту.

да и как выбирать, какой деструктор выбрать при delete obj; ?

Добавлено через 3 минуты и 16 секунд
Цитата(yull @  24.11.2011,  20:55 Найти цитируемый пост)
деструктор не передает параметры

тока щаз понял - куда не передаёт ?

Автор: hawk3500 24.11.2011, 22:08
Деструктор должен вернуть системе область памяти, выделенную при выполнении конструктора.
По этому на входе ему никакие аргументы не требуются не нужны.
Деструктор нельзя объявлять как принимающий аргументы. 
Класс может иметь сколько угодно конструкторов (с разными наборами параметров), в том числе шаблонных, и только один) деструктор.
По сути можно сказать что деструктор есть не что иное как операция free.
Конструктор и деструктор являются функциями только с точки зрения их синтаксического описания.
Конструктор нельзя вызвать в любом месте, он всегда вызывается только в момент создания объекта.
Деструкторы тоже не совсем таак сказать функции, но их можно вызвать в любом произвольном месте.
Но есть несколько моментов которые надо учитывать:
1)конструктор выделяет область памяти под переменные класса(а не под функции-т.е. для всех классов одного и тогоже типа функии одни и теже и находятся по одному и томуже адресу).
2)при создании и инициализации указателя на класс можно и нужно пользоваться функцией delete .Исключение состовляет лишь такой случай:
Код

char *buf = new char[sizeof(T)];
  T *t = new(buf) T;
  
  t->~T();

3)код деструктора реально отрабатывает раньше функции free (class)

Автор: yull 25.11.2011, 02:07
Цитата(hawk3500 @ 24.11.2011,  22:08)
Деструктор должен вернуть системе область памяти, выделенную при выполнении конструктора.
По этому на входе ему никакие аргументы не требуются не нужны.
Деструктор нельзя объявлять как принимающий аргументы. 
Класс может иметь сколько угодно конструкторов (с разными наборами параметров), в том числе шаблонных, и только один) деструктор.
По сути можно сказать что деструктор есть не что иное как операция free.
Конструктор и деструктор являются функциями только с точки зрения их синтаксического описания.
Конструктор нельзя вызвать в любом месте, он всегда вызывается только в момент создания объекта.
Деструкторы тоже не совсем таак сказать функции, но их можно вызвать в любом произвольном месте.
Но есть несколько моментов которые надо учитывать:
1)конструктор выделяет область памяти под переменные класса(а не под функции-т.е. для всех классов одного и тогоже типа функии одни и теже и находятся по одному и томуже адресу).
2)при создании и инициализации указателя на класс можно и нужно пользоваться функцией delete .Исключение состовляет лишь такой случай:
Код

char *buf = new char[sizeof(T)];
  T *t = new(buf) T;
  
  t->~T();

3)код деструктора реально отрабатывает раньше функции free (class)

Спасибо, вы мне очень помогли)

Автор: Леопольд 25.11.2011, 19:03
Деструктор нужен для того что-бы освободить ресурсы, если есть. Если предполагается удалять объект по указатель или ссылке на базовый класс, тогда деструктор должен быть виртуальным, иначе деструкторы наследников не будут вызваны - неопределённое поведение.

Конструктор не выделяет память, он её инициализирует, для этого у него есть одна особенность, которой нет у других функций - список инициализации. Выглядит вот так:
Код
struct Foo
{
    Foo() : a(0), b('\n') // после двоеточия идёт список инициализации агрегированных членов класса
    {}
private:
    int a;
    char b;
}


И конструкторы и деструкторы можно вызвать в любом месте, но двойной вызов конструктора или деструктора на один и тот же адрес памяти - неопределённое поведение. Т.е. так делать нельзя:
Код
void foo()
{
    Foo * ptr = new Foo; // выделение памяти и вызов конструктора Foo::Foo на этот участок памяти
    // эквивалентно этому: Foo * ptr = malloc(sizeof(Foo)); if(!ptr) throw std::bad_alloc(); new(ptr) Foo;
    new(ptr) Foo; // второй вызов конструктора
    ptr->~Foo(); // ручной вызов деструктора
    delete ptr; // второй вызов деструктора, следом освобождение памяти 
    // эквивалентно этому: ptr->~Foo(); free(ptr);
}


Есть определённый порядок вызова конструктора/деструктора для агрегированных типов и базовых классов. Конструктор вызывается сначала для базовых классов, если есть, потом для агрегированных типов в порядке их определения в классе (но не в списке инициализации), потом для самого класса.
Порядок вызова деструктора полностью противоположен.

Автор: maxim1000 25.11.2011, 19:22
Автоматический вызов деструктора - одна из важнейших особенностей C++.

Если программисту приходится участвовать в этом процессе, передавая параметры или выбирая один из двух вариантов, то этот процесс становится гораздо менее автоматическим.

Автор: hawk3500 25.11.2011, 20:11
Цитата(Леопольд @ 25.11.2011,  19:03)
Конструктор не выделяет память, он её инициализирует, для этого у него есть одна особенность, которой нет у других функций - список инициализации.

Т.е. как это не выделяет?
А кто тогда выделяет??
При создании объекта и вызове конструктора выделяется память.
А вызов конструктора без создания объекта не возможен.

Автор: Леопольд 25.11.2011, 20:28
Цитата(hawk3500 @  25.11.2011,  20:11 Найти цитируемый пост)
При создании объекта и вызове конструктора выделяется память.
Память выделяется до того как будет вызван конструктор.
Память может быть выделена в статической области данных процесса - объект "живёт" до тех пор, пока жив процесс.
На стеке - память автоматически выделяется и освобождается на стеке процесса (при вызове функций, например)
В куче - память выделяется программистом вручную, обычно при помощи оператора new. Освобождается тоже вручную, обычно при помощи оператора delete. Процесс освобождения желательно автоматизировать при помощи умных указателей.

Автор: hawk3500 25.11.2011, 20:41
Ага, понял  о чём вы.

Автор: bems 25.11.2011, 23:12
Цитата(hawk3500 @  24.11.2011,  22:08 Найти цитируемый пост)
1)конструктор выделяет область памяти под переменные класса(а не под функции-т.е. для всех классов одного и тогоже типа функии одни и теже и находятся по одному и томуже адресу).
вроде это не обязано быть так во всех реализациях

Автор: mes 26.11.2011, 09:14
Цитата(hawk3500 @  25.11.2011,  19:11 Найти цитируемый пост)
А вызов конструктора без создания объекта не возможен. 

смотрите размещающий new, который может создать объект в любой заранее выделенной памяти..

Автор: boostcoder 26.11.2011, 10:06
Цитата(hawk3500 @  25.11.2011,  20:11 Найти цитируемый пост)
Т.е. как это не выделяет?
А кто тогда выделяет??

компилятор подсчитывает размер класса эквивалентно сипользованию sizeof(). полученное значение передается оператору new, результат которого, используется по аналогии с placement new.
т.е. такой вот "стенд" получился:
Код

#include <iostream>
#include <sstream>
#include <iomanip>

/***************************************************************************/

std::string hex_dump(const char* buf, size_t len) {
   const unsigned char* buffer = reinterpret_cast<const unsigned char*>(buf);
   std::stringstream os;
   if ( !buffer || len <= 0 ) return os.str();

   size_t addr = 0;

   size_t n = 0, idx = 0, cnt2 = 0;
   os << std::endl;
   for ( n = 0; n < len; ++n ) {
      if ( cnt2 == 0 ) {
         os << std::setw(7) << std::setfill('0') << addr << ": ";
         addr += 16;
      }
      cnt2 = (cnt2 + 1) % 18;
      if ( cnt2 <= 16 ) {
         os << std::hex << std::setw(2) << std::setfill('0') << (size_t)buffer[n] << " ";
      } else {
         os << "  ";
         os << std::setfill(' ');
         for ( idx = n - cnt2 + 1; idx < n; ++idx) {
            if ( buffer[idx] < 32 || 126 < buffer[idx] ) {
               os << '.';
            } else {
               os << buffer[idx];
            }
         }
         os << std::endl << std::dec;
         cnt2 = 0;
         n--;
      }
   }

   os << std::setfill(' ');

   for ( idx = cnt2+1; idx <= 16 ; ++idx ) {
      os << std::setw(2) << "-- ";
   }
   os << "  ";

   for ( idx = n - cnt2; cnt2 <= 16 && idx < n; ++idx ) {
      if ( buffer[idx] < 32 || 126 < buffer[idx] ) {
         os << '.';
      } else {
         os << buffer[idx];
      }
   }
   os << std::endl;

   return os.str();
}

/***************************************************************************/

struct type {
   type()
      :i(3)
      ,d(3.14)
   {std::cout << "type ctor" << std::endl;}
   ~type()
   {std::cout << "type dtor" << std::endl;}

   int i;
   double d;
};

int main() {
   std::cout << "sizeof(type) = " << sizeof(type) << std::endl;

   char buf[sizeof(type)] = {0};
   std::cout << "before placement new:" <<
   hex_dump(buf, sizeof(type)) << std::endl;

   type* p1 = new(buf)type;

   std::cout << "after placement new:" <<
   hex_dump(buf, sizeof(type)) << std::endl;
   
   p1->~type();

   std::cout << "after delete:" <<
   hex_dump(buf, sizeof(type)) << std::endl;
}


http://liveworkspace.org/code/444383e6535a6cc043662995399a6cd1
вывод:
Цитата

sizeof(type) = 12
before placement new:
0000000: 00 00 00 00 00 00 00 00 00 00 00 00 -- -- -- --   ............

type ctor
after placement new:
0000000: 03 00 00 00 1f 85 eb 51 b8 1e 09 40 -- -- -- --   .......Q...@

type dtor
after delete:
0000000: 03 00 00 00 1f 85 eb 51 b8 1e 09 40 -- -- -- --   .......Q...@

обрати внимание на то, что delete применять к p1 нельзя из-за того, что он пытается вернуть выделенную память куче, в то время, как она на самом деле была выделена на стеке. а так же на то, что деструктор не затирает память, как иногда встречаются заблуждения.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)