Правильное использование new/delete
От: ice71crew Россия  
Дата: 29.07.03 12:17
Оценка:
Помогите мне плZ, а то совсем мозги уже потекли.
Имеем простой до безобразия строковой класс XString.
Почему при создании объекта этого класса способом
XString s = "string";
вызывается второй конструктор XString(LPSTR string),
а не просто XString, с последующим вызовом перегруженного
оператора = ?
И самое главное, почему при присваивании одного
объекта этого класса другому вообще вылетает Debug
Assertion Fault, хотя присваивается то все нормально.
Насколько я понимаю, я что-то очень нехорошее замутил
с new/delete. Далее приведен исходник.


Visual C++ 5.0 — DEBUG

//-----------------------------------------------------------------------------------
#include <windows.h>



class XString
{
public:
    char *Text;
    WORD Size;
    
    
    XString();
    XString(LPSTR string);
    ~XString();

    void SetText(LPSTR string);
    
    void operator =(LPSTR string);
};




XString::XString()
{
    Text = NULL;
    Size = 0;
}



XString::XString(LPSTR string)
{
    Text = NULL;
    Size = 0;
    SetText(string);
}



XString::~XString()
{
    if(Text)
    {
        delete Text;
        Text = NULL;
    }
}



void XString::SetText(LPSTR string)
{
    int i = 0;
    while(string[i])    i ++;
    Size = i + 1;

    if(Text)    delete Text;
    Text = new char[Size];    //Вот эта строчка вызывает fault при присваивании

    for(i = 0; i < Size; i ++)
        Text[i] = string[i];
}



void XString::operator =(LPSTR string)
{
    SetText(string);
}



void main()
{
    XString str1 = "Just a text string\r\n";
    XString str2 = "Another text string\r\n";

    str1 = str2;    //А это само присваивание ;))
}


Исправлена подсветка синтаксиса. -- ПК.
-=Ай=-
Re: Правильное использование new/delete
От: ice71crew Россия  
Дата: 29.07.03 12:21
Оценка:
Прошу прощения, при помещении вопроса убило все табуляции
-=Ай=-
Re: Правильное использование new/delete
От: UnrealAlex Россия  
Дата: 29.07.03 12:40
Оценка: 12 (1)
Здравствуйте, ice71crew, Вы писали:

I>Почему при создании объекта этого класса способом

I>XString s = "string";

1. Потому как это не присваивание а инициализация <=> XString s("string");

2. исправленный код
class XString
{
public:
    char* Text;
    WORD  Size;
    
    
    XString();
    XString(LPSTR string);
    ~XString();
    
    void SetText(LPSTR string);
    
//    void operator =(LPSTR string);
    XString& operator =(LPSTR string);
};

XString::XString() : Text(NULL), Size(0)
{
//    Text = NULL;
//    Size = 0;
}

XString::XString(LPSTR string) : Text(NULL), Size(0)
{
//    Text = NULL;
//    Size = 0;
    SetText(string);
}

XString::~XString()
{
    if(Text)
    {
        delete [] Text;
        Text = NULL;
    }
}



void XString::SetText(LPSTR string)
{
    //int i = 0;
    //while(string[i]) i ++;
    //Size = i + 1;
    Size = strlen(string) + 1;
    
    if(Text) 
        delete [] Text;
    Text = new char[Size]; //Вот эта строчка вызывает fault при присваивании
    
    //for(i = 0; i < Size; i ++)
    //Text[i] = string[i];
    memcpy(Text, string, Size);
}



//void XString::operator =(LPSTR string)
XString& XString::operator =(LPSTR string)
{
    SetText(string);

    return *this;
}

int main()
{
    XString str1 = "Just a text string\r\n";
    XString str2 = "Another text string\r\n";
    
    str1 = str2; //А это само присваивание )

    return 0;
}


3. Чем не подходит std::string??
Невозможное мы сделаем сегодня — чудо займет немного больше времени. /Аноним/
Re: Правильное использование new/delete
От: kaifo Россия  
Дата: 29.07.03 12:44
Оценка:
Потому что initialization и assignment — это две разные вещи в С++
Читай раздел Explicit Initialization, например, в том же самом MSDN.


Note that because the equal sign (=) in the context of initialization is different from an assignment operator, overloading operator= has no effect on initialization.

