Объявите указатель типа базового класса, но затем создайте его экземпляр, указав на дочерний класс.Это хорошая практика программирования? - PullRequest
0 голосов
/ 12 апреля 2019

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

Он объявляет class A и class B, которые наследуются от A.

Но для создания экземпляра объекта B он делает так:

A* b = new B;

и он работает нормально (и он продолжает использовать этот метод при создании более сложных классов, векторного массива базового типа Shape, который содержит дочерние классы Square, Circle и т. Д., Т. Е.

std::vector<Shape*> shapes;
shapes.push_back(new Circle.........
shapes.push_back(new Rectangle.......

Возможно, но вполне нормально (и даже рекомендуется) объявить указатель на объект базового класса, но затем создать его экземпляр, указав на его дочерний класс ??

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

Спасибо!

Ответы [ 2 ]

1 голос
/ 12 апреля 2019

Вы частично ответили на свой вопрос, но могут быть и другие причины.

Полиморфизм таков, что вы можете держать некоторые вещи абстрактными. Представьте себе, например, такой код:

Person *actor;

if (occupation == 'nerd') {
    actor = new NerdPerson();
}
else if (occupation == 'mortician') {
    actor = new MorticianPerson();
}
...

actor->printOccupation();

Очевидно, что это выдуманная причина. Так что это МОЖЕТ быть о коллекциях чего-то, но это только одна причина.

1 голос
/ 12 апреля 2019

но вполне ли (и даже рекомендуется) объявить указатель на объект базового класса, но затем создать его экземпляр, указав на его дочерний класс?

Когда вы используете

A* ptr = new B();

вместо

B* ptr = new B();

Вы теряете только небольшое количество информации. Вы не можете использовать функции-члены, которые определены в B, но не в A. Обратите внимание, что совершенно нормально использовать virtual функции-члены, объявленные в A, но реализованные в B.

Если эта небольшая сумма убытка в порядке в вашем случае использования, то использование A* ptr = не повредит. Если вы хотите иметь возможность сохранить B -очность указателя, чтобы вы могли использовать его для доступа к элементам, относящимся к B, необходимо будет использовать B* ptr =.


Существуют руководящие принципы использования указателей / ссылочных типов:

  1. При определении интерфейса функции используйте тип указателя / референа, который находится выше в иерархии классов, чем вы можете избежать при реализации функции.

    С учетом

    struct Shape { ... };
    struct Circle : Shape { ... };
    

    не используйте Circle в интерфейсе функции, если все, что вам нужно в функции, может быть получено из Shape.

    При добавлении следующего подкласса Shape,

    struct Rectangle : Shape { ... };
    

    эта функция также может использоваться с Rectangle без каких-либо изменений.

  2. При определении указателя / ссылки используйте тип, который сохраняет как можно больше информации.

    Если есть функция

    Circle* someFunction();
    

    затем используйте

    Circle* circlePtr = someFunction();
    

    вместо

    Shape* shapePtr = someFunction();
    

    Вы можете использовать circlePtr там, где вам нужно Shape*, но вы не сможете использовать shapePtr там, где вам нужно Circle*.

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