2010-09-14 2 views
1

Я хотел бы знать, как C++ обеспечивает концепцию макета в памяти этих классов для поддержки наследования.Как C++ обеспечивает концепцию базового класса и производного класса

, например:

class Base1 
{ 

    public: 
     void function1(){cout<<"Base1"}; 

}; 

class Base2 
{ 

    public: 
     void function2(){cout<<"Base2"}; 

}; 

class MDerived: Base1,Base2 
{ 

    public: 
     void function1(){cout<<"MDerived"}; 

}; 

void function(Base1 *b1) 
{ 

    b1->function1(); 
} 

Поэтому, когда я прохожу функционировать объект производного типа функция должна смещение в функции класса Base1 и назвать его. Как C++ обеспечивает такой макет.

+0

Ваш метод «MDerived :: function1» печатал «Base1», что, по-вашему, не так. Я отредактировал ваш пост. –

+0

Может ли кто-нибудь заложить макет памяти, который должен генерировать компилятор. и как производный объект отбрасывается в базовый класс. Потому что в производной памяти есть два базовых класса, как компилятор знает о том, какое смещение выбрать. – mousey

+1

Стандарт не определяет макет памяти, это осталось до исполнителей. Строго говоря, каждый ответ в этом вопросе говорит о специфике платформы, которую некоторые компиляторы используют для реализации виртуальных функций. –

ответ

0

Когда MDerived* необходимо преобразовать в Base1*, компилятор отрегулирует указатель так, чтобы он указывал на правильный адрес памяти, в котором расположены члены этого базового класса. Это означает, что MDerived*, который передается в Base1*, может указывать на другой адрес памяти, чем исходный MDerived* (в зависимости от расположения памяти производного класса).

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

Например, это может печатать разные адреса:

int main() { 
    MDerived *d = new MDerived; 
    std::cout << "derived: " << d << std::endl; 
    std::cout << "base1: " << (base1*)d << std::endl; 
    std::cout << "base2: " << (base2*)d << std::endl; 
} 

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

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

0

Макет класса в памяти включает его элементы и вспомогательные объекты базового класса (§10/2). Члены также являются подобъектами. Указатель на базовый подобъект является указателем на объект (но не на самый производный объект).

При преобразовании MDerived * в Base2 *, компилятор смотрит смещение Base2 объекта внутри MDerived объекта и использует его для создания this для унаследованного метода.

+0

как компилятор знает о смещении. Он где-то хранится? – mousey

+0

Это часть определения структуры. Он запоминается компилятором, но не сохраняется непосредственно в скомпилированной программе. Вы могли бы сказать, что это «хранится» в инструкции ADD, которая генерирует базовый указатель из производного указателя. – Potatoswatter

0

I думаю вы спрашиваете, почему, когда вы звоните b1->function(), делает Base1::function1() пожара?

Если да, то причина в том, что b1 является указателем Base1, а не указателем MDerived. Объект, на который он указывает, на самом деле может быть «be» MDerived, но function(Base1*) не знает этого, поэтому он называет единственное, что он знает - Base1::function1().

Теперь, если вы ознаменовал функцию базового класса, как virtual, все меняется:

#include <iostream> 
#include <string> 
using namespace std; 

class Base1 
{ 
public: virtual void function1() { cout<<"Base1"; } 
}; 

class Base2 
{ 
public: void function2(){cout<<"Base2";} 
}; 

class MDerived: public Base1, public Base2 
{ 
public: void function1(){cout<<"MDerived";} 
}; 

void function(Base1 *b1) 
{ 
    b1->function1(); 
} 

int main() 
{ 
    MDerived d; 
    function(&d); 
} 

Выход программы:

"MDerived"

void function(Base1 *b1) до сих пор не что объект, на который указывает, на самом деле равен MDerived, но теперь этого не нужно. Если вы вызываете virtual функции через указатель базового класса, вы получаете поведение polymorphic. Что в этом случае означает MDerived::function1(), потому что это самый производный тип.

0

Мало что в вашем коде

  1. Множественное наследование должно быть публичным, иначе компилятор будет жаловаться, что нет доступа к базовым классам
  2. Нет ; в конце заявления COUT
  3. не включать <iostream> (ок может быть я слишком педантичный)

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

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