Re: Правильное использование new/delete
От: Кодт Россия  
Дата: 29.07.03 12:49
Оценка:
Здравствуйте, ice71crew, Вы писали:

<>

1) new[] --> delete[]
2) SetText не проверяет, создан ли ранее буфер (должен либо повторно использовать, либо уничтожить)
3) вместо while(string[i]) -- не проще ли использовать strlen(), strcpy() ?
Перекуём баги на фичи!
Re: Правильное использование new/delete
От: Alexey Chen Чили  
Дата: 29.07.03 12:51
Оценка:
Здравствуйте, ice71crew, Вы писали:

I> Помогите мне плZ, а то совсем мозги уже потекли.

I>Имеем простой до безобразия строковой класс XString.
I>Почему при создании объекта этого класса способом
I>XString s = "string";
I>вызывается второй конструктор XString(LPSTR string),
I>а не просто XString, с последующим вызовом перегруженного
I>оператора = ?
I>И самое главное, почему при присваивании одного
I>объекта этого класса другому вообще вылетает Debug
I>Assertion Fault, хотя присваивается то все нормально.
I>Насколько я понимаю, я что-то очень нехорошее замутил
I>с new/delete. Далее приведен исходник.


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

Добавь их реализацию и все будет работать.

З.Ы.
а чем, действительно, std::string не угодил?
Re: Правильное использование new/delete
От: Bell Россия  
Дата: 29.07.03 13:13
Оценка: 17 (2)
Здравствуйте, ice71crew, Вы писали:

I> Помогите мне плZ, а то совсем мозги уже потекли.

I>Имеем простой до безобразия строковой класс XString.
I>Почему при создании объекта этого класса способом
I>XString s = "string";
I>вызывается второй конструктор XString(LPSTR string),
I>а не просто XString, с последующим вызовом перегруженного
I>оператора = ?

Потому что operator = здесь совершенно не при чем. Подобная запись означает "инициализацию копированием", и производится с помощью конструктора копии (возможно после вызова конструктора — преобразователя типа).

Давай смотреть, что происходит на самом деле.

XString str1 = "Just a text string\r\n";


Это, как я уже сказал, инициализация копированием. В данном случае возможны 2 варианта:
Вариант1.
Первым делом создается временный объект XString с помощью констркутора XString::XString(LPSTR). Этот временный объект используется в качестве аргумента "настоящего" конструктора копирования, который ты кстати не определил, и поэтому за тебя это сделал компилятор. Однако компилятор не провидец, и не знает, что именно должно происходить при копировании, а посему идет самым простым путем — т.е. производит поэлементное копирование. А значит наш временный объект, и объект str1 содержат указатель на один и тот же блок. После окончания инициализации временный объект разрушается (вызывая в деструкторе delete [] Text), и элемент Text объекта str1 после всего этого указывает в туман. Естественно, любая попытка обратиться по этому адресу (в том числе и delete) ведут к ошибке (и это в лучшем случае).
Вариант 2.
Компилятор обходится без создания временного объекта, и производит инициализацию "напрямую" с использованием XString::XString(LPSTR). В этом случае программа продолжает работу. Однако никто не гарантирует, что она также будет работать при смене компилятора (или версии компилятора ).

XString str2 = "Another text string\r\n";


то же самое.

str1 = str2;


Это присваивание. И естественно, тут имеет место быть вызов operator=. Тонкость только в том, что вызывается
XString::operator = (const XString&)

а совсем не то недоразумение, которое объявлено у тебя. Как видно из кода, "правильного" operator= тоже нет, и поэтому его "сделал" компилятор. И что самое обидное — так же, как и конструктор копии, т.е. ограничился поэлементным копированием. А значит, после записи str1 = str2; оба объекта опять ссылаются на один и тот же блок памяти, п при разрушении этих объектов delete вызовется 2 раза для одного и того же адреса. Последствия, опять же, могут быть самыми необыкновенными.

PS
И еще. Если память выделяется с помощью new [], то и освобождать ее надо с помощью delete []
Пример

