Вызов деструктора

Тема в разделе "LANGS.C", создана пользователем cresta, 11 июн 2007.

  1. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Почему деструктор класса вызывается в начале работы с классом?
    Собственно я ожидал совсем другого поведения :dntknw:

    вот такой код:

    Код (Text):
    1. /* -- это деструктор в классе -- */
    2. string:: ~string( void )
    3.     {
    4.         delete buf;
    5.         printf ("destructor\n");
    6.     }
    7. /*-------------------------------*/
    8. int main(){  
    9.     int             a;
    10.     string          str1, str2;
    11.  
    12.     str1 = "some string";
    13.     str2 = "another string";
    14.    
    15.     str1.print();
    16.     str2.print();
    17.  
    18.     return 0;
    19. }
    выводит на экран следующее:

    Код (Text):
    1. destructor
    2. destructor
    3. some string
    4. another string
    5. destructor
    6. destructor
     
  2. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    str1 = "some string";
    случаем, не разворачивается в str1 = string("some string") ? Введи тогда уж отладочные сообщения во все операции, чтобы иметь полную картину. Или смотри ассемблерный листинг.
     
  3. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    IceStudent

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

    Сам класс выглядит так:

    Код (Text):
    1.     class string
    2.     {
    3.         char        *buf;
    4.         int         length;       // длина буфера (не строки);
    5.  
    6.         public:
    7.             virtual
    8.                 ~string( void );
    9.                 string( const char *input_str = "" );
    10.                 string( const string &r );
    11.  
    12.             virtual const string &operator=( const string &r );
    13.  
    14.             virtual int operator< ( const string &r ) const;
    15.             virtual int operator> ( const string &r ) const;
    16.             virtual int operator==( const string &r ) const;
    17.             virtual void print() const;
    18.     // ...
    19.     };
    20.  
    21.    
    22.  
    23.     //----------------------------------------------------------------
    24.     inline string::string( const char *input_str /* = ""*/ )
    25.     {
    26.         length = strlen(input_str) + 1;
    27.         buf = new char[ length ];
    28.         strcpy( buf, input_str );
    29.         printf ("function inline string::string( const char *input_str /* = ""*/ )\n");
    30.         printf ("Buffer = %s\n", buf);
    31.     }
    32.    
    33.     //----------------------------------------------------------------
    34.     inline string::string( const string &r )
    35.     {
    36.         length = r.length;
    37.         buf = new char[ length ];
    38.         strcpy( buf, r.buf );
    39.         printf ("function inline string::string( const string &r )\n");
    40.     }
    41.    
    42.     //----------------------------------------------------------------
    43.     /* виртуальный */ string:: ~string( void )
    44.     {
    45.         delete buf;
    46.         printf ("function string:: ~string( void )\n");
    47.         printf ("Buffer = %s\n", buf);
    48.     }
    49.    
    50.     //----------------------------------------------------------------
    51.     /* виртуальный */ const string &string::operator=( const string &r)
    52.     {
    53.         if( this != &r )
    54.         {
    55.             if( length != r.length )
    56.             {
    57.                 free( buf );
    58.                 length = r.length;
    59.                 buf = new char[ length ];
    60.             }
    61.             strcpy( buf, r.buf );
    62.         }
    63.         printf ("operator=\n");
    64.         return *this;
    65.     }
    66.  
    67.     //----------------------------------------------------------------
    68.     /* виртуальный */ int string::operator< ( const string &r ) const
    69.     {
    70.         return strcmp(buf, r.buf) < 0;
    71.     }
    72.     //----------------------------------------------------------------
    73.     /* виртуальный */ int string::operator> ( const string &r ) const
    74.     {
    75.         return strcmp(buf, r.buf) > 0;
    76.     }
    77.     //----------------------------------------------------------------
    78.     /* виртуальный */ int string::operator==( const string &r ) const
    79.     {
    80.         return strcmp(buf, r.buf) == 0;
    81.     }
    82.     //----------------------------------------------------------------
    83.     /* виртуальный */ void string::print() const
    84.     {
    85.         printf("%s\n", buf);
    86.     }
    а теперь простенький main:


    Код (Text):
    1. int main(){  
    2.     string          str1;
    3.     str1 = "some string";
    4.     return 0;
    5. }
    Этот main последовательно вызывает следующие функции класса и показывает состояние буфера:

    Код (Text):
    1. function inline string::string( const char *input_str /* = ""*/ )
    2. Buffer =
    3. function inline string::string( const char *input_str /* = ""*/ )
    4. Buffer = some string
    5. operator=
    6. function string:: ~string( void )
    7. Buffer = some string
    8. function string:: ~string( void )
    9. Buffer = some string
    Тут у меня вопросы:
    1.Откуда берется второй вызов функции inline string::string( const char *input_str /* = ""*/ ), ведь она из функции operator= не вызывается? И как при этом в буфер попала строка "some string"?

    2.Почему два раза вызывается деструктор string:: ~string( void )?

    3.Для чего нужна функция inline string::string( const string &r ), если у нас есть оператор присваивания?
     
  4. Nouzui

    Nouzui New Member

    Публикаций:
    0
    Регистрация:
    17 ноя 2006
    Сообщения:
    856
    1. У тебя нет конструктора по-умолчанию (если бы ты вообще не написал ни одного конструктора, он бы был создан неявно, а так его нет), поэтому при объявлении
    string str1;
    выывается единственный подходящий: string::string( const char *input_str ) с аргументом по умолчанию - ""
    отсюда и получаются первые две строчки:
    function inline string::string( const char *input_str /* = ""*/ )
    Buffer =

    дальше идет:
    str1 = "some string";
    у тебя нет оператора присваивания с аргументом const char*, поэтому единственный путь для компилятора - создать временный объект, используя конструктор string::string( const char *input_str ) и уже его присвоить объекту str1:
    string tmp("some string");
    str1.operator=(tmp);
    создание неявного объекта и порождает следующие две строки:
    function inline string::string( const char *input_str /* = ""*/ )
    Buffer = some string


    2. теперь уже очевидно, почему вызываются два деструктора: один для str, другой для временного объекта
    кстати, в деструкторе ты выводишь строку по адресу buf после вызова delete buf;
    в твоем компилере это работает, но в общем случае использование указателя после освобождения указываемой им памяти некорректно

    3. конструкторы копирования используются при передаче аргументов функции по значению, а также при возврате значения объектного типа
    кажется, есть еще и другие случаи, точно не помню
     
  5. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Nouzui

    Если я правильно понял, для предотвращения создания временного объекта и связанных с эти накладных расходов правильнее будет непосредственно вызвать конструктор при определении объекта?
    Код (Text):
    1. int main{
    2.     string str1("another string");
    3.     return 0;
    4. }
     
  6. maxdiver

    maxdiver Max

    Публикаций:
    0
    Регистрация:
    18 июл 2006
    Сообщения:
    308
    Адрес:
    Саратов
    cresta
    В данном случае можно написать и так:
    Код (Text):
    1. int main{
    2.     string str1 = "another string";
    3.     return 0;
    4. }
    По определению при конструировании можно указывать и =, и (), и это будет эквивалентно (естественно, если аргумент только один). Ни в том, ни в другом случае временных объектов создаваться не будет.
     
  7. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    а проще написать перегружаемый оператор = и для char *. тогда проблема с временным объектом должна отпасть.
     
  8. asmfan

    asmfan New Member

    Публикаций:
    0
    Регистрация:
    10 июл 2006
    Сообщения:
    1.004
    Адрес:
    Abaddon
    Желательно всегда создавать конструкторы по умолчанию и конструкторы копирования.
     
  9. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    = нельзя, если конструктор явный (explicit).
     
  10. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Более или менее все понятно. Спасибо всем.