воскресенье, 8 августа 2010 г.

Забывая о методах класса создаваемых по умолчанию

Весьма забавный феномен, если не знать тонкости ;)

class CStr {
public:
  CStr(char * str) {
    if(!str) {
      m_Ch  = new char[1];
      *m_Ch = 0x0;
    }
    else {
      m_Ch = new char[strlen(str)+1];
      strcpy(m_Ch,str);
    }
  }
  ~CStr() { delete(m_Ch); }
  void dump(std::ostream & strm) {
    strm << "\"" << m_Ch << "\"\n";
  }
private:
  char * m_Ch;
};

CStr * s1 = new CStr("First String !!!");
CStr * s2 = new CStr("Second String !!!");

s1->dump(std::cout);
s2->dump(std::cout);

s2 = s1;

s1->dump(std::cout);
s2->dump(std::cout);

delete(s1);

s2->dump(std::cout);
delete(s2);

Думаю этот пример убедил, что нужно быть внимательней при разработке класса ;)

4 комментария:

hunt комментирует...

Я не уловил феномена. Не могу понять, то ли из-за не знания тонкостей, то ли наоборот. =)

~CStr() { delete(m_Ch); }

Распространённая ошибка - с массивами надо использовать delete []. Из C++ Faq Lite:

[16.12] What if I forget the [] when deleteing array allocated via new T[n]?

All life comes to a catastrophic end.

It is the programmer's —not the compiler's— responsibility to get the connection between new T[n] and delete[] p correct. If you get it wrong, neither a compile-time nor a run-time error message will be generated by the compiler. Heap corruption is a likely result. Or worse. Your program will probably die.

The NT Visigoth комментирует...

2 Hunt :
Тонкость заключена в том, что в классе CStr не перегружен оператор присваивания и следовательно используется тот оператор '=', который по дефолту! ;)

hunt комментирует...

s2 = s1;

Тут всё-таки копирование указателя на объект. В s2 и s1 после этой строки хранится один и тот же адрес:
printf("0x%p, 0x%p\n", s1, s2);

Конструктор копирования вызвался бы в этом случае:

*s2 = *s1;

The NT Visigoth комментирует...

1) Создание двух объектов класса строк
2) s2 = s1; тут вызывается оператор присвания по умолчанию, а не конструктор! А он в свою очередь копирует указатель на первую строку!!!
3) Херится, т.е. утечка памяти , т.к. уже ни кто не указывает на вторую строку
4) Первый объект удаляется, но второй продолжает указывать на ту область где раньше лежала первая строка
5) пытаемся юзать dump
6) пытаемся удалить второй объект

Это разве хорошо? ;)