char* ptr = new char[10];
...
delete [] ptr;
Любите книгу — источник знаний (с) М.Горький
Re: Правильное использование new/delete
От: ice71crew Россия  
Дата: 29.07.03 13:32
Оценка:
Большое спасибо всем ответившим. Standart string вещь безусловно хорошая, просто я не люблю когда что-то делают за меня + надо же мне самому уметь делать такие вещи
-=Ай=-
Re[2]: Правильное использование new/delete
От: Bell Россия  
Дата: 29.07.03 13:45
Оценка:
Здравствуйте, ice71crew, Вы писали:

I>Большое спасибо всем ответившим. Standart string вещь безусловно хорошая, просто я не люблю когда что-то делают за меня

И что теперь — ты будешь переписывать все подряд?!

I>+ надо же мне самому уметь делать такие вещи

Вот это да. Это аргумент. А то если начать переписывать стандартные вещи, то на работу времени не останется
Любите книгу — источник знаний (с) М.Горький
Re[2]: Правильное использование new/delete
От: Hermes Россия  
Дата: 29.07.03 14:10
Оценка:
у меня тоже вопросик по импользованию new и delete


char *ch2[3];
ch2[0] = new char[8];
ch2[1] = new char[8];
ch2[2] = new char[8];
ch2[0] = "0000000";
ch2[1] = "1111111";
ch2[2] = "2222222";
cout << ch2[0] << ch2[1] << ch2[2] <<endl;



вот как в этом случае удалить, выделившуюся память?
Re[3]: Правильное использование new/delete
От: Bell Россия  
Дата: 29.07.03 14:21
Оценка: -1
Здравствуйте, Hermes, Вы писали:

H>у меня тоже вопросик по импользованию new и delete



H>
H>char *ch2[3];
H>ch2[0] = new char[8];
H>ch2[1] = new char[8];
H>ch2[2] = new char[8];
H>ch2[0] = "0000000";
H>ch2[1] = "1111111";
H>ch2[2] = "2222222";
H>cout << ch2[0] << ch2[1] << ch2[2] <<endl;
H>



H>вот как в этом случае удалить, выделившуюся память?



for(int i = 0; i < sizeof(ch2)/sizeof(ch2[0]); ++i)
   delete [] ch2[i];
Любите книгу — источник знаний (с) М.Горький
Re[3]: Правильное использование new/delete
От: Аноним  
Дата: 29.07.03 14:26
Оценка:
Здравствуйте, Hermes, Вы писали:

H>
H>char *ch2[3];
H>ch2[0] = new char[8];
H>ch2[1] = new char[8];
H>ch2[2] = new char[8];
// (1)
H>ch2[0] = "0000000";
H>ch2[1] = "1111111";
H>ch2[2] = "2222222";
H>cout << ch2[0] << ch2[1] << ch2[2] <<endl;
H>


H>вот как в этом случае удалить, выделившуюся память?


Уже никак, она утекла безвозвратно. Нужно было ее ловить в строке (1), как указал Bell.
Re[4]: Правильное использование new/delete
От: Hermes Россия  
Дата: 29.07.03 14:28
Оценка:
B>
B>for(int i = 0; i < sizeof(ch2)/sizeof(ch2[0]); ++i)
B>   delete [] ch2[i];
B>


Неа VC6 ругается ...
Re[4]: Правильное использование new/delete
От: Hermes Россия  
Дата: 29.07.03 14:32
Оценка:
Здравствуйте, Аноним, Вы писали:

А как сделать без утечки массив из 3 строк?
Re[5]: Правильное использование new/delete
От: Bell Россия  
Дата: 29.07.03 14:34
Оценка:
Здравствуйте, Hermes, Вы писали:


B>>
B>>for(int i = 0; i < sizeof(ch2)/sizeof(ch2[0]); ++i)
B>>   delete [] ch2[i];
B>>


H>Неа VC6 ругается ...

H>

И правильно делает. Я ошибся. Ты потерял свои указатели на выделенные блоки после строки (1), как совершенно справедливо заметил наш анонимный коллега чуть ниже.
Возможно, ты хотел что-то типа этого?

char *ch2[3];
ch2[0] = new char[8];
ch2[1] = new char[8];
ch2[2] = new char[8];
strcpy(ch2[0], "0000000");
strcpy(ch2[1], "1111111");
strcpy(ch2[2], "2222222");
cout << ch2[0] << ch2[1] << ch2[2] <<endl; 

for(int i = 0; i < sizeof(ch2)/sizeof(ch2[0]); ++i)
   delete [] ch2[i];
Любите книгу — источник знаний (с) М.Горький
Re[4]: Правильное использование new/delete
От: Кирпа В.А. Украина  
Дата: 29.07.03 14:38
Оценка:
Здравствуйте, Bell, Вы писали:

B>Здравствуйте, Hermes, Вы писали:


H>>у меня тоже вопросик по импользованию new и delete



H>>
H>>char *ch2[3];
H>>ch2[0] = new char[8];
H>>ch2[1] = new char[8];
H>>ch2[2] = new char[8];
H>>ch2[0] = "0000000";
H>>ch2[1] = "1111111";
H>>ch2[2] = "2222222";
H>>cout << ch2[0] << ch2[1] << ch2[2] <<endl;
H>>



H>>вот как в этом случае удалить, выделившуюся память?



B>
B>for(int i = 0; i < sizeof(ch2)/sizeof(ch2[0]); ++i)
B>   delete [] ch2[i];
B>


В этом коде происходит потеря 3-х указатей
Указатели полученные через new затем стали указывать на статические строки
Естественно программа дожна падать на delete
По научному это называется dangling (подвешивание или потеря адреса памяти)
Кстати это одна из распространненых ошибок при работе с указателями в С
Есть еще aliasing (Псевдонимизация адреса)
Например

int *p = new int;
int *q = new int;
p = q; // начиная с этого момента p указывает на ту же область что и q

Писать по полученым адресам надо так


strcpy(ch2[0], "0000000");
strcpy(ch2[1], "1111111");
strcpy(ch2[2], "2222222");
!0xDEAD
Re[5]: Правильное использование new/delete
От: UnrealAlex Россия  
Дата: 29.07.03 14:38
Оценка:
Здравствуйте, Hermes, Вы писали:

H>Здравствуйте, Аноним, Вы писали:


H>А как сделать без утечки массив из 3 строк?


std::vector<std::string> vec;
vec.push_back("string 1");
vec.push_back("string 2");
vec.push_back("string 3");
Невозможное мы сделаем сегодня — чудо займет немного больше времени. /Аноним/
Re[3]: Правильное использование new/delete
От: Кодт Россия  
Дата: 29.07.03 14:44
Оценка:
Здравствуйте, Hermes, Вы писали:

H>char *ch2[3];
H>ch2[0] = new char[8];
H>ch2[1] = new char[8];
H>ch2[2] = new char[8];

// а теперь мы дружно забиваем на выделенную память
// и присваиваем указатели на строковые литералы :(

H>ch2[0] = "0000000";
H>ch2[1] = "1111111";
H>ch2[2] = "2222222";
H>cout << ch2[0] << ch2[1] << ch2[2] <<endl;

H>вот как в этом случае удалить, выделившуюся память?
В этом случае — уже никак.

Нужно было:
...
strcpy( ch2[0], "0000000" );
strcpy( ch2[1], "1111111" );
strcpy( ch2[2], "2222222" );
...

delete[] ch2[0];
delete[] ch2[1];
delete[] ch2[2];
Перекуём баги на фичи!
Re[4]: Правильное использование new/delete
От: Hermes Россия  
Дата: 29.07.03 15:03
Оценка:
Здравствуйте, Кодт, Вы писали:

К>[ccode]

H>>char *ch2[3];
H>>ch2[0] = new char[8];
H>>ch2[0] = "0000000";
А почему так нелься? Объясните пожалуйста!!
Re[5]: Правильное использование new/delete
От: Другой Аноним  
Дата: 29.07.03 15:10
Оценка:
Здравствуйте, Hermes, Вы писали:

К>>
H>>>char *ch2[3];
H>>>ch2[0] = new char[8];
H>>>ch2[0] = "0000000";
К>>

H>А почему так нелься? Объясните пожалуйста!!

ch2[0] — это объект типа указатель.
Сначала ты в него записываешь указатель на размещенный оператором new блок памяти.
Но в следующей строке ты перезаписываешь это значение другим указателем — на статически (при компиляции) размещенный блок памяти, содержащий строковый литерал "0000000".
Прежнее значение оказывается безвозвратно утерянным и вернуть блок уже не удастся.
При попытке выполнить delete[] ch2[0] поведение программы не определено (скорее всего, просто упадет), т. к. блок памяти, на который указывает ch2[0] не был размещен при помощи new.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.