Объявление указателя на базовый и производный классы - PullRequest
4 голосов
/ 12 января 2010

Я только что обнаружил, что меня смущает один базовый вопрос в C ++

class Base {

};

class Derived : public Base {

}

Base *ptr = new Derived(); 

Что это значит? PTR указывает на базовый класс или производный класс? В этой строке, сколько памяти выделено для ptr? в зависимости от размера производного или базового?

В чем разница между этим и следующим:

Base *ptr = new Base();
Derived *ptr = new Derived();

Есть ли такой случай?

Derived *ptr = new Base();

Спасибо!

Ответы [ 7 ]

13 голосов
/ 12 января 2010

Для Base *ptr = new Derived(); память выделяется в соответствии с классом Derived. ptr указывает на объект, но компилятору поручено только «предоставить доступ» (видимость) членам объекта, которые объявлены в классе Base.

Конечно, память, связанная с указателем ptr, является той же самой, то есть независимо от объекта, на который она указана. Обычно размер "объекта указателя" постоянен в архитектуре ЦП, например 32 бита / 64 бита (или меньше на встроенных устройствах, например).

Для Derived *ptr = new Base();: нет, это недопустимо.

Класс Derived - это не просто класс Base, а определяется как , получая из Base: следовательно, экземпляр указателя на экземпляр Derived объекта нельзя просто назначить к экземпляру объекта класса Base.


Вы могли бы рассмотреть возможность использования очень хороших статей Википедии о полиморфизме и Наследование .

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

В 32-битной системе 4 байта стекового пространства выделяются для ptr. В 64-битной системе это будет 8 байт. (Предполагая, что компилятор не решит оставить его в регистре и вообще не выделять пространство стека).

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

Ваша последняя строка (Derived *ptr = new Base();) недействительна, поскольку Base - это , а не a Derived.

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

Полиморфизм

ptr - указатель; он имеет одинаковый размер независимо от того, на что он указывает.

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

Чтобы понять систему типов C ++, важно понимать разницу между статическими типами и динамическими типами. В вашем примере вы определили типы Base и Derived и переменную ptr, которая имеет тип static Base *.

Теперь, когда вы звоните new Derived(), вы получаете указатель с статическим и динамическим типом Производный *. Поскольку Derived является подтипом Base , его можно неявно преобразовать в static тип Base * и присвоить ptr как типы static теперь совпадают. Однако тип dynamic остается Derived *, что очень важно, если вы вызываете любую виртуальную функцию Base через ptr, поскольку вызов виртуальных функций всегда основан на для объекта типа dynamic , а не типа static .

1 голос
/ 12 января 2010

Ваш вопрос касается одной из самых важных частей объектно-ориентированного программирования: полиморфизм .

Derived является подтипом Base. Это означает, что все, что Base может сделать, Derived также может сделать. Часто Derived является более конкретным, чем Base: он работает на подмножестве того, что делает Base, но делает это подмножество намного лучше, чем то, что делает Base.

Подумайте над примером.

Подумайте о написании графической программы. Возможно, у вас есть класс ClosedShape и метод внутри него fill(). Можно создать очень общий метод, который может заполнить любую замкнутую фигуру ... но обычно этот метод занимает память, и он может быть медленным.

Возможно, у вас есть другой класс, Square. Теперь заполнение квадратов очень просто и очень быстро: это два вложенных цикла. Поскольку Square делает все, что делает ClosedShape, он может наследовать от ClosedShape.

Почему важен полиморфизм? Подумайте о многих видах ClosedShape: треугольниках, шестиугольниках и так далее. Если вы хотите заполнить все из них, просто сделайте:

for (i=0; i<num; i++) {
    cs[i].fill();
}

Все они будут использовать свою собственную версию fill()!

0 голосов
/ 07 июля 2016

A Derived объект добавляется в конец объекта Base, поэтому префикс [в битах] Derived на самом деле Base, поэтому нет проблем при назначении объекта Derived* в Base* переменную. Доступ к любому из полей / методов Base объекта Derived совершенно безопасен.

Однако - опозит не соответствует действительности. Если вы назначите адрес Base* переменной Derived*, а затем получите доступ к одному из полей Derived [которого нет в Base], вы освободитесь из выделенного пространства.

Печатая рассуждения:
Также обратите внимание, что значение может быть присвоено переменной только в том случае, если она имеет правильный тип без приведения [c ++ - статическая типизация langauge]. Поскольку Derived - это Base, Derived* - это Base*, так что это не противоречит, но обратное неверно.

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

0 голосов
/ 07 июля 2016

для базы * ptr = new Derived (); память распределяется в соответствии с производным классом. ptr указывает на объект, но компилятору поручено только «предоставить доступ» (видимость) членам объекта, которые объявлены в базовом классе.

Конечно, память, связанная с указателем ptr, одинакова, т.е. не зависит от объекта, на который она указана. Обычно размер "объекта указателя" постоянен в архитектуре ЦП, например 32 бита / 64 бита (или меньше на встроенных устройствах, например).

Для производных * ptr = new Base () ;: нет, это недопустимо.

Класс Derived - это не просто класс Base, а определяется как производный от Base: следовательно, экземпляр указателя на экземпляр объекта Derived нельзя просто назначить экземпляру объекта класса Base.

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