• R.12.1 Конструкторы
  • R.12.2 Временные объекты
  • R.12.3 Преобразования
  • R.12.3.1 Преобразование с помощью конструктора
  • R.12.3.2 Функции преобразования
  • R.12.4 Деструкторы
  • R.12.5 Свободная память
  • R.12.6 Инициализация
  • R.12.6.1 Явная инициализация
  • R.12.6.2 Инициализация членов и базовых классов
  • R.12.7 Конструкторы и деструкторы
  • R.12.8 Копирование объектов класса
  • R.12 Специальные функции-члены

    Некоторые функции-члены считаются специальными, поскольку они влияют на то, как объекты класса создаются, копируются и уничтожаются, и как значения одного типа преобразуются в значения другого типа. Часто такие функции вызываются неявно.

    Эти функции-члены подчиняются обычным правилам доступа (§R.11). Например, описание конструктора со спецификацией protected гарантирует, что создавать объекты с его помощью смогут только производные классы и друзья.

    R.12.1 Конструкторы

    Конструктором называется функция-член, имя которой совпадает с именем класса, он используется для построения значений, имеющих тип данного класса. Если в классе есть конструктор, то каждый объект этого класса перед произвольным использованием будет инициализироваться, см. §R.12.6.

    Конструктор может вызываться для объекта со спецификацией const или volatile. Сам конструктор нельзя описывать со спецификацией const или volatile (§R.9.3.1). Конструктор также не может иметь спецификацию virtual или static.

    Конструкторы не наследуются, однако, стандартные конструкторы и конструкторы копирования при необходимости создаются транслятором (§R.12.8). Такие конструкторы являются общими.

    Стандартным конструктором для класса X является такой конструктор класса X, который можно вызывать без параметров. Стандартный конструктор для класса X будет создан только тогда, когда для класса X не описано ни одного конструктора.

    Конструктором копирования для класса X называется конструктор, который вызывается для копирования объекта класса X, т.е. вызывается с одним параметром типа X. Например, X::X(const X&) и X::X(X&, int=0) являются конструкторами копирования. Конструктор копирования создается только тогда, когда не описано ни одного конструктора копирования.

    Конструктор копирования для класса X не должен иметь в качестве параметра объект типа X, например X::X(X) незаконное обращение.

    Конструктор для массива элементов вызывается в порядке возрастания адресов элементов (§R.8.2.4).

    Если у класса есть базовые классы с конструктором или члены, являющиеся объектами с конструктором, их конструкторы вызываются прежде, чем конструктор производного класса. В §R.12.6.2 объясняется как задаются параметры для таких конструкторов и как определяется порядок их вызова.

    Объект класса с конструктором не может быть членом объединения.

    Для конструктора не нужно указывать никакого типа возвращаемого значения, даже void. В операторе return в теле конструктора нельзя указывать возвращаемое значение. Не допустима операция взятия адреса конструктора.

    Конструктор можно явно использовать для создания объектов его типа с помощью следующей записи:

    имя-класса ( список-выражений opt )

    Приведем пример:

    complex zz = complex(1,2.3);

    print(complex(7.8,1.2));

    Объект, созданный таким образом является безымянным (если только конструктор не использовался для инициализации поименованной переменной как zz выше), а время его жизни ограничено выражением, в котором он был создан, см. §R.12.2.

    В конструкторе можно вызывать функцию-член, см. §R.12.7.

    R.12.2 Временные объекты

    В некоторых ситуациях транслятору бывает необходимо или удобно создавать временные объекты. Использование временных объектов зависит от реализации. Если транслятору понадобился временный объект типа класса с конструктором, он должен обеспечить вызов конструктора для этого временного объекта. Аналогично, необходимо вызывать деструктор для объекта класса, в котором описан деструктор. Приведем пример:

    class X {

     //…

    public:

     //…

     X(int);

     X(X&);

     ~X();

    };


    X f(X);


    void g()

    {

     X a(1);

     X b = f(X(2));

     a = f(b);

    }

    Здесь нужен временный объект для построения X(2), прежде чем передать его функции f() с помощью X(X&). Альтернативное решение, - построить объект X(2) в памяти, используемой для хранения параметра при первом вызове f(). Помимо этого, временный объект может понадобиться для хранения результата f(X(2)) прежде, чем копировать его в объект b с помощью X(X&), и здесь возможно альтернативное решение: хранить результат f(X(2)) в памяти для объекта b. С другой стороны, существует много функций f(), для которых выполнение выражения a=f(a) требует временного объекта или для параметра a, или для результата f(a), чтобы избежать нежелательного использования памяти, которой приписывается имя a.

    Транслятор обязан гарантировать уничтожение временных объектов. Точный момент уничтожения определяется реализацией. С временными объектами можно производить только две операции: выбрать значение объекта (неявно копируя его) для использования в другом выражении, или взять ссылку на него. Если значение временного объекта получено, он считается ненужным и может уничтожаться немедленно. Если на него получена ссылка, то уничтожать его нельзя, пока существует ссылка. Уничтожение должно произойти до выхода из области определенности, в которой был создан временный объект.

    Другой вид временных объектов обсуждается в §R.8.4.3.

    R.12.3 Преобразования

    Преобразования объектов класса можно задать с помощью конструкторов или функций преобразования.

    Такие преобразования, обычно называемые пользовательскими, используются неявно в совокупности со стандартными преобразованиями (§R.4). Например, функцию с формальным параметром типа X можно вызывать не только с параметром типа X, но и параметром типа T, если существует преобразование типа T в X. Пользовательские преобразования применяются в тех же ситуациях, что и стандартные: преобразование инициализаторов (§R.8.4), параметров функции (§R.5.2.2), возвращаемых функцией значений (§R.6.6.3, §R.8.2.5), выражений фактических параметров (§R.5), выражений, управляющих циклом и выбором операторов (§R.6.4, §R.6.5) и явные операции преобразования типа (§R.5.2.3, §R.5.4).

    Пользовательские преобразования применяются только в случае их однозначности (§R.10.1.1, §R.12.3.2). Преобразования проходят проверку на соответствие правилам доступа (§R.11). Как всегда проверка доступа осуществляется после разрешения неоднозначности (§R.10.4).

    Применение преобразований при вызове функции рассматривается на примерах, приведенных ниже, а также обсуждается в §R.13.2.

    R.12.3.1 Преобразование с помощью конструктора

    Конструктор, имеющий единственный параметр, задает преобразование типа своего фактического параметра в тип его класса, например:

    class X {

     //…

    public:

     X(int);

     X(const char*, int = 0);

    };


    void f(X arg) {

     X a = 1; // a = X(1);

     X b = "Jessie"; // b = X("Jessie",0)

     a = 2; // a = X(2)

     f(3); // f(X(3))

    }

    Если в классе X нет конструктора, который допускает заданный тип, не делается попытки найти какой-либо конструктор другого класса или функцию преобразования для приведения заданного значения в значение типа,допустимого для конструктора класса X, например:

    class X { /*… */ X(int); };

    class Y { /*… */ Y(X); };

    Y a = 1; // недопустимо: преобразование Y(X(1))

      // не применяется

    R.12.3.2 Функции преобразования

    Функция-член класса X, имя которой имеет вид,

    имя-функции-преобразования:

     operator имя-типа-преобразования

    имя-типа-преобразования:

     список-спецификаций-типа opt операция-ptr opt

    задает преобразование из типа X в тип, определяемый конструкцией имя-типа-преобразования. Такие функции-члены называются функциями преобразования. В конструкции список-спецификаций-типа нельзя описывать классы, перечисления и имена-typedef, а также нельзя задавать типы формальных параметров и тип возвращаемого значения.

    Приведем пример:

    class X {

     //…

    public:

     operator int();

    };


    void f(X a)

    {

     int i = int(a);

     i = (int)a;

     i = a;

    }

    Здесь во всех трех операторах присваиваемое значение будет преобразовываться с помощью функции X::operator int(). Пользовательские преобразования не ограничиваются только использованием в присваивании и инициализации, например:

    void g(X a, X b)

    {

     int i = (a) ? 1+a : 0;

     int j = (a&&b) ? a+b : i;

     if (a) {//…

     }

    }

    Операции преобразования наследуются. Функции преобразования могут быть виртуальными.

    К данному значению неявно применяется не более одного пользовательского преобразования (с помощью конструктора или функции преобразования), например:

    class X {

     //…

    public:

     operator int();

    };


    class Y {

     //…

    public:

     operator X();

    };


    Y a;

    int b = a; // недопустимо: преобразование

      // a.operator X().operator int() не применяется

    int c = X(a); // нормально: a.operator X().operator int()

    Пользовательские преобразования осуществляются неявно только при условии их однозначности. Функция преобразования производного класса не закрывает функцию преобразования базового класса, если только они не преобразуют к одному и тому же типу, например:

    class X {

    public:

     //…

     operator int();

    };


    class Y: public X {

    public:

     //…

     operator void*();

    };


    void f(Y& a)

    {

     if (a) {// ошибка: неоднозначность

     }

    }

    R.12.4 Деструкторы

    Деструктором называется функция-член класса cl с именем ~cl, она используется для уничтожения значений типа cl непосредственно перед уничтожением объекта, содержащего их. Деструктор не имеет формальных параметров и для него нельзя задать тип возвращаемого значения (даже void). Нельзя применять операцию взятия адреса для деструктора. Можно вызывать деструктор для объектов со спецификацией const или volatile, но сам деструктор нельзя описывать с этими спецификациями (§R.9.3.1). Деструктор не может быть и статическим.

    Деструкторы не наследуются. Если базовый класс или член имеют деструктор, а сам производный класс - нет, то создается стандартный деструктор, который вызывает деструкторы базовых классов и членов производного класса. Такие созданные деструкторы имеют спецификацию public.

    Тело деструктора выполняется прежде деструкторов для объектов, являющихся членами. Деструкторы для нестатических объектов, являющихся членами, выполняются прежде, чем деструкторы для базовых классов. Деструкторы для невиртуальных базовых классов выполняются прежде, чем деструкторы для виртуальных базовых классов. Деструкторы для невиртуальных базовых классов выполняются в порядке, обратном их описанию в производном классе. Деструкторы виртуальных базовых классов выполняются в порядке, обратном появлению их при обходе снизу и слева-направо ацикличного направленного графа базовых классов. Здесь "слева-направо" означает порядок появления имен базовых классов, который был при описании их в производном классе. Деструкторы для элементов массива вызываются в порядке, обратном вызовам при их построении.

    Деструктор может быть виртуальным.

    В деструкторе можно вызывать функцию-член, см. §R.12.7.

    Объект класса с деструктором не может быть членом объединения.

    Деструкторы вызываются неявно в следующих случаях:

    (1) когда исчезают из области видимости объекты auto (§R.3.5) или временные объекты (§R.12.2, §R.8.4.3);

    (2) при завершении программы (§R.3.4) для построенных статических объектов (§R.3.5);

    (3) благодаря обращению к операции delete (§R.5.3.4) для объектов, созданных с помощью операции new (§R.5.3.3);

    (4) при явном вызове.

    Когда деструктор вызывается операцией delete, то он освобождает память для самого большего из производных классов (§R.12.6.2) того объекта, который использовал операцию delete() (§R.5.3.4), например:

    class X {

     //…

     public:

     X(int);

     ~X();

    };


    void g(X*);


    void f() // общий случай

    {

     X* p = new X(111); // размещение и инициализация

     g(p);

     delete p; // освобождение и удаление

    }

    Явные вызовы деструкторов применяются редко. Примером этого может служить вызов деструктора для объектов, созданных в некоторой определенной адресной области с помощью операции new. Размещение объектов в определенном адресном пространстве и последующее уничтожение их может потребоваться для использования специфических возможностей аппаратуры и для правильного функционирования оперативной памяти. Приведем пример:

    void* operator new(size_t, void* p) { return p; }


    void f(X* p);


    static char buf[sizeof(X)];


    void g() // редкий, специальный случай

    {

     X* p = new(buf) X(222); // размещение в buf[] и инициализация

     f(p);

     p-›X::~X(); // удаление

    }

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

    int* p;

    //…

    p-›int::~int();

    Использование такой записи для типа, у которого нет деструктора, проходит бесследно. Допуская такую запись, мы разрешаем пользователям писать программу, не задумываясь над тем, есть ли данного типа деструктор.

    R.12.5 Свободная память

    Когда создается объект с помощью операции new, для получения свободной памяти вызывается (неявно) функция operator new() (§R.5.3.3).

    Если функция operator new() не может выполнить запрос, она возвращает 0.

    В классе X функция X::operator new() является статическим членом, даже если она не описана явно как static. Первый ее параметр должен иметь тип size_t, - зависящий от реализации целочисленный тип, который определен в стандартном заголовочном файле ‹stddef.h›, и она должна возвращать значение типа void*, например:

    class X {

     //…

     void* operator new(size_t);

     void* operator new(size_t, Arena*);

    };

    Правила выбора подходящей функции operator new() обсуждаются в §R.5.3.3.

    В классе X функция X::operator delete() является статическим членом, даже если она не описана явно как static. Первый ее параметр должен быть типа void* и можно добавлять второй параметр типа size_t. Она не может возвращать какое-либо значение и тип возвращаемого значения должен быть void, например:

    class X {

     //…

     void operator delete(void*);

    };


    class Y {

     //…

     void operator delete(void*, size_t);

    };

    В каждом классе можно описать только одну функцию operator delete(), значит эта функция не может быть перегруженной. Глобальная функция operator delete() имеет единственный параметр типа void*.

    Если функция описана с двумя формальными параметрами, она вызывается с двумя параметрами, второй из которых показывает размер удаляемого объекта. Передаваемый размер определяется с помощью деструктора (если он есть) или по типу (статическому) указателя на удаляемый объект. Операция пройдет корректно, если тип указателя, заданного как фактический параметр, будет совпадать с типом объекта (а не будет, к примеру, просто типом указателя на базовый класс) или, если этот тип является типом указателя на базовый класс с виртуальным деструктором.

    Для массивов объектов типа класс используются глобальные функции operator new() и operator delete() (§R.5.3.3, §R.5.3.4).

    Поскольку функции X::operator new() и X::operator delete() статические, они не могут быть виртуальными. Функция operator delete(), которая вызывается из деструктора для освобождения памяти, выбирается по обычным правилам областей видимости, например:

    struct B {

     virtual ~B();

     void* operator new(size_t);

     void operator delete(void*);

    };


    struct D: B {

     ~D();

     void* operator new(size_t);

     void operator delete(void*);

    };


    void f()

    {

     B* p = new D;

     delete p;

    }

    В этом примере память для объекта класса D выделяется с помощью D::operator new(), а благодаря наличию виртуального деструктора, освобождается с помощью D::operator delete().

    R.12.6 Инициализация

    Объект класса без конструкторов, без частных или защищенных членов, без виртуальных функций и без базовых классов можно инициализировать с помощью списка инициализаторов (§R.8.4.1). Объект класса с конструктором должен инициализироваться или иметь стандартный конструктор (§R.12.1). Стандартный конструктор используется для объектов, которые не проходят явной инициализации.

    R.12.6.1 Явная инициализация

    Объекты классов с конструкторами (§R.12.1) можно инициализировать списком выражений, заключенным в скобки. Этот список считается списком фактических параметров для вызова конструктора, производящего инициализацию. Иначе, в качестве инициализатора задается с помощью операции = одно значение. Оно используется как фактический параметр для конструктора копирования. Обычно можно обойтись без вызова конструктора копирования, например:

    class complex {

     //…

    public:

     complex();

     complex(double);

     complex(double,double);

     //…

    };


    complex sqrt(complex,complex);


    complex a(1); // инициализация вызовом

      // complex(double)

    complex b = a; // инициализация копированием `a'

    complex c = complex(1,2); // конструктор complex(1,2)

      // вызывается complex(double,double)

      // и копируется в `c'

    complex d = sqrt(b,c); // вызывается sqrt(complex,complex),

      // результат копируется в `d'

    complex e; // инициализация вызовом конструктора

    complex f = 3; // complex(3), вызывается

      // complex(double) и результат

      // копируется в `f'

    Перегрузка операции присваивания = не оказывает влияние на инициализацию.

    Инициализация, происходящая при передаче фактических параметров и при возврате из функции, эквивалентна инициализации вида

    T x = a;

    Инициализация, происходящая в выражении операции new (§R.5.3.3) и при инициализации базовых классов и членов, эквивалентна инициализации вида

    T x(a);

    Для массивов объектов класса с конструкторами используются при инициализации (§R.12.1) конструкторы как и для одиночных объектов. Если оказалось, что инициализаторов в списке меньше, чем элементов массива, используется стандартный конструктор (§R.12.1). Если его нет, список инициализаторов должен быть полным. Приведем пример:

    complex cc = { 1, 2 }; // ошибка: необходимо

      // использовать конструктор

    complex v[6] = { 1,complex(1,2),complex(),2 };

    Здесь v[0] и v[3] инициализируются значением complex::complex(double), v[1] инициализируется complex::complex(double,double), а v[2], v[4] и v[5] инициализированы complex::complex().

    Объект класса M может быть членом класса X в одном из следующих случаев:

    (1) M не имеет конструктора;

    (2) M имеет стандартный конструктор;

    (3) X имеет конструктор и каждый из них задает инициализатор-ctor (§R.12.6.2) для члена M.

    В случае 2 при создании составного объекта вызывается стандартный конструктор. Если член составного объекта имеет деструктор, то он вызывается при уничтожении составного объекта.

    Конструкторы для нелокальных статических объектов вызываются в том порядке, в каком они идут в тексте программы, деструкторы вызываются в обратном порядке, см. также §R.3.4, §R.6.7, §R.9.4.

    R.12.6.2 Инициализация членов и базовых классов

    В определении конструктора можно задать инициализацию прямых базовых классов и членов, не наследуемых из базовых классов. Это особенно полезно для тех объектов, констант и ссылок, для которых различаются семантики присваивания и инициализации. Конструкция инициализатор-ctor имеет вид

    инициализатор-ctor:

     : список-инициализаторов-членов

    список-инициализаторов-членов:

     инициализатор-члена

     инициализатор-члена , список-инициализаторов-члена

    инициализатор-члена:

     полное-имя-класса ( список-выражений opt )

     идентификатор

    Список параметров используется для инициализации нестатических членов или объектов базового класса. Это единственный способ инициализации нестатических членов, являющихся ссылками или объектами типа const, например:

    struct B1 { B1(int); /*… */ };

    struct B2 { B2(int); /*… */ };


    struct D: B1, B2 {

     D(int);

     B1 b;

     const c;

    };


    D::D(int a): B2(a+1), B1(a+2), c(a+3), b(a+4)

    {/*… */}

    D d(10);

    В начале инициализируются базовые классы в порядке их описания (независимо от порядка инициализаторов-членов), затем по той же схеме инициализируются члены, и наконец выполняется тело D::D() (§R.12.1). Порядок описания выдерживается для того, чтобы гарантировать, что вложенные объекты и члены будут уничтожаться в порядке, обратном их инициализации.

    Особый случай представляют виртуальные базовые классы. Они создаются прежде, чем любой невиртуальный базовый класс и в том же порядке, в каком появляются при обходе снизу и слева-направо ацикличного направленного графа базовых классов. Порядок "слева-направо" - это тот, в котором имена базовых классов задаются при описании в производном классе.

    Полным называется объект, который не является вложенным объектом, представляющим некоторый базовый класс. Класс такого объекта называют наибольшим производным классом объекта. Все вложенные объекты виртуальных базовых классов инициализируются с помощью конструктора наибольшего производного класса. Если в конструкторе наибольшего производного класса не задан инициализатор-члена для виртуального базового класса, тогда этот виртуальный базовый класс должен иметь стандартный конструктор,либо не иметь никакого конструктора. Всякий инициализатор-члена для виртуального базового класса, заданный не в конструкторе класса полного объекта, игнорируется. Приведем пример:

    class V {

    public:

     V();

     V(int);

     //…

    };


    class A: public virtual V {

    public:

     A();

     A(int);

     //…

    };


    class B: public virtual V {

    public:

     B();

     B(int);

     //…

    };


    class C: public A, public B, private virtual V {

    public:

     C();

     C(int);

     //…

    };


    A::A(int i): V(i) {/*… */}

    B::B(int i) {/*… */}

    C::C(int i) {/*… */}


    V v(1); // use V(int)

    A a(2); // use V(int)

    B b(3); // use V()

    C c(4); // use V()

    Инициализатор-члена вычисляется в области видимости конструктора, в котором он появился. Например, в следующем фрагменте

    class X {

     int a;

    public:

     const int& r;

     X()::r(a) {}

    };

    X::r инициализируется для каждого объекта класса X ссылкой на X::a.

    R.12.7 Конструкторы и деструкторы

    В конструкторах и деструкторах можно вызывать функцию-член. Отсюда следует, что можно вызывать (непосредственно или косвенно) виртуальные функции. Вызываемая функция должна быть определена в классе самого конструктора или деструктора или в базовых классах, но не должна быть функцией, которая их подавляет в производном классе. Этим обеспечивается то, что еще несозданные объекты не будут использованы при выполнении конструктора или деструктора. Рассмотрим пример:

    class X {

    public:

     virtual void f();

     X() { f(); } // вызов X::f()

     ~X() { f(); } // вызов X::f()

    };


    class Y: public X {

     int& r;

    public:

     void f()

     {

      r++; // беда, если `r' не инициализировано

     }

     Y(int& rr)::r(rr) {}

    };

    Результат непосредственного или косвенного вызова из конструктора чистой виртуальной функции для инициализируемого объекта неопределен, если только явно не использовано уточнение имени функции (§R.10.3).

    R.12.8 Копирование объектов класса

    Объекты класса могут копироваться двумя способами: либо присваиванием (§R.5.17), либо инициализацией (§R.12.1, §R.8.4), которая может происходить при передаче параметров (§R.5.2.2) или результата функции (§R.6.6.3). Для класса X эти две операции концептуально реализуются как операция присваивания и конструктор копирования (§R.12.1). В программе можно определить или одну из них, или обе. Если пользователь не определил их в программе, то они будут для всех членов класса X определяться соответственно как присваивание по членам и инициализация по членам.

    Если все базовые классы и все члены класса X имеют конструктор копирования, в котором допустимы в качестве параметра объекты типа const, то порождаемый конструктор копирования для X будет иметь единственный параметр типа const X& и записываться так:

    X::X(const X&)

    Иначе, у него будет единственный параметр типа X&:

    X::X(X&)

    и инициализация копированием объектов типа const класса X будет невозможна.

    Аналогично, если все базовые классы и члены класса X имеют операцию присваивания, допускающую параметры типа const, тогда порождаемая для X операция присваивания будет иметь единственный параметр типа const X& и записываться так:

    X& X::operator=(const X&)

    Иначе, у нее будет единственный параметр типа X&:

    X& X::operator=(X&)

    и присваивание копированием объектов класса X типа const будет невозможно. Стандартная операция присваивания возвращает ссылку на объект, который нужно было копировать.

    Объекты, представляющие виртуальные базовые классы, будут инициализироваться только один раз с помощью порождаемого конструктора копирования. Объекты, представляющие виртуальные базовые классы, допускают присваивания им только один раз с помощью порождаемой операции присваивания.

    Присваивание по членам и инициализация по членам означают следующее: если класс X имеет в качестве члена класс M, для реализации присваивания и инициализации члена используются операции присваивания в M и конструктор копирования M соответственно. Если класс имеет член типа const, или член, являющийся ссылкой, или член или базовый класс такого класса, где функция operator=() является частной, то для него стандартная операция присваивания не может быть создана. Аналогично, если член или базовый класс класса M имеет частный конструктор копирования, то стандартный конструктор копирования для такого класса не может быть создан.

    Пока не появится необходимость в определении, стандартные присваивание и конструктор копирования будут только описаны (т.е. не будет создано тело функции). Иными словами, функция X::operator=() будет порождена только тогда, когда нет явного описания операций присваивания, а объект класса X присваивается объекту класса X или объекту класса, производного от X, или вычисляется адрес функции X::operator=(). Аналогичная ситуация с инициализацией.

    Если присваивание и конструктор копирования описаны неявно, то они будут общими функциями-членами и операция присваивания для класса X определяется таким образом, что ее результатом является ссылка типа X& на объект, которому присваивают.

    Если в классе X есть функция X::operator=(), параметром которой является сам класс X, то стандартное присваивание не будет порождаться. Если в классе определен какой-либо конструктор копирования, то стандартный конструктор копирования не будет порождаться. Приведем пример:

    class X {

     //…

    public:

     X(int);

     X(const X&, int = 1);

    };


    X a(1); // вызов X(int)

    X b(a,0); // вызов X(const X&,int)

    X c = b; // вызов X(const X&,int)

    Присваивание объектов класса X определяется через функцию X::operator=(const X&). Это означает (§R.12.3), что объекты производного класса можно присваивать объектам общего базового класса, например:

    class X {

    public:

     int b;

    };


    class Y: public X {

    public:

     int c;

    };


    void f()

    {

     X x1;

     Y y1;

     x1 = y1; // нормально

     y1 = x1; // ошибка

    }

    В этом примере y1.b присваивается x1.b, а x1.c не копируется.

    Копирование одного объекта в другой с помощью стандартной операции копирования или стандартного конструктора копирования не изменяет структуру обоих объектов. Приведем пример:

    struct s {

     virtual f();

     //…

    };


    struct ss: public s {

     f();

     //…

    };


    void f()

    {

     s a;

     ss b;

     a = b; // на самом деле выполняется a.s::operator=(b)

     b = a; // ошибка

     a.f(); // вызов s::f

     b.f(); // вызов ss::f

     (s&)b = a; // присваивание a b

      // на самом деле выполняется ((s&)b).s::operator=(a)

     b.f(); // все еще вызов ss::f

    }

    Вызов a.f() приведет к вызову s::f() (как и должно быть для объекта класса s (§R.10.2)), а вызов b.f() приведет к вызову ss::f() (как и должно быть для объекта класса ss).








    Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх