На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> copy constructor
    Я понимаю, что вопрос глупый, но мне все таки очень интересно:
    А что собственно тако copy constructor и как его сделать?
      Вот выдержка из книги Джеффа Элджера "БИБЛИОТЕКА ПРОГРАММИСТА C++"
      Цитата

      Конструкторы копий
      Конструктор копий (copy constructor) определяется специальной сигнатурой:
      class Foo {
      public:
      Foo(const Foo&);
      };
      Foo::Foo(const Foo& f)...
      Конструктор копий предназначен для создания копий объектов. Эта задача может возникнуть в самых
      разных обстоятельствах.
      void Fn(Foo f) {...}
      void Gn(Foo& f) {...}
      Foo f;
      Foo f1(f);
      Foo f2 = f; // Конструирование, а не присваивание!
      Fn(f); // Вызывает конструктор копий для передачи по назначению
      const Foo f3;
      Gn(f3); // Конструктор копий используется
      // для создания неконстантной копии
      Давайте внимательно рассмотрим этот фрагмент. Строка Foo f1(f); создает новый экземпляр класса
      Foo, передавая другой экземпляр класса Foo в качестве аргумента. Это всегда можно сделать, если
      класс Foo не содержит чисто виртуальных функций. Не важно, объявили ли вы свой собственный
      конструктор копий; если нет, компилятор построит его за вас. Не важно, есть ли в Foo другие
      пользовательские конструкторы; в отличие от конструкторов без аргументов, конструктор копий
      доступен всегда.
      Строка Foo f2 = f выглядит как присваивание из-за присутствия оператора =, но на самом деле это
      альтернативный вариант вызова конструктора копий. Чтобы понять, чем присваивание отличается от
      инициализации, спросите себя: «Был ли объект сконструирован заранее или же его создание является
      частью команды?» Если объект уже существует, вы имеете дело с присваиванием. Если он создается на
      месте, как в пашем примере, используется конструктор копий.
      При вызове функции Fn() происходит передача по значению копии Foo. Конструктор копий
      используется для создания временной копии, существующей лишь во время выполнения Fn(). После
      этого вызывается деструктор копии, который уничтожает ее.
      Вызов функции Gn(), вероятно, ошибочен, и хороший компилятор прочитает вам суровую нотацию о
      стиле программирования на C++ — что-нибудь вроде:
      «Создается временная неконстантная копия — поучись программировать, тупица!» По крайней мере,
      со мной компиляторы обычно поступают именно так. Проблема заключается в том, что аргумент
      передается по ссылке, однако фактический аргумент является константным, а формальный — нет. Все
      изменения аргумента внутри Gn() вносятся в копию, а не в оригинал.
      В создаваемом компилятором конструкторе копий по умолчанию используется строго определенная
      последовательность вызова конструкторов копий базовых классов и переменных класса.
      1. Конструкторы копий базовых классов вызываются в том порядке, в котором они объявлены в
      списке наследования.
      2. Конструкторы копий переменных вызываются в том порядке, в котором они объявлены в
      объявлении класса.
      Описанный порядок применяется рекурсивно, то есть первым копируется первый базовый класс
      первого базового класса... и т. д. Звучит знакомо, не правда ли? Тот же порядок, что и для любого
      другого конструктора.
      С конструкторами копий, в отличие от всех остальных, компилятор ведет себя гордо и ревниво. Если
      вы перегрузите конструктор копий для некоторого класса, компилятор, фигурально выражаясь,
      умывает руки и отправляется домой. При отсутствии явного вызова конструкторов копий базовых
      классов и переменных класса в списке инициализации членов вашего собственного конструктора
      копий компилятор будет использовать конструктор без аргументов для инициализации базовых
      классов и переменных.
      class Foo {...};
      class Bar : public Foo {
      private:
      Foo f;
      public:
      Bar(const Bar&);
      };
      // Вероятно, ошибка
      Bar::Bar(const Bar& b)
      {
      // Стоп! Нет списка инициализации членов
      // Будут использованы конструкторы без аргументов
      // базового класса и переменной
      }
      // Вероятно, ошибки нет
      Bar::Bar(const Bar& b) : Foo(b), f(b.f) {...}
      Компилятор очень сильно обидится на первый конструктор копий — так сильно, что он спустит ваше
      произведение в мусоропровод и даже не сообщит об этом. Для инициализации базового класса и
      переменной будет использован конструктор Foo без аргументов. В 99 случаях из 100 это совсем не то,
      чего вы добивались; обычно требуется, чтобы базовые классы и переменные тоже копировались.
      Вероятно, второй вариант правилен. Базовый класс и переменная присутствуют в списке
      инициализации членов, поэтому будут вызваны их конструкторы копий (компилятор преобразует b к
      типу Foo в выражении Foo(b)).
      В некоторых ситуациях вас интересует именно поведение компилятора по умолчанию. В качестве
      примера рассмотрим следующий базовый класс, который присваивает уникальный серийный номер
      каждому производному объекту.
      class Serialized {
      private:
      static int NextSerialNumber;
      int serialNumber;
      public:
      Serialized(const Serialized&);
      Serialized();
      int SerialNumber();
      };
      // В Serialized.cpp
      int Serialized::NextSerialNumber = 0;
      Serialized::Serialized() : serialNumber(NextSerialNumber++)
      {
      }
      Serialized::Serialized(const Serialized&) : serialNumber(NextSerialNumber++)
      {
      }
      int Serialized::SerialNumber()
      {
      return serialNumber;
      }
      Нac не интересует, какой конструктор — без аргументов или копий — выберет компилятор во время
      компиляции производного класса, поскольку мы перегрузили оба конструктора, и они делают одно и
      то же.
        Спасибо! А то до меня все не доходило... измучился весь... :D
          первое, что я делаю, если вижу какую нибудь ну совсем чудесатую ошибку (что нибудь, ну
          совсем интересное) - это проверяю ;) - а правильно ли я написал конструктор копии?
          а второе - это проверяю - не поставил ли я в if() вместо == просто =. Эффект от этих
          2-х штюк бывает оччень похож ;)
            Цитата
            AQL, 11.02.04, 21:36
            первое, что я делаю, если вижу какую нибудь ну совсем чудесатую ошибку (что нибудь, ну
            совсем интересное) - это проверяю - а правильно ли я написал конструктор копии?
            а второе - это проверяю - не поставил ли я в if() вместо == просто =. Эффект от этих
            2-х штюк бывает оччень похож

            И третье (или нулевое) - а нужно ли было вообще писать конструктор копий? В большинстве случаев компилятор это сделает гораздо лучше.
              В большинстве случаев - да. ;) .
              Но только на компилятор надеяться я больше никогда не буду.
                Цитата
                AQL, 12.02.04, 12:05
                Но только на компилятор надеяться я больше никогда не буду.

                Чем же он тебя так обидел?
                  Да было пару раз на заре.
                  Складывал в vector объекты, а потом, когда оттуда доставал -
                  получал немножко не то, что клал (сильно не то). Объекты портились в момент копирования.
                  Долго я бился, пока кто то из товарищей мне не посоветовали. ;)

                  может быть к совсем-совсем современным компилерам это не относится
                  Сообщение отредактировано: AQL -
                    Цитата
                    AQL, 12.02.04, 12:17
                    может быть к совсем-совсем современным компилерам это не относится


                    я думаю это относится ко всем компиляторам, особо при копировании объекта в котором есть указатели, если нет, то можно забить.
                      Цитата AQL @ 12.02.04, 09:17
                      Складывал в vector объекты, а потом, когда оттуда доставал -
                      получал немножко не то, что клал (сильно не то). Объекты портились в момент копирования.

                      Неудивительно.

                      Это не "особенность" компиляторов, а задокументированная фича контейнеров.
                      Смотрим Стандарт
                      Цитата

                      23.1 Container requirements
                      ...
                      3. The type of objects stored in these components must meet the requirements of CopyConstructible types (20.1.3), and the additional requirements of Assignable types.


                      Т.е. обекты, находящиеся в контенере должны уметь "правильно" копироваться и присваиваться. "Правильно" -- в соответствии с тем, как этого хочет разаработчик этих типов. Если ничего особенного не нужно, то можно обойтись тем, что по умолчанию делает компилятор. А если, например, нужно как-то разруливать внутренние ресурсы (скажем, указатели: просто копировать значение указателя или копировать еще и указываемый объект?), то тогда надо писать свои копиконструкторы и присваивание.
                      1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                      0 пользователей:


                      Рейтинг@Mail.ru
                      [ Script execution time: 0,0312 ]   [ 15 queries used ]   [ Generated: 17.05.24, 19:25 GMT ]