На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела FAQ в группе разделов С++.
1. Раздел FAQ предназначен для публикации готовых статей.
2. Здесь нельзя задавать вопросы, для этого существуют соответствующие разделы:
Чистый С++
Visual C++ / MFC / WTL / WinApi
Borland C++ Builder
COM / DCOM / ActiveX / ATL
Сопутствующие вопросы
3. Внимание, все темы и сообщения в разделе премодерируются. Любое сообщение или тема будут видны остальным участникам только после одобрения модератора.
Модераторы: B.V., Qraizer
  
> Кто такие POD и не-POD типы , Как их различить и не ошибиться в использовании
    1. Краткий экскурс в местоположение POD и non-POD-типов в системе типов C++.
      Тут всё просто – все типы делятся на две группы: объектные типы и все остальные (к этим остальным относятся функции, ссылки и тип void. Грубо говоря, это типы, которые нельзя «пощупать», например, нельзя узнать их размер с помощью операции sizeof). Группа объектных типов содержит две подгруппы – POD и не-POD типы.
    2. Определение.
      POD – это аббревиатура от “Plain Old Data”, литературно я бы это перевёл как «Простые данные в стиле C»
    3. Содержание.
      К POD-типам относятся:
      1. все встроенные арифметические типы (включая wchar_t и bool);
      2. энумераторы, т.е. типы, объявленные с помощью ключевого слова enum;
      3. все указатели;
      4. POD-структуры (struct или class) и POD-объединения (union), которые удовлетворяют всем нижеприведенным требованиям:
        1. не содержат пользовательских конструкторов, деструктора или копирующего оператора присваивания (копирующий оператор присваивания – это такой нешаблонный нестатический operator=, у которого есть строго один параметр типа X, X&, const X&, volatile X& или const volatile X&, где X – тип рассматриваемой структуры или объединения);
        2. не имеют базовых классов;
        3. не содержат виртуальных функций;
        4. не содержат защищенных (protected) или закрытых (private) нестатических членов данных;
        5. не содержат нестатических членов данных не-POD-типов (или массивов из таких типов), а также ссылок.
      Соответственно, все оставшиеся объектные типы являются не-POD-типами.
      Примечание: наличие квалификаторов const и volatile не влияет на «POD-овость» типа.
    4. Отличия в поведении POD и не-POD типов.
      Впрочем, сами отличия описывать не буду – их много – лучше опишу основную причину этих отличий: для не-POD-типа нельзя сделать (практически) никаких предположений о том, как устроен объект. Внутри такого объекта в (практически) произвольном месте могут располагаться служебные области, неподконтрольные программисту. Например, рассмотрим POD-тип S:
      ExpandedWrap disabled
        struct S
        {
          char c;
          int i;
        // protected:
          char sz[10];
        } s1, s2;
         
        &s1 == &s1.c; // (1) всегда истина
        &s1.i == &s1.c + 1 + выравнивание; // (2) всегда истина
        &s1.i > &s1.c; // (3) всегда истина
        &s1.sz > &s1.i; // (4) всегда истина
         
        memset(&s1, 0, sizeof s1); // (5) обнуление объекта
        memcpy(&s1, &s2, sizeof s1); // (6) копирование объекта

      Все подобные штучки-дрючки (адресная арифметики, побитовая работа с объектом целиком и пр.) неприменимы для не-POD-типов. Раскомментированием "//protected:" приведенная структура превращается в не-POD-структуру. Во многих компиляторах формат представления в памяти объекта из данного примера не изменится, и все приведенные операции будут продолжать работать по-прежнему, но тем не менее, Стандартом С++ это не гарантируется (кроме условия (3)). С другой стороны, добавление виртуальной функции, например, практически на всех компиляторах вызовет нарушение (1) и некорректную работу (5). Вообщем, лучше полагаться на Стандарт, и всегда избегать делать предположения о представлении объекта в памяти.
    5. Для чего придуман такой геморрой с поддержанием в языке обоих типов объектов.
      Это всё от двуликости С++, от того, что он не является чистым ООП-языком. POD-объекты введены для обеспечения совместимости с C. А формат представления не-POD-объектов не задокументирован для того, чтобы не связывать руки производителям компиляторов в их стремлении придумывать наиболее эффективные способы реализации всех задумок создателей языка (тем более, что на разных платформах эти способы могут сильно разниться).
    6. Что делать любителям играться с битами представления объекта (по типу (5) )
      Перестать играться с битами. Зачастую такая игра показывает, что программист пытается на С++ писать в стиле С – это неразумно.

      Я говорить так, как говорят Саттер с Александреску, не умею, поэтому лучше пусть говорят они в завершение данной статьи:
      Цитата
      Just define types appropriately, then read and write data using those types instead of thinking bits and words and addresses. The C++ memory model ensures efficient execution without forcing you to rely on manipulating representation. So don't.
      / Просто определите типы корректно, затем читайте и записывайте данные, используя эти типы вместо работы с битами, словами и адресами. Модель памяти С++ обеспечивает эффективную работу, не заставляя Вас работать с представлением данных в памяти. Так и не делайте этого. /
      Еще одно дополнение - отличие в поведении при простом объявлении и через оператор new. Иллюстрирую кодом:

      ExpandedWrap disabled
        #include <stdio.h>
         
        typedef struct tag_s {
          int a;
          int b;
        } s;
         
        class c : public s {
          public:
          c() {
            a = 0;
            b = 0;
          }
          operator s(){
            return *this;
          }
        };
         
        class cni : public s {
          public:
          cni() {
          }
          operator s(){
            return *this;
          }
        };
         
        void Dump(s& t, const char* d){
          printf("%5s: %8d %8d\n", d, t.a, t.b);
        }
         
        int main(void)
        {
          cni cni1;
          s s1;
          c c1;
         
          Dump(c1,   "c1");
          Dump(cni1, "cni1");
          Dump(s1,   "s1");
         
          cni *pcni = new cni;
          s *ps = new s;
          c *pc = new c;
         
          Dump(*pc,   "pc");
          Dump(*pcni, "pcni");
          Dump(*ps,   "ps");
         
          delete pcni;
          delete ps;
          delete pc;
          
          return 0;
        }


      Результат:

      ExpandedWrap disabled
           c1:        0        0
         cni1:     2048        0
           s1:     2048        4
           pc:        0        0
         pcni:        0        0
           ps:        0        0


      Как видно, только для класса с определенным конструктором инициализация происходит и при объявлении, и при создании экземпляра через new. Для структуры и производного от нее класса - только при создании через new.
        Цитата barazuk @
        Как видно, только для класса с определенным конструктором инициализация происходит и при объявлении, и при создании экземпляра через new. Для структуры и производного от нее класса - только при создании через new.

        Неправильный вывод.
        Никакой инициализации в строке s *ps = new s; не делается. Тебе просто повезло, что operator new вернул обнуленную память. Для гарантии, что члены такой структуры будут обнулены, надо писать s *ps = new s();
        Про инициализацию cni - тут вообще нет инициализации, т.к. конструктор, вызываемый в любом случае, ничего инициализирующего тут не делает. И это никаким боком к рассматриваемой теме не относится.
          А если есть простой пользовательский конструктор?
          Например:

          ExpandedWrap disabled
            struct Point
            {
                Point(int _x, int _y)
                    :x(_x), y(_y)
                {
                }
             
                int x;
                int y;
            };


          Он введен для удобства - чтобы не писать несколько строк присваиваний для инициализации структуры

          Это POD-тип или нет? :)
            Цитата grustnoe @
            А если есть простой пользовательский конструктор?

            К сожалению, не-POD , если следовать букве закона, то бишь Стандарта.
            Фактически, я не встречался с компиляторами, которые как-то меняли представление объекта при добавлении такого элементарного конструктора. Но это не значит, что их нет... :)
            Сообщение отредактировано: Hryak -
              Цитата grustnoe @
              Это POD-тип или нет? :)

              нет
                Цитата grustnoe @
                Он введен для удобства - чтобы не писать несколько строк присваиваний для инициализации структуры

                А чем тебя (в таком случае) не устраивает инициализация в стиле "С":
                ExpandedWrap disabled
                  Point pt = {1, 2};
                  Цитата Flex Ferrum @
                  чем тебя (в таком случае) не устраивает инициализация в стиле "С":

                  не всегда удобно так писать, очень не всегда, например при возврате из функции getZeroPoint или передачи в функцию. Самого "убивала", такая мелочь. Самый простой выход написать функцию
                  ExpandedWrap disabled
                    TPoint  Point( int, int ); // сразу бросается в глаза неудобство создания POD структуры без каких бы то небыло префиксов.
                    Цитата Hryak
                    объектные типы и все остальные (к этим остальным относятся функции, ссылки и тип void. Грубо говоря, это типы, которые нельзя «пощупать», например, нельзя узнать их размер с помощью операции sizeof

                    Поправочка: sizeof можно применять к любым типам-ссылкам на объектные типы (хотя типы-ссылки объектными типами действительно не являются).
                    Сообщение отредактировано: Unreal Man -
                      Применять-то sizeof можно. Но нельзя узнать размер "типа-ссылки". Т.е. sizeof(Type&) тоже самое, что sizeof(Type).
                        Цитата LPBOY @
                        Применять-то sizeof можно. Но нельзя узнать размер "типа-ссылки". Т.е. sizeof(Type&) тоже самое, что sizeof(Type).

                        Как это понимать? :wacko: У большинства типов-ссылок есть размер. Под этим размером понимается размер соответствующего типа-нессылки. А под размером типа-нессылки понимается размер объекта этого типа.

                        Есть довольно практичный синоним понятия «объект» – всякая сущность, которая может быть элементом массива.
                          Цитата Unreal Man @
                          У большинства типов-ссылок есть размер. Под этим размером понимается размер соответствующего типа-нессылки.

                          Как тогда ты объяснишь, что у меня размер такой структуры
                          ExpandedWrap disabled
                            struct A
                            {
                                int i;
                                const double &r;
                                A():r(1.0){}
                            };

                          равен 8-ми, тогда как
                          sizeof(int) + sizeof(double) = 4 + 8 = 12
                          В большинстве реализаций ссылки реализуются посредством указателей и соответственно занимают такой же объем памяти. Хотя в стандарте все же написано "It is unspecified whether or not a reference requires storage". Тот факт, что sizeof(T&)===sizeof(T) объясняется лишь тем, что любые действия над ссылками сводятся к действиям над объектами, на которые они ссылаются.
                          Цитата Unreal Man @
                          Есть довольно практичный синоним понятия «объект» – всякая сущность, которая может быть элементом массива.

                          Как насчет объектов таких классов
                          std::type_info,
                          ExpandedWrap disabled
                            struct A
                            {
                                A(int){}
                            };

                          ,
                          ExpandedWrap disabled
                            struct B
                            {
                            private: B(){}
                            };
                            Цитата LPBOY @
                            Как тогда ты объяснишь

                            Размер типа-ссылки не равен физическому размеру объявляемой под этим типом сущности. По сути ты апеллируешь к понятию физического размера, но ведь он присущ лишь объекту. А что такое тип? Тип – это абстракция, у которой не может быть физического размера. Тем не менее, как и для других абстракций, для типа ради удобства можно ввести формальное понятие – в данном случае понятие размера.

                            Цитата LPBOY @
                            Тот факт, что sizeof(T&)===sizeof(T) объясняется лишь тем, что любые действия над ссылками сводятся к действиям над объектами, на которые они ссылаются.

                            Именно так. Но давай всё же зададимся таким вопросом: как удобнее называть то, что получается, когда мы применяем оператор sizeof к типу-ссылке: «результат применения оператора sizeof» или же «размер»?

                            Цитата LPBOY @
                            Как насчет объектов таких классов

                            Подразумевалась лишь возможность объявления массива объектов, реальное же существование объектов в составе массива в run-time не рассматривалось. Возможно, более правильно было бы говорить об объектных типах, а не о самих объектах, но у меня что-то не получается это нормально сформулировать применительно к типам :)

                            ExpandedWrap disabled
                              struct C
                              {
                                  std::type_info arr1[2];
                                  A arr2[2];
                                  B arr3[2];
                              };

                            Ты можешь определить такую структуру, это вполне законно. Можно сказать, что arr1 – это массив, элементы которого имеют тип std::type_info. Сущности же необъектных типов не могут быть элементами массива. Т.е. не может быть массива функций и ссылок – такие объявления будут некорректными (ну а сущностей типа void вообще не бывает, что уже говорить про массивы таких сущностей...).

                            Реальную практическую пользу представляет из себя следующий код:

                            ExpandedWrap disabled
                              template <class T>
                                  class IsObject
                              {
                                  template <class T_>
                                      static char (&Check(...))      [1];
                                  template <class T_>
                                      static char (&Check(T_(*)[1])) [2];
                               
                              public:
                                  static const bool value = sizeof Check<T>(0) - 1;
                              };

                            Этот класс предназначен для того, чтобы определять принадлежность типа T к объектным типам. Если T – объектный тип, то типы T[1] и T(*)[1] являются корректно составленными, и тогда выбирается вторая функция. Если же тип T – необъектный, то T(*)[1] не является корректно составленным типом, и, как следствие, выбирается функция с эллипсисом.
                            Сообщение отредактировано: Unreal Man -
                              Цитата Unreal Man @
                              По сути ты апеллируешь к понятию физического размера, но ведь он присущ лишь объекту. А что такое тип? Тип – это абстракция, у которой не может быть физического размера.

                              Я и говорил о размерах объектов, а не типов. Запись sizeof(Type) означает "размер объекта типа Type", а не "размер типа Type".

                              Кстати сравни свои аргументы:
                              1."У большинства типов-ссылок есть размер."
                              2. "Тип – это абстракция, у которой не может быть физического размера."
                              Противоречия не замечаешь?
                              Цитата Unreal Man @
                              Именно так. Но давай всё же зададимся таким вопросом: как удобнее называть то, что получается, когда мы применяем оператор sizeof к типу-ссылке: «результат применения оператора sizeof» или же «размер»?

                              Это к чему? :wacko:
                              Цитата Unreal Man @
                              Подразумевалась лишь возможность объявления массива объектов, реальное же существование объектов в составе массива в run-time не рассматривалось.

                              У меня именно ошибки времени компиляции, никакого рантайма. :)
                              Цитата Unreal Man @
                              Ты можешь определить такую структуру, это вполне законно.
                              ...
                              Этот класс предназначен для того, чтобы определять принадлежность типа T к объектным типам.
                              ...

                              Хорошо, но все это в лучшем случае критерий определения объектных типов, но никак не "синоним понятия «объект»".

                              Напомню с чего началась эта дискуссия:
                              Hryak > нельзя узнать их (необъектные типы) размер с помощью операции sizeof
                              Unreal Man > Поправочка: sizeof можно применять к любым типам-ссылкам на объектные типы
                              У слов "узнать" и "применять" разный смысл. :wall:
                                Что-то мне этот мелочный терминологический спор уже надоел...

                                Цитата LPBOY @
                                Запись sizeof(Type) означает "размер объекта типа Type", а не "размер типа Type".

                                С такой трактовкой получается, что здесь

                                Цитата Hryak @
                                Грубо говоря, это типы, которые нельзя «пощупать», например, нельзя узнать их размер с помощью операции sizeof

                                Hryak попросту лил воду, ибо размер вообще никакого типа нельзя узнать, поскольку понятия размера для типа якобы не существует.

                                Но давай почитаем тот же стандарт:

                                Цитата ISO/IEC 14­882:2003(E) §5.3.3 Sizeof
                                When applied to a reference or a reference type, the result is the size of the referenced type.

                                Значит, понятие размера для типа всё же имеет место, не так ли? И поэтому можно говорить, что sizeof(Type) означает «размер типа Type», что в свою очередь означает «размер объекта типа Type» (если речь идёт об объектном типе).

                                Цитата LPBOY @
                                1."У большинства типов-ссылок есть размер."
                                2. "Тип – это абстракция, у которой не может быть физического размера."
                                Противоречия не замечаешь?

                                Нет, не замечаю.

                                Цитата LPBOY @
                                Это к чему?

                                Ты никогда не задумывался, для чего существует такая штука, как терминология?
                                Сообщение отредактировано: Unreal Man -
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0519 ]   [ 15 queries used ]   [ Generated: 1.05.24, 22:37 GMT ]