Re[2]: Resource Acquisition Is Initialization
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 19.09.12 15:47
Оценка: -4 :)
Здравствуйте, Шебеко Евгений, Вы писали:

ШЕ>Старайтесь избегать в коде явного освобождения ресурсов.


Что-то мне подсказывает, что часть советов можно заменить простым: используйте valgrind/dtrace.
Re[5]: delete и delete[] различаются
От: Кодт Россия  
Дата: 19.09.12 18:33
Оценка: 4 (1)
Здравствуйте, uzhas, Вы писали:

U>падение программы на delete — это крайне редкое событие имхо


Да легко!
struct foo { virtual ~foo() {} };

int main()
{
  delete new foo[2];
}

Немедленно валится
*** glibc detected *** ./x: munmap_chunk(): invalid pointer: 0x09ac900c ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x73e42)[0x8a9e42]
/lib/i386-linux-gnu/libc.so.6(+0x74525)[0x8aa525]
/usr/lib/i386-linux-gnu/libstdc++.so.6(_ZdlPv+0x1f)[0x54c51f]
./x[0x8048708]
./x[0x80486b6]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0x84f4d3]
./x[0x80484d1]

А всё потому, что выделенный блок памяти (0x09ac9008) имеет отрицательное смещение от собственно указателя на первый элемент (0x09ac900c). Деструктор первого элемента отрабатывает нормально, а вот собственно освобождение памяти обламывается.
Перекуём баги на фичи!
Re[6]: Resource Acquisition Is Initialization
От: Кодт Россия  
Дата: 19.09.12 18:39
Оценка:
Здравствуйте, igna, Вы писали:

I>Настоящие гуру это Мейерс, Саттерс и Ко.


Саттерс — это семья Саттеров? Уже молчу, кто такой гуру всех времён и народов К.О. (хотя... если взглянуть на мой никнейм...)

Кстати, напоминаю о существовании правил форума, где попрекание собеседников нехваткой квалификации не приветствуется.
Перекуём баги на фичи!
Re: TOP граблей С++ для новичков
От: Sashaka Россия  
Дата: 20.09.12 04:35
Оценка: +3
Здравствуйте, Шебеко Евгений, Вы писали:

ШЕ>Сейчас приходится собеседовать много новичков.

ШЕ>Заметил что ничего в жизни не меняется.
ШЕ>Даже люди с опытом работы в крупных наступают на те же грабли, на которые наступал я в своё время.


Зачем все это?
Как уже резонно заметили выше, новички которые это не знают и им это неинтересно — просто ничего не поймут.
Те, кто заинтересовался хорошими С++ практиками — быстро найдут ответы в книгах Саттер/Майерса/Александреску.

Шансы на то, что данный топ прочитают новички — минимален. Так что данный топик разве что вызвать C++ флуд очередной и померяться писюнами.
Re[7]: Resource Acquisition Is Initialization
От: igna Россия  
Дата: 20.09.12 07:32
Оценка: +1 :)
Здравствуйте, Кодт, Вы писали:

К>Саттерс — это семья Саттеров?




К>Кстати, напоминаю о существовании правил форума, где попрекание собеседников нехваткой квалификации не приветствуется.


Нет, ну "не гуру" это не нехватка квалификации. А то так можно дойти до того, что сказать человеку, что он не гений, будет восприниматься как оскорбление.
Re: TOP граблей С++ для новичков
От: kov_serg Россия  
Дата: 20.09.12 12:29
Оценка:
Деструкторы записывают обратно свою таблицу виртуальных функций.
#include <stdio.h>

struct A {
  void check_fn() { printf("check:"); fn(); }
  virtual void fn() { printf("A.fn\n"); }
  virtual ~A() { printf("~A\n"); check_fn(); }
};

struct B : A {
  void fn() { printf("B.fn\n"); }
  ~B() { printf("~B\n"); }
};

int main(int,char**) {
  B b;
  b.check_fn();
  return 0;
}
Output:
check:B.fn
~B
~A
check:A.fn

Типичная грабля с использованием этого факта
struct Thread { // TThread, CWinThread...
  virtual void Run()=0;
  virtual ~Thread() { Join(); }
  void Join() { ... }
  void Start() { ... }
};
struct MsgThread : Thread {
  void Run() { 
    for(;;) dispatch_message();
  }
  virtual void dispatch_message() {...}
};
...
void test() {
  MsgThread t;
  t.Start();
  sleep(100);
  // тут неявный вызов ~MyThread, он в свою очередь запустит ~Thread который начнёт ожидать завершения потока.
  // и почти сразу упадёт Run т.к. указателя на dispatch_message уже нет. ~Thread поменял vft.
}

Настипить можно в C++Builder и VisualStudio c MFC
Re[2]: TOP граблей С++ для новичков
От: XuMuK Россия  
Дата: 21.09.12 07:25
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Деструкторы записывают обратно свою таблицу виртуальных функций.


не важно кто что и куда записывает, вирутальные функции вызванные из конструктора или деструктора вызываются как невиртуальные, самый простой способ не наступать на грабли: "не делайте так" (используйте двухфазную инициализацию/удаление).
Re[3]: TOP граблей С++ для новичков
От: kov_serg Россия  
Дата: 21.09.12 08:40
Оценка:
Здравствуйте, XuMuK, Вы писали:

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


_>>Деструкторы записывают обратно свою таблицу виртуальных функций.


XMK>не важно кто что и куда записывает, вирутальные функции вызванные из конструктора или деструктора вызываются как невиртуальные, самый простой способ не наступать на грабли: "не делайте так" (используйте двухфазную инициализацию/удаление).

Ептить. Я же не говорю что это правильно. Я говорю что это неявная грабля на которую можно наступить. И она заложена в дизайне VCL и MFC.
Re[3]: TOP граблей С++ для новичков
От: Masterkent  
Дата: 21.09.12 09:24
Оценка:
XuMuK:

XMK>вирутальные функции вызванные из конструктора или деструктора вызываются как невиртуальные


Вызовы там виртуальные, только final overriders определяются специфически.

#include <iostream>

struct B
{
    virtual void f() { std::cout << "B::f\n"; }
    void g() { f(); }
};

struct D1 : B
{
    D1()
    {
        B *b = this;
        b->f(); // calls D1::f
        g();    // calls D1::f
    }
    virtual void f() { std::cout << "D1::f\n"; }
};

struct D2 : D1
{
    virtual void f() { std::cout << "D2::f\n"; }
};

int main()
{
    D2 d;
}
Re[4]: TOP граблей С++ для новичков
От: kov_serg Россия  
Дата: 21.09.12 15:18
Оценка:
Здравствуйте, Masterkent, Вы писали:

M>XuMuK:


XMK>>вирутальные функции вызванные из конструктора или деструктора вызываются как невиртуальные


M>Вызовы там виртуальные, только final overriders определяются специфически.


M>
#include <iostream>

M>struct B
M>{
M>    virtual void f() { std::cout << "B::f\n"; }
M>    void g() { f(); }
M>};

M>struct D1 : B
M>{
M>    D1()
M>    {
M>        B *b = this;
        b->>f(); // calls D1::f
M>        g();    // calls D1::f
M>    }
M>    virtual void f() { std::cout << "D1::f\n"; }
M>};

M>struct D2 : D1
M>{
M>    virtual void f() { std::cout << "D2::f\n"; }
M>};

M>int main()
M>{
M>    D2 d;
M>}


Не понял о чем вы спроите
class CLASS {
 ctor {
  this->vft=CLASS.vft;
  ctor_body();
 }
 dtor {
  this->vft=CLASS.vft;
  dtor_body();
 }
}

В конструкторе всегда таблица от текущего класса и в деструкторе тоже.
Именно эта особенность приводит к граблям при параллельном исполнении.
Re[3]: TOP граблей С++ для новичков
От: Кодт Россия  
Дата: 21.09.12 20:50
Оценка:
Здравствуйте, XuMuK, Вы писали:

_>>Деструкторы записывают обратно свою таблицу виртуальных функций.


XMK>не важно кто что и куда записывает, вирутальные функции вызванные из конструктора или деструктора вызываются как невиртуальные,


Функции там вызываются как виртуальные — но только для текущего типа, т.е. для базы.
Насколько компилятору хватает поля зрения, он подменяет виртуальный вызов статическим.
struct base
{
  virtual void f() {}
  void g() { f(); } // здесь будет заведомо виртуальный вызов (хотя, компилятор может проинлайнить g()...)
  base() { f(); g(); } // по крайней мере единожды будет виртуальный вызов f() - из g()
};


XMK> самый простой способ не наступать на грабли: "не делайте так" (используйте двухфазную инициализацию/удаление).


"Не делайте так" здесь значит более общее: не лезьте в разрушаемый объект, особенно — из другого потока, особенно — оставаясь в функции-члене разрушаемого объекта (впрочем, есть способ отстрелить себе ногу и в однопоточном режиме и даже без виртуальных функций).
struct foo
{
  std::string name;

  void curricumvita()
  {
    born();
    lived();
    dead();
    rip();
  }

  void born()  { name = "foo"; }
  void lived() { std::cout << "hello, " << name << std::endl; }
  void dead()  { delete this; }
  void rip()   { std::cout << "R.I.P. " << name << std::endl; } // бабах!
};

int main() { new foo(); }


А рецепт с двухфазностью — да, рулит. В том числе и здесь.
Перекуём баги на фичи!
Re[5]: TOP граблей С++ для новичков
От: Кодт Россия  
Дата: 21.09.12 20:58
Оценка: +1
Здравствуйте, kov_serg, Вы писали:

_>В конструкторе всегда таблица от текущего класса и в деструкторе тоже.

_>Именно эта особенность приводит к граблям при параллельном исполнении.

На самом деле, грабли там в кривом дизайне и перекрытии времени жизни.
Ну нельзя делать так, что время жизни объекта-работодателя (до входа в деструктор) было короче, чем время жизни потока-подчинённого.
А если очень-очень хочется задействовать RAII, чтобы поток гасился из деструктора, — так не надо смешивать объект-работодатель и RAII-сторож.
Да, получится двухфазная деинициализация.

И вообще, ООП конкретно в этом месте больше мешает, чем помогает, — провоцируя на антипаттерн самоубийцы. ФП-подход, где данные для потока передаются в замыкание и там живут до самой смерти, — более естественнен.
Перекуём баги на фичи!
Re[4]: TOP граблей С++ для новичков
От: kov_serg Россия  
Дата: 21.09.12 21:32
Оценка:
Здравствуйте, Кодт, Вы писали:
...
К>А рецепт с двухфазностью — да, рулит. В том числе и здесь.
Рулить то оно может и рулит, но:
http://docwiki.embarcadero.com/Libraries/en/System.Classes.TThread.Execute
http://msdn.microsoft.com/en-us/library/48xz4yz9(v=vs.110).aspx

Не всплывёт его бедное тело, грабли на дне я поставил умело.

Re[5]: TOP граблей С++ для новичков
От: Кодт Россия  
Дата: 22.09.12 07:46
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>

Не всплывёт его бедное тело, грабли на дне я поставил умело.

Антипаттерн "самоубийца" зашит в библиотеку C'est la vie. (Et la MORT!!!)
Имхо, лучшее, что можно сделать здесь — это использовать TThread/CWinThread исключительно как хэндл потока, и никогда от них не наследоваться.
Не приходит же в голову (или приходит? после стольких лет с дебилдером и мфц?) наследоваться от boost/std::thread ?
Перекуём баги на фичи!
Re[2]: delete и delete[] различаются
От: carpenter СССР  
Дата: 23.09.12 18:04
Оценка: -3
Здравствуйте, Шебеко Евгений, Вы писали:


ШЕ>Если вы создаёте массив, то для его разрушения должны вызывать delete[].


для POD типов неактуально
Re: TOP граблей С++ для новичков
От: carpenter СССР  
Дата: 23.09.12 18:12
Оценка:
Здравствуйте, Шебеко Евгений, Вы писали:

вставка и , особенно, удаление из stl контейнеров ... в частности вектора

... код приводить не буду, ибо нефик
Re[3]: delete и delete[] различаются
От: carpenter СССР  
Дата: 24.09.12 16:27
Оценка:
Здравствуйте, carpenter, Вы писали:

интересна аргументация минуса
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.