Конструктор и деструктор...
От: Ovl Россия  
Дата: 08.08.03 11:35
Оценка:
Добрый вечер.
Такой вопрос есть насчет new, delete, конструктора и деструктора.

Есть класс, членами в котором являются указатели на класс CPString. он похож на то, о чем говорили в
http://www.rsdn.ru/Forum/?mid=338555
Автор: Кодт
Дата: 29.07.03
, точнее говоря я его оттуда "скопировал".

class CPString
{
private:
    CHAR* m_szStr;
public:
    CPString();
    ~CPString();

    VOID    Set     (CHAR* src);
    CHAR*   Get     ();
    DWORD   Length  () const;

    CPString& operator = (CHAR* src);

};
//
//  функции такие же как в форуме
//

class CStringTable
{
public:
    CPString*      m_nTableName;

    CPString**     m_nNames;
    CPString**     m_nMessages;

    CStringTable();
    ~CStringTable();
}
//
// здесь напишу только конструктор и деструктор.
//

CStringTable::CStringTable()
{
    m_nTableName = new CPString();

    m_nNames = new CPString*[99];
    for (BYTE i = 0; i < 99; i++)
    {
        m_nNames[i] = new CPString();
    }
    m_nMessages = new CPString*[145];
    for (BYTE i = 0; i < 145; i++)
    {
        m_nMessages[i] = new CPString();
    }
};

CStringTable::~CStringTable()
{
    delete    m_nTableName;

    for (BYTE i = 0; i < 99; i++)
    {
        delete m_nNames[i];
    }
    delete[] m_nNames;
    for (BYTE i = 0; i < 145; i++)
    {
        delete m_nMessages[i];
    }
    delete[] m_nMessages;
};


Первый вопрос: в деструктор при delete m_nTable (и у остальных) для CPString вызовется деструктор или просто указатель в ноль установит?

Далее, в программе делаю vector<CStringTable*>.

vector<CStringTable*> m_vcStrings;

CStringTable* cNew1STable = new CStringTable();
m_vcStrings.push_back(cNew1STable);
CStringTable* cNew2STable = new CStringTable();
m_vcStrings.push_back(cNew2STable);

После использования мне вектор надо очистить

Так?
for (int i = 0; i < (int) m_vcStrings.size(); i++)
{
   m_vcStrings[i]->~CStringTable();
}
m_vcStrings.clear();

или так?
for (int i = 0; i < (int) m_vcStrings.size(); i++)
{
   delete m_vcStrings[i];
}
m_vcStrings.clear();


Или можно вообще этого не делать — vector<>::clear() сам очистит память от всего?

Вызывает ли delete деструктор объекта?

Заранее спасибо
Read or Die!
Как правильно задавать вопросы
Как правильно оформить свой вопрос
Автор: anvaka
Дата: 15.05.06
Re: Конструктор и деструктор...
От: Кодт Россия  
Дата: 08.08.03 11:56
Оценка:
Здравствуйте, Ovl, Вы писали:

Ovl>
Ovl>class CPString
Ovl>{
Ovl>private:
Ovl>    CHAR* m_szStr;
Ovl>public:
Ovl>    CPString();
Ovl>    ~CPString();

Ovl>    VOID    Set     (CHAR* src);
Ovl>    CHAR*   Get     ();
Ovl>    DWORD   Length  () const;

Ovl>    CPString& operator = (CHAR* src);

// Не хватает конструктора копирования.
// По умолчанию компилятор создаст copy-ctor дебильный, который скопирует указатель.

Ovl>};

Ovl>class CStringTable
Ovl>{
Ovl>public:
Ovl>    CPString*      m_nTableName;

// а смысл? Почему нельзя было сделать просто
        CPString       m_nTableName;

Ovl>    CPString**     m_nNames;
Ovl>    CPString**     m_nMessages;

// Тот же самый вопрос.
// И вообще, почему не векторы?
        vector<CPString> m_nNames, m_nMessages;

Ovl>    CStringTable();
Ovl>    ~CStringTable();

// как насчет конструктора копирования?
// если даже он не нужен - сделай хотя бы заглушку, чтобы компилятор по рогам надавал
private: CStringTable(CStringTable const&) { assert(!"Нафиг"); }
Ovl>}

Ovl>CStringTable::CStringTable()
Ovl>{
Ovl>    m_nTableName = new CPString();

// члены - не-указатели - не нуждаются в лишнем new.

Ovl>    m_nNames = new CPString*[99];
Ovl>    for (BYTE i = 0; i < 99; i++)
Ovl>    {
Ovl>        m_nNames[i] = new CPString();

// смысл массива указателей - только если ты каждую строчку конструируешь отдельным образом

Ovl>    }
Ovl>    m_nMessages = new CPString*[145];
Ovl>    for (BYTE i = 0; i < 145; i++)
Ovl>    {
Ovl>        m_nMessages[i] = new CPString();
Ovl>    }
Ovl>};

Ovl>CStringTable::~CStringTable()
Ovl>{
Ovl>    delete    m_nTableName;

Ovl>    for (BYTE i = 0; i < 99; i++)
Ovl>    {
Ovl>        delete m_nNames[i];
Ovl>    }
Ovl>    delete[] m_nNames;
Ovl>    for (BYTE i = 0; i < 145; i++)
Ovl>    {
Ovl>        delete m_nMessages[i];
Ovl>    }
Ovl>    delete[] m_nMessages;
Ovl>};
Ovl>


Ovl>Первый вопрос: в деструктор при delete m_nTable (и у остальных) для CPString вызовется деструктор или просто указатель в ноль установит?


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

Ovl>Далее, в программе делаю vector<CStringTable*>.


Тоже вопрос: зачем вектор указателей?

Ovl>
Ovl>vector<CStringTable*> m_vcStrings;

Ovl>CStringTable* cNew1STable = new CStringTable();
Ovl>m_vcStrings.push_back(cNew1STable);
Ovl>CStringTable* cNew2STable = new CStringTable();
Ovl>m_vcStrings.push_back(cNew2STable);
Ovl>

Ovl>После использования мне вектор надо очистить

Ovl>Так?

Ovl>
Ovl>for (int i = 0; i < (int) m_vcStrings.size(); i++)
Ovl>{
Ovl>   m_vcStrings[i]->~CStringTable();

// нет!!!
// раз уж ты выделял память (new) то изволь освобождать (delete)

Ovl>}
Ovl>m_vcStrings.clear();
Ovl>

Ovl>или так?
Ovl>
Ovl>for (int i = 0; i < (int) m_vcStrings.size(); i++)
Ovl>{
Ovl>   delete m_vcStrings[i];

// так правильно.

Ovl>}
Ovl>m_vcStrings.clear();
Ovl>


Ovl>Или можно вообще этого не делать — vector<>::clear() сам очистит память от всего?


Он (грубо говоря) выполнит деструкторы указателей.
А это, фактически, деструктор целого числа. Что там разрушать?
Поэтому будет куча висячих объектов.

Ovl>Вызывает ли delete деструктор объекта?


Обязательно.

Ovl>Заранее спасибо


Ты случаем не с Паскаля на ++ пересел?
Перекуём баги на фичи!
Re: Конструктор и деструктор...
От: Bell Россия  
Дата: 08.08.03 12:00
Оценка:
Добрый день , Ovl, Вы писали:

Ovl>Добрый вечер.

...

Ovl>Первый вопрос: в деструктор при delete m_nTable (и у остальных) для CPString вызовется деструктор или просто указатель в ноль установит?


Для каждого объекта CPString сначала вызывается деструктор, затем овобождается память, который каждый из этих объектов занимал. Кстати для самого CPString я не увидел деструктора. Его нет, или ты его просто не привел?

Ovl>Далее, в программе делаю vector<CStringTable*>.


Ovl>
Ovl>vector<CStringTable*> m_vcStrings;

Ovl>CStringTable* cNew1STable = new CStringTable();
Ovl>m_vcStrings.push_back(cNew1STable);
Ovl>CStringTable* cNew2STable = new CStringTable();
Ovl>m_vcStrings.push_back(cNew2STable);
Ovl>

Ovl>После использования мне вектор надо очистить

Ovl>Так?

Ovl>
Ovl>for (int i = 0; i < (int) m_vcStrings.size(); i++)
Ovl>{
Ovl>   m_vcStrings[i]->~CStringTable();
Ovl>}
Ovl>m_vcStrings.clear();
Ovl>


Ни в коем случае. Деструкторы для объектов CStringTable конечно будут вызваны, но "сырая" память, в которой эти объекты размещались, не будет возвращена системе. Т.е. получаем memory leak.


Ovl>или так?

Ovl>
Ovl>for (int i = 0; i < (int) m_vcStrings.size(); i++)
Ovl>{
Ovl>   delete m_vcStrings[i];
Ovl>}
Ovl>m_vcStrings.clear();
Ovl>



можно и так...
А еще так:
В лоб:
for(vector<CStringTable*>::iterator it = m_vcStrings.begin(), itEnd = m_vcStrings.end(); it != itEnd; ++it )
   delete *it;



Но есть и более изощренные методы
Например MaximE в этом топике
Автор: folk
Дата: 07.08.03
предлагал такой вариант:

#include <boost/checked_delete.hpp>

// ...

for_each(m_vcStrings.end().begin(), m_vcStrings.end().end(), boost::checked_deleter<CStringTable>());




Ovl>Или можно вообще этого не делать — vector<>::clear() сам очистит память от всего?

Конечно нет.

Ovl>Вызывает ли delete деструктор объекта?

Всенепременно.

Ovl>Заранее спасибо
Любите книгу — источник знаний (с) М.Горький
Re[2]: Конструктор и деструктор...
От: Ovl Россия  
Дата: 08.08.03 12:35
Оценка:
Здравствуйте, Кодт, Вы писали:

К>// Не хватает конструктора копирования.

К>// По умолчанию компилятор создаст copy-ctor дебильный, который скопирует указатель.
Я хотел его сделать, но не знал как именно. Спасибо за пример

Ovl>>class CStringTable

Ovl>>{
Ovl>>public:
Ovl>> CPString* m_nTableName;

К>// а смысл? Почему нельзя было сделать просто

К> CPString m_nTableName;

Ovl>> CPString** m_nNames;

Ovl>> CPString** m_nMessages;

К>// Тот же самый вопрос.

К>// И вообще, почему не векторы?
К> vector<CPString> m_nNames, m_nMessages;
Я подумал, что вектор сохдает полную копию объекта и если его (объекта) размер изменяется (а для CPString это в порядке вещей), то это плохо будет. Вектор использует сам объект или только ссылку на него?

Ovl>> CStringTable();

Ovl>> ~CStringTable();

К>// как насчет конструктора копирования?

К>// если даже он не нужен — сделай хотя бы заглушку, чтобы компилятор по рогам надавал
Обязательно, теперь знаю как.

Ovl>>CStringTable::CStringTable()

Ovl>>{
Ovl>> m_nTableName = new CPString();

К>// члены — не-указатели — не нуждаются в лишнем new.

Я ведь объявлял их как указатели, так ведь?

Ovl>> m_nNames = new CPString*[99];

Ovl>> for (BYTE i = 0; i < 99; i++)
Ovl>> {
Ovl>> m_nNames[i] = new CPString();
Ovl>> }

К>// смысл массива указателей — только если ты каждую строчку конструируешь отдельным образом

В смысле?

Ovl>>Первый вопрос: в деструктор при delete m_nTable (и у остальных) для CPString вызовется деструктор или просто указатель в ноль установит?


Ovl>>Или можно вообще этого не делать — vector<>::clear() сам очистит память от всего?

К>Он (грубо говоря) выполнит деструкторы указателей.
К>А это, фактически, деструктор целого числа. Что там разрушать?
К>Поэтому будет куча висячих объектов.

Ovl>>Вызывает ли delete деструктор объекта?


К>Обязательно.


Ovl>>Заранее спасибо


К>Ты случаем не с Паскаля на ++ пересел?

Нет, я настоящий Паскаль вообще с трудом представляю. Просто строки — у меня слабое место. Я привык CString пользовать, ну или если в BCB , то AnsiString. А теперь хочу до конца разобратся. Просто хочется сделать код максимально переносимым.
Read or Die!
Как правильно задавать вопросы
Как правильно оформить свой вопрос
Автор: anvaka
Дата: 15.05.06
Re[2]: Конструктор и деструктор...
От: Ovl Россия  
Дата: 08.08.03 12:39
Оценка:
Здравствуйте, Bell, Вы писали:

B>Для каждого объекта CPString сначала вызывается деструктор, затем овобождается память, который каждый из этих объектов занимал. Кстати для самого CPString я не увидел деструктора. Его нет, или ты его просто не привел?

Не привел.
вот он какой

CPString::CPString() : m_szStr(NULL)
{
}

CPString::~CPString()
{
    if (m_szStr)
    {
        delete[] m_szStr;
//        m_szStr = NULL;
    }
}

Кстати, в форуме я видел пример с незакомментированным m_szStr = NULL, но у меня это вызывает ошибку.
Почему?

B>можно и так...

B>А еще так:
B>В лоб:
B>
B>for(vector<CStringTable*>::iterator it = m_vcStrings.begin(), itEnd = m_vcStrings.end(); it != itEnd; ++it )
B>   delete *it;
B>

почему ++it, а не it++?
Read or Die!
Как правильно задавать вопросы
Как правильно оформить свой вопрос
Автор: anvaka
Дата: 15.05.06
Re[3]: Конструктор и деструктор...
От: Bell Россия  
Дата: 08.08.03 12:55
Оценка:
Здравствуйте, Ovl, Вы писали:

Ovl>Не привел.

Ovl>вот он какой

Ovl>
Ovl>CPString::CPString() : m_szStr(NULL)
Ovl>{
Ovl>}

Ovl>CPString::~CPString()
Ovl>{
Ovl>    if (m_szStr)
Ovl>    {
Ovl>        delete[] m_szStr;
Ovl>//        m_szStr = NULL;
Ovl>    }
Ovl>}
Ovl>

Ovl>Кстати, в форуме я видел пример с незакомментированным m_szStr = NULL, но у меня это вызывает ошибку.
Ovl>Почему?

Как справеливо заметил Кодт, у тебя отсутствовали "правильные" конструктор копии и оператор присваивания. Поэтому почти наверняка ошибка была вызвана повторным удалением после (возможно неявного) создания временной копии. Строка m_szStr = NULL не содержит никакого криминала, кроме того, что является совершенно лишней. Впрочем как и проверка if (m_szStr).


B>>
B>>for(vector<CStringTable*>::iterator it = m_vcStrings.begin(), itEnd = m_vcStrings.end(); it != itEnd; ++it )
B>>   delete *it;
B>>

Ovl>почему ++it, а не it++?

А в чем разница между префиксной и постфиксной формами operator ++ ?
Любите книгу — источник знаний (с) М.Горький
Re[3]: Конструктор и деструктор...
От: Кодт Россия  
Дата: 08.08.03 12:58
Оценка:
Здравствуйте, Ovl, Вы писали:

К>>// И вообще, почему не векторы?

К>> vector<CPString> m_nNames, m_nMessages;
Ovl>Я подумал, что вектор сохдает полную копию объекта и если его (объекта) размер изменяется (а для CPString это в порядке вещей), то это плохо будет. Вектор использует сам объект или только ссылку на него?

Вектор размещает свои данные на куче. Сам sizeof(vector<xxx>) — естественно, величина постоянная.
Упрощенно говоря, вектор — это надстройка над массивом, созданным на куче.

Размер объекта — не изменяется. Это принцип.
Объект может держать указатель на внешнюю память, с которой что-то делает.
Твой CPString, например, держит указатель на массив символов (образующие строчку).
Ovl>>>CStringTable::CStringTable()
Ovl>>>{
Ovl>>>    m_nTableName = new CPString();

К>>// члены — не-указатели — не нуждаются в лишнем new.
Ovl>Я ведь объявлял их как указатели, так ведь?

Объявлял. Просто — зачем?
Тем более, что sizeof(CPString) == 8, а sizeof(CPString*) = 4. И еще сам объект на куче (от 16 до 64 байт)...
Ovl>>>    m_nNames = new CPString*[99];
Ovl>>>    for (BYTE i = 0; i < 99; i++)
Ovl>>>    {
Ovl>>>        m_nNames[i] = new CPString();
Ovl>>>    }

К>>// смысл массива указателей — только если ты каждую строчку конструируешь отдельным образом
Ovl>В смысле?

В том смысле, что
Object* objects = new Object [100];
// в памяти - последовательность из 100 элементов Object
// были вызваны конструкторы без параметров: Object::Object()

Object** objectptrs = new Object* [100];
for(i = 0; i < 50; ++i)
  objectptrs[i] = new Object(taram-param);
for(i = 50; i < 100; ++i)
  objectptrs[i] = new Object(shurum-burum);
// каждый объект конструируется индивидуально.


К>>Ты случаем не с Паскаля на ++ пересел?

Ovl>Нет, я настоящий Паскаль вообще с трудом представляю. Просто строки — у меня слабое место. Я привык CString пользовать, ну или если в BCB , то AnsiString. А теперь хочу до конца разобратся. Просто хочется сделать код максимально переносимым.

Представь себе, что в твоей CStringTable используются CString
Перекуём баги на фичи!
Re[3]: Конструктор и деструктор...
От: Кодт Россия  
Дата: 08.08.03 13:02
Оценка:
Здравствуйте, Ovl, Вы писали:

B>>
B>>for(vector<CStringTable*>::iterator it = m_vcStrings.begin(), itEnd = m_vcStrings.end(); it != itEnd; ++it )
B>>   delete *it;
B>>

Ovl>почему ++it, а не it++?

Потому что ++it экономнее, чем it++.
Условно говоря,
struct the_iterator
{
  ...

  the_iterator& operator++() // ++prefix
  {
    ... // что-то там делаем
    return *this; // возвращаем ссылку на себя (фактически, просто указатель)
  }

  the_iterator operator++(int) // postix++
  {
    the_iterator old = *this; // создаем временный объект - старое значение
    ++(*this); // инкрементируем текущий
    return old; // возвращаем старый (создаем еще одну промежуточную копию)
  }
};
Перекуём баги на фичи!
Re[4]: Конструктор и деструктор...
От: Ovl Россия  
Дата: 08.08.03 13:22
Оценка:
B>>>
B>>>for(vector<CStringTable*>::iterator it = m_vcStrings.begin(), itEnd = m_vcStrings.end(); it != itEnd; ++it )
B>>>   delete *it;
B>>>

Ovl>>почему ++it, а не it++?

B>А в чем разница между префиксной и постфиксной формами operator ++ ?

B>
в том что при ++it, сначала увеличивается, а потом идет цикл.... наверное...
Read or Die!
Как правильно задавать вопросы
Как правильно оформить свой вопрос
Автор: anvaka
Дата: 15.05.06
Re[4]: Конструктор и деструктор...
От: Ovl Россия  
Дата: 08.08.03 13:24
Оценка:
К>Представь себе, что в твоей CStringTable используются CString

Представил =)
Проблема в том, что CString умные люди писали.. а CPString — даун-самоучка =)
Read or Die!
Как правильно задавать вопросы
Как правильно оформить свой вопрос
Автор: anvaka
Дата: 15.05.06
Re[4]: Конструктор и деструктор...
От: Ovl Россия  
Дата: 08.08.03 13:26
Оценка:
Здравствуйте, Кодт, Вы писали:
К>Потому что ++it экономнее, чем it++.

не знал... но в принципе понятно...
Read or Die!
Как правильно задавать вопросы
Как правильно оформить свой вопрос
Автор: anvaka
Дата: 15.05.06
Re[5]: Конструктор и деструктор...
От: Bell Россия  
Дата: 08.08.03 13:43
Оценка:
Здравствуйте, Ovl, Вы писали:

Ovl>в том что при ++it, сначала увеличивается, а потом идет цикл.... наверное...

Ай яй яй

for(init-statement; expression1; expression2)


expression2 всегда выполняется в конце итерации.

Теперь собственно разница.

class X {
int n_;
public:
X& operator++(); // prefix ++a
X operator++(int); // postfix a++
};

X& X::operator++() 
{  ++n_;
   return *this; 
}
X X::operator++(int)
{
   X tmp(*this);//создаем копию
   ++(*this);//инкрементируем себя
   return tmp;
}


Итак, когда пишем
++x; то происходит всего лишь инкремент члена n_. А когда x++, то кроме всего прочего создается временный объект.
Любите книгу — источник знаний (с) М.Горький
Re[6]: Конструктор и деструктор...
От: Ovl Россия  
Дата: 08.08.03 13:48
Оценка:
Здравствуйте, Bell, Вы писали:

B>Ай яй яй

B>Итак, когда пишем
B>++x; то происходит всего лишь инкремент члена n_. А когда x++, то кроме всего прочего создается временный объект.

Допонял =)
Read or Die!
Как правильно задавать вопросы
Как правильно оформить свой вопрос
Автор: anvaka
Дата: 15.05.06
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.