2011-09-24 2 views
36

Немного о создании объектов. Скажем, у меня есть эти два класса:Порядок вызова конструкторов/деструкторов в наследовании

struct A{ 
    A(){cout << "A() C-tor" << endl;} 
    ~A(){cout << "~A() D-tor" << endl;} 
}; 

struct B : public A{ 
    B(){cout << "B() C-tor" << endl;} 
    ~B(){cout << "~B() D-tor" << endl;} 

    A a; 
}; 

и в основном я создаю экземпляр B:

int main(){ 
    B b; 
} 

Обратите внимание, что B производные от A, а также имеет поле типа A.

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

Что относительно полей (A a; в данном случае)? Когда создается B, когда он вызывает конструктор A? Я не определил список инициализации, есть ли какой-то список по умолчанию? А если нет списка по умолчанию? И тот же вопрос о деструкции.

+3

Ваш пример может быть более объяснительным, если ваше сообщение для деструктора отличается от вашего сообщения для конструктора. Кроме того, что такое 'std :: sort'? – Tom

+0

Кроме того, при экспериментировании сравните конструкцию и разрушение 'B b',' B * b = new B(); delete b; 'и' A * a = new b(); delete a; '(Сравните, что происходит, когда вы используете ключевое слово' virtual' для вашего деструктора, то есть 'virtual ~ A() {cout <<" D-tor "<< endl;}') – Tom

+0

@Tom, вы правильно. Удаление ошибок компилятора. – iammilind

ответ

67
  • Строительство всегда начинается с основания class. Если есть несколько базовых class es, то строительство начинается с самой левой базы. (сторона примечание: Если есть наследование virtual, тогда оно получает более высокое предпочтение).
  • Затем поля членов построены. Они инициализируются в порядке они объявлены
  • Наконец, сама class построена
  • Порядок деструктора является обратной

Независимо от списка инициализации, порядок вызова будет как это: '

  • class B конструктору' s поле

    1. База class A имени a (типа class A) будет построен
    2. производный class B «s конструктор
  • +2

    'В конце концов сам класс построен' - вы говорите здесь о кузове конструктора? – Wolf

    +1

    @ Волк: Думаю, да. – Ludwik

    +0

    @ Вольф. Он относится к производному классу – MSD561

    7

    Базовые классы всегда строятся перед членами данных. Элементы данных создаются в том порядке, в котором они объявлены в классе. Этот порядок не имеет ничего общего с списком инициализации. Когда элемент данных инициализируется, он будет просматривать список инициализации для параметров и вызывать конструктор по умолчанию, если нет совпадения. Деструкторы для членов данных всегда вызывают в обратном порядке.

    +1

    +1 Краткий, но хороший. Возможно, какой-то типографский сахар привлечет больше читателей. – Wolf

    20

    Если предположить, что это не виртуальные/множественное наследование (что усложняет вещи совсем немного), то правила просты:

    1. Память объект выделяется
    2. Конструктор базовых классов выполняются, заканчивая наиболее полученного
    3. инициализация элемента выполнена
    4. объект становится настоящим экземпляром этого класса
    5. код
    6. конструктора выполняется

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

    Следует также отметить, что даже если во время выполнения конструктора коды this объекта уже получил свой окончательный класс (например, в отношении виртуальной отправки) деструктора класса не будет называться, если конструктор не завершит выполнение , Только когда конструктор завершает выполнение, экземпляр объекта является реальным гражданином первого класса среди экземпляров ... до этого момента это всего лишь «желаемый экземпляр» (несмотря на наличие правильного класса).

    Уничтожение происходит в точном обратном порядке: сначала выполняется деструктор объекта, затем он теряет свой класс (т. Е. Из этой точки на объекте считается базовым объектом), тогда все члены уничтожаются в обратном порядке объявления и, наконец, процесс разрушения базового класса выполняется до самого абстрактного родителя. Что касается конструктора, если вы вызываете любую виртуальную функцию-член объекта (прямо или косвенно) в деструкторе базы или члена, исполняемая реализация будет родительской, потому что объект потерял название класса при завершении деструктора класса.

    +0

    'начиная с самого абстракта 'в точке 2: это правда? – Wolf

    +0

    @ Вольф: Да, это гарантировано. Если у вас есть класс 'D', который является производным от' B', тогда при создании экземпляра 'D' сначала выполняется конструктор' B' (самый абстрактный), а затем выполняется конструктор 'D'. Фактически, объект становится реальным «D» (по отношению к виртуальным методам) только после того, как было завершено построение всех баз и всех остальных членов (это сложная задача о вызове виртуальных методов 'D' при построении одного из членов или во время построения базового под-объекта). – 6502

    +0

    Это не заказ на строительство от оснований к тому, что я просил, это слово «абстрактное», которое, кажется, вводит в заблуждение. Как я узнал, абстрактным классом является класс, по крайней мере, по чистому виртуальному методу. Пожалуйста, посмотрите [этот пример] (http://coliru.stacked-crooked.com/a/2f5e712f8421d303) – Wolf

    0

    Выход из модифицированного кода:

    A() C-tor 
    A() C-tor 
    B() C-tor 
    ~B() D-tor 
    ~A() D-tor 
    ~A() D-tor 
    
    3

    конструктор базового класса всегда выполняет first.so, когда вы пишете заявление B b; конструктор A называется первой и затем B класса constructor.therefore выход из конструкторы будет в следующей последовательности:

    A() C-tor 
    A() C-tor 
    B() C-tor 
    
    1
    #include<iostream> 
    
    class A 
    { 
        public: 
        A(int n=2): m_i(n) 
        { 
        // std::cout<<"Base Constructed with m_i "<<m_i<<std::endl; 
        } 
        ~A() 
        { 
        // std::cout<<"Base Destructed with m_i"<<m_i<<std::endl; 
        std::cout<<m_i; 
        } 
    
        protected: 
        int m_i; 
    }; 
    
    class B: public A 
    { 
        public: 
        B(int n): m_a1(m_i + 1), m_a2(n) 
        { 
        //std::cout<<"Derived Constructed with m_i "<<m_i<<std::endl; 
        } 
    
        ~B() 
        { 
        // std::cout<<"Derived Destructed with m_i"<<m_i<<std::endl; 
        std::cout<<m_i;//2 
        --m_i; 
        } 
    
        private: 
        A m_a1;//3 
        A m_a2;//5 
    }; 
    
    int main() 
    { 
        { B b(5);} 
        std::cout <<std::endl; 
        return 0; 
    } 
    

    ответ в данном случае 2531. Как constructo г называются здесь:

    1. B :: A (Int N = 2) конструктор вызывается
    2. B :: B (5) конструктор вызывается
    3. Б.m_A1 :: А (3), называется
    4. B.m_A2 :: А (5) называется

    Тот же сторону деструктор называется:

    1. B :: ~ B() называется. i.e m_i = 2, который уменьшает m_i до 1 в A.
    2. B.m_A2 :: ~ A(). m_i = 5
    3. B.m_A1 :: ~ A() вызывается. m_i = 3 4 B :: ~ A() называется., m_i = 1

    В этом примере, строительство m_A1 & m_A2 не имеет никакого значения порядка порядка списка инициализации, но их порядок декларации.

     Смежные вопросы

    • Нет связанных вопросов^_^