[C++] вопрос связанный с конструктором

Тема в разделе "LANGS.C", создана пользователем osox, 3 окт 2010.

  1. osox

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    Всех приветствую
    вот например хочу защитится от повторного
    вызова конструктора для уже созданного объекта
    пример
    Код (Text):
    1. typedef char* pChar;
    2.  
    3. class string_t
    4. {
    5.     char *str;
    6.     size_t len;
    7.     size_t size;
    8. public:
    9.     string_t(const char *str);
    10. };
    11.  
    12. string_t::string_t(const char *str)
    13. {
    14.     this->len = strlen(str);
    15.     this->size = this->len + 1;
    16.     this->str = pChar(malloc(this->size));
    17.     strcpy(this->str, str);
    18. }
    19.  
    20. int main()
    21. {
    22.     string_t str("content");
    23.     new(&str)string_t("new content");
    24. }
    так вот в итоге теряется старый указатель а проверять сразу неинициализированный объект в конструкторе на предмет был ли создан уже объект
    нет возможности вернее все последующие то вызовы можно а первый нет
    как поступить ? я смотрел реализацию класса CString из MFC он тоже при таком использовании начинает терять память потом я посмотрел std::string он этот случай обрабатывает нормально
    тоесть память не утекает просто присваивается новая строка но не смог разобратся в исходниках
    стандартного класса чтобы выяснить как он это делает есть идеи ?
     
  2. n0name

    n0name New Member

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

    вполне валидное решение добавить проверку а-ля:
    Код (Text):
    1. if (_str)
    2.     free((void *)_str);
     
  3. osox

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    n0name
    в str при первом вызове все что угодно может быть то что было в стеке или в куче то и будет на что проверять то ? понятно что последующие вызовы констуктора я так смогу проверить но первый непридумаю как
     
  4. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    точно.
    тогда хз. с std::string у тебя работает потому что строки короткие (< 16 символов).
    Ппроверь на более длинных строрках, результат будет тот же.
     
  5. osox

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    и правда а я думал что std::string этим не страдает а он оказывается короткие строки хранит прямо в объекте
    Код (Text):
    1. int main()
    2. {
    3.     std::string str("content");
    4.     char *p = pChar(&str);
    5.     for (size_t i = 0; i < sizeof(std::string); i++)
    6.         printf("%c", p[i]);
    7. }
    8.     ╠╠╠╠content ╠╠╠╠╠╠╠╠   ☼   Для продолжения нажмите любую клавишу . . .
    теперь понятно почему он не теряет память на коротких строчках
     
  6. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Зачем два раз конструировать один и тот же объект? Это ошибка программиста, а не дизайна. Но если так хочется, то можно завести статический контейнер объектов(менеджер объектов) и возиться. Хотя непонятно зачем.
     
  7. osox

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    Booster
    да я бы может сразу так и подумал что если кто то так напишет то пусть сам и отвечает но заметил что std::string ведет себя нормально с этого все и началось а тут уже выяснили что std::string тоже не закодирована предвидеть такое использование и корректно реагировать
     
  8. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    osox
    FindAtom / AddAtom
    Конечно ОСозависимо, но наверняка в nix есть что нибудь подобное, так что можно сделать универсальную обёртку или самому их аналог написать.
     
  9. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Y_Mur
    Зачем? ^)
     
  10. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Booster
    Универсальное решение позволяющее определить первый/повторный вызов любого кода (главное правильно алгоритм генерации уникальных идентификаторов продумать). Согласен, что заморачиваться с решением этой задачи здесь нецелесообразно, но если очень хочется то технически решение возможно :)
     
  11. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Y_Mur
    Это нужно делать не на уровне всех процессов, а на уровне одного. Менеджер объектов - вот решение.
     
  12. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Booster
    Менеджер объектов понятие весьма растяжимое :) В данном случае он вполне может быть самописным аналогом целочисленных win атомов, т.е. таблицей this-ов с функциями поиска/добавления и всё ;)
    А можно и готовые вин функции использовать, хотя гемороя с ними будет больше чем самому аналог сделать.

    ЗЫ: Кстати на уровне всех процессов это GlobalAddAtom а не просто AddAtom.
     
  13. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Y_Mur
    А я про что говорю? Хоть мапа [key->this][val->this]. Никаких атомов не надо. Насчёт локальной таблицы, да ошибся, но всё равно это абсолютно не нужно. ^)
     
  14. osox

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    Y_Mur
    Booster
    решил что бред огород городить просто не надо так писать
    вот еще пример в конструктор копий надо вставлять проверку на создание из себя самого например
    Код (Text):
    1. string_t::string_t(const string_t &str)
    2. {
    3.     if (*this != str)
    4.         // ...  
    5.     else
    6.         this->string_t() // ...  
    7. }
    8. // ...
    9. string_t str = str;
    а то например mfc::string падает на этом а std::string это обрабатывает вприниципе можно и вставить а можно позволить и упасть
    а вообще это все провокационный код надеящийся что об этом позаботились что уже отобъет всю охоту так писать у незнакомых типов да и с практической точки зрения это просто выпрашивание непрятностей не более
    можно сделать почленное копирование так как объект еще не создан мы ничего не теряем но с другой стороны мы можем вызвать конструктор по умолчанию
     
  15. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    osox
    Проверка на самого себя в конструкторе копирования не нужна, так как конструктор вызывается только один раз. Проверка нужна в операторе присваивания.
     
  16. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    osox
    Отвечу на первый пост.
    У Вас нет повторного вызова конструктора одного и того же объекта, поэтому тут нельзя ничего избежать. Вы вызывали конструктор нового объекта, перезаписывая при этом содержимым нового объекта память старого. С тем же успехом можно было сделать ZeroMemory (&str, sizeof(str)) и удивляться, почему указатель теряется.
     
  17. osox

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    l_inc
    всмысле ? явно видно что просто конструктор вызывается для того же объекта еще раз new ведь в данном случае память не выделяет а просто адрес возвращает нами же ему и переданный
    Код (Text):
    1. int main()
    2. {
    3.     string_t str("content");
    4.     new(&str)string_t("new content");
    5. }
    6.  
    7. int main()
    8. {
    9. 004134E0 55               push        ebp  
    10. 004134E1 8B EC            mov         ebp,esp
    11. 004134E3 83 EC 54         sub         esp,54h
    12. 004134E6 53               push        ebx  
    13. 004134E7 56               push        esi  
    14. 004134E8 57               push        edi  
    15.     string_t str("content");
    16. 004134E9 68 98 78 41 00   push        offset string "content" (417898h)
    17. 004134EE 8D 4D F4         lea         ecx,[str]
    18. 004134F1 E8 17 DD FF FF   call        string_t::string_t (41120Dh)
    19.     new(&str)string_t("new content");
    20. 004134F6 8D 45 F4         lea         eax,[str]
    21. 004134F9 50               push        eax  
    22. 004134FA 6A 0C            push        0Ch  
    23. 004134FC E8 40 DB FF FF   call        operator new (411041h)
    24. 00413501 83 C4 08         add         esp,8
    25. 00413504 89 45 B0         mov         dword ptr [ebp-50h],eax
    26. 00413507 83 7D B0 00      cmp         dword ptr [ebp-50h],0
    27. 0041350B 74 12            je          main+3Fh (41351Fh)
    28. 0041350D 68 88 78 41 00   push        offset string "new content" (417888h)
    29. 00413512 8B 4D B0         mov         ecx,dword ptr [ebp-50h]
    30. 00413515 E8 F3 DC FF FF   call        string_t::string_t (41120Dh)
    31. 0041351A 89 45 AC         mov         dword ptr [ebp-54h],eax
    32. 0041351D EB 07            jmp         main+46h (413526h)
    33. 0041351F C7 45 AC 00 00 00 00 mov         dword ptr [ebp-54h],0
    34. }
    35. 00413526 8D 4D F4         lea         ecx,[str]
    36. 00413529 E8 35 DC FF FF   call        string_t::~string_t (411163h)
    37. 0041352E 33 C0            xor         eax,eax
    38. 00413530 5F               pop         edi  
    39. 00413531 5E               pop         esi  
    40. 00413532 5B               pop         ebx  
    41. 00413533 8B E5            mov         esp,ebp
    42. 00413535 5D               pop         ebp  
    43. 00413536 C3               ret
     
  18. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    osox
    Не понимаю, где Вы в листинге видите, что конструктор для того же объекта. Формально это новый объект. Просто Вы его расположили по тому же адресу, что и предыдущий.

    Причём в листинге используются "разные" адреса:

    Для первого объекта конструктору передаётся адрес, взятый через:
    004134EE 8D 4D F4 lea ecx,[str]

    Для второго объекта честно используется адрес, возвращённый new:
    00413504 89 45 B0 mov dword ptr [ebp-50h],eax
    ...
    00413512 8B 4D B0 mov ecx,dword ptr [ebp-50h]

    Так что ИМХО "явно видно" как раз обратное.
     
  19. osox

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    l_inc
    lea ecx,[str];
    и то что возвращает new это в точности один и тот же адрес
    вот код этой версии new
    Код (Text):
    1. inline void *__CRTDECL operator new(size_t, void *_Where) _THROW0()
    2.     {   // construct array with placement at _Where
    3. 00401000 55               push        ebp  
    4. 00401001 8B EC            mov         ebp,esp
    5.     return (_Where);
    6. 00401003 8B 45 0C         mov         eax,dword ptr [ebp+0Ch]
    7.     }
    8. 00401006 5D               pop         ebp  
    9. 00401007 C3               ret
     
  20. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    osox
    Ну и что, что тот же самый адрес? Это ведь Вы расположили второй объект, используя placement new, по тому же адресу.

    Рассмотрим такой код:
    Код (Text):
    1. class string_t
    2. {
    3.     char *str;
    4.     size_t len;
    5.     size_t size;
    6. public:
    7.     string_t(const char *str);
    8. };
    9.  
    10. class Client
    11. {
    12.     size_t budget;
    13.     size_t credit;
    14.     char *name;
    15. public:
    16.     Client(const char *name);
    17. };
    18.  
    19. int main()
    20. {
    21.     string_t str("content");
    22.     Client *client = new(&str)Client("Vas'a");
    23. }
    Хотите сказать, что здесь опять вызываются конструкторы одного и того же объекта? Ведь адрес-то один и тот же.