Как объекты подклассов расположены в C ++? - PullRequest
3 голосов
/ 14 января 2010

У меня путаница с понятием наследования в C ++. Предположим, у нас есть класс с именем computer, и мы публично наследуем класс с именем laptop от класса computer. Теперь, когда мы создаем объект класса Laptop в главной функции, что происходит в памяти? Пожалуйста, объясните.

Ответы [ 7 ]

11 голосов
/ 14 января 2010
class Computer {
  public:
   Computer() { /* whatever */}
   /* whatever */
};

class Laptop : public Computer {
   public:
    Laptop() { /* whatever */ }
  /* whatever */
};

А потом ...

x = new Laptop();

Что компилятор реализует как:

  1. ptr = ::operator new(sizeof(Laptop))
  2. Computer::Computer(ptr)
  3. Laptop::Laptop(ptr)
  4. x = ptr

Эквивалент стека, который я оставляю читателю в качестве упражнения.

4 голосов
/ 15 января 2010

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

class Base()
{
public:
    virtual void foo();
    virtual void bar();
    void hello();

private:
    int variable1;
};

class Derived : public Base
{
public:
    virtual void foo();
    virtual void bar();
    void bye();

private:
    float variable2;
};

(Примечание для педантов: виртуальный деструктор намеренно опущен для ясности.)

Схема памяти будет выглядеть примерно так:

/*
Base object layout:
[vftable pointer]   (points to the 1st row in the virtual function table)
[int variable1  ]   (from Base)

Derived object layout:
[vftable pointer]   (points to the 2nd row in the virtual function table)
[int variable1  ]   (inherited from Base)
[float variable2]   (from Derived)

virtual function table layout:
[&Base::foo   ] [&Base::bar   ]
[&Derived::foo] [&Derived::bar]
*/

Обратите внимание, что во всей программе имеется только одна таблица виртуальных функций для Base и Derived. Таблица не дублируется для каждого экземпляра Derived. Вместо этого каждый производный экземпляр содержит скрытый указатель на «строку» таблицы виртуальных функций.

Также обратите внимание, что hello () и bye () не отображаются в таблице vf, поскольку они не являются виртуальными. Правильный указатель функции тоже вызов в этом случае можно выяснить во время компиляции.

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

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

Для более опытных программистов другим хорошим упражнением будет попытка реализовать полиморфизм в C с использованием структур и указателей на функции.

4 голосов
/ 14 января 2010

Класс ноутбука содержит атрибуты, определенные как в самом себе, так и в классе компьютера. Производный класс включает в себя базовый класс. Вот почему Страуструп выбрал термин «базовый класс», а не «супер класс» для обозначения класса, унаследованного от. Слово super подразумевает, что наследуемый класс больше, но наоборот, наследующий класс расширяет класс, от которого он наследуется. Компилятор выделяет блок памяти, достаточный для хранения производного класса.

3 голосов
/ 14 января 2010

Я предполагаю, что ноутбук наследует от компьютера, и объясняю, что происходит в целом; детали реализации C ++ (по причинам оптимизации) могут отличаться от этого общего объяснения.

Логически, определение класса Laptop содержит указатель на определение класса Computer. Экземпляр класса Laptop имеет указатель на определение класса Laptop (в C ++, скорее всего, это просто ссылка на массив указателей на функции для методов класса).

Когда объект ноутбука получает сообщение, он сначала ищет в своей собственной таблице методов соответствующую функцию. Если его там нет, он следует за указателем наследования и ищет в таблице методов класс Computer.

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

2 голосов
/ 14 января 2010

Очень простой пример может быть:

class Computer {
    char manufacturer[20];
    char type[10];
}

class Laptop : Computer {
    int runningTime;
}

если вы сейчас создадите объект типа компьютера, будет выделено 20 + 10 = 30 байт памяти. Предполагая, что в вашей системе целое число требует 4 байта, из-за наследования производителя и типа для ноутбука экземпляр ноутбука потребует дополнительных 4 байтов = 34 байта. Адрес, по которому происходит распределение, зависит от текущего состояния кучи. (А на самом деле управление памятью и т. Д.) Новый созданный объект должен быть присвоен ссылочной переменной:

i.g.

Laptop lap = new Laptop();
1 голос
0 голосов
/ 14 января 2010

Память выделяется для размещения размера (объекта). Это включает в себя любые примитивы, такие как int, char и т. Д., Которые могут наследоваться. Может быть в куче / стеке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...