24 января 2009 г.

Контейнеростроение

Строя контейнеры под свой гейм проект,
установил некоторые правила, которые распростроняются и на STL контейнеры
1. Не передавайте в контейнер обьект по оператору new если не собираетесь его удалять через delete
2. Старайтесь не использовать контейнер обьектов, если добавляете обьекты на стёке, перфоманс падает на конструкции и деструкции на стёке + конструкции или копировании в самом контейнере. Используйте контейнер указателей на обьекты.

5 комментариев:

  1. А можно поучаствовать в конкурсе самых банальных правил? Вот, например:
    3. Не создавайте контейнеры содержащие auto_ptr
    4. Используйте empty() вместо size()==0
    5. Позаботтесь об написании соответствующих копирующих конструкторов для классов, которые используют указатели на динамически создаваемые объекты...

    ОтветитьУдалить
  2. Да :) Это как-то пропустил, думал и так понятно.
    Особенно про копирующие конструкторы, тут важно тонкое понимание.

    ОтветитьУдалить
  3. Ну так а я о чем?
    Вот твое первое правило. Разве не понятно, что созданные в куче объекты сами не удаляются? И здесь никакое тонкое понимание не нужно. Это основы

    А второе? Вообще очень странная связь между стеком и указателями и просто катастрофическое заблуждение!
    "Старайтесь не использовать контейнер обьектов, если добавляете обьекты на стёке, ... Используйте контейнер указателей на обьекты."
    Браво! Если коротко, то ты написал: "Используйте контейнеры, содержащие указатели на автоматические объекты!". А если автоматические объекты уйдут из области видимости раньше контейнера? Это тебя не смущает? И еще:
    "перфоманс падает на конструкции и деструкции на стёке + конструкции или копировании в самом контейнере"
    Это специфика автоматических объектов, понимаешь-ли. Если ты планируешь использовать их дольше их времени жизни, то их обязательно нужно скопировать! А если ты их планируешь использовать их не дольше их времени жизни, то намного проще обходится без контейнеров.
    Вот это тонкое понимание. Нужно хорошо понимать разницу между автоматическими, статическими и динамическими объектами и применять те из них, которые больше отвечают требованиям, а не пытаться использовать объекты в неестественным для них образом. Если необходимо произвольное время жизни, то нужно использовать динамические объекты, а не пытаться симулировать их поведение путем явного вызова деструкторов для автоматических объектов. И если время жизни объекта ограничено некоторым блоком, то лучше сделать его автоматическим, а не руками выделять под него память в начале блока и высвобождать в конце.

    ОтветитьУдалить
  4. По поводу ясности правила, да хороший низкоуровневый прогер это учтёт, но никак не среднячёк, я пишу просто потому что постоянно натыкаюсь на конструкции в стороннем коде вроде:


    std::vector[someObj*] objList;

    objList.push_back( new someObj( ... ) );

    //и далее
    objList.clear();

    Якобы по их мнению это очистит контейнер и деструктнрёт обьекты.


    Это, да, просто я с русской терминологией не всегда возможно чёток.

    Имелось в виду:

    Что не рационально писать, что-то вроде:
    // квадратные скобки т.к не поддерживает &gt, &lt
    std::list[object] olist;
    olist.push_back( object o( ... ) );

    и делать это на стёке, т.е в некой функции которая может вызываться сотни раз.

    проще либо указатели, либо понимание, что упадём на многократной конструкции деструкции, т.к "o" будет не просто клонирован (ctor), но
    и создан и удалён в теле самого push_back + (ctor/dtor)
    и того 3 вызова вместо 1 ого.

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

    ОтветитьУдалить
  5. Ну во-первых запись
    olist.push_back( object o( ... ) );
    синтаксически не верна. object o( ... ) - это объявление, но никак не выражение типа object. Это основы и это стоит знать.

    Во-вторых olist.push_back(...) делать можно ТОЛЬКО в некой функции и нигде более. И абсолютно не важно сотни или десятки раз ф-я вызывается - в любом случае все ее локальные данные на стеке создаются. Поэтому фраза:
    "Что не рационально писать...
    и делать это на стёке, т.е в некой функции ..." - полный бред. Невозможно что-либо делать не в функции!

    "проще либо указатели". Ха! Указатели на автоматические объекты? Даже и не смешно! Ты это себе вообще представляешь?

    list[obj*] l;
    for(int i=0;i<3;i++)
    {
    obj k(i);//объект в стеке
    i.push_back(&k);//сохраняем не объект, а указатель на него
    }
    //и что теперь в нашем списке? Скорее всего одинаковые адреса, доступ по которым в лучшем случае вызовет ошибку сегментации!

    Такой ты выход предлагаешь? Да, ctor/dtor только по разу для объекта вызовется, ну и что с того? Смысла все равно никакого в этом нет! И не должно быть! Взятие адреса автоматических объектов - чрезвычайно чревато! И тебе, как человеку говорящему "Я то как раз точно понимаю, когда и что использовать и как оно себя поведёт", это странно не знать... Для автоматических объектов нет альтернативы кроме копирования. Это тоже стоит знать.

    Если конструирование и/или копирование обходится слишком дорого - то используй альтернативные стратегии, в которых конструирование/копирование будет сведено к минимуму, или значительно удешевляться. Например, одноразовое выделение большого куска памяти и распределение его на каждый новый объект, а не многоразовое выделение маленьких кусочков.

    ОтветитьУдалить