Преобразование общего указателя в производный класс с дополнительной функциональностью - это безопасно?

Рассмотрим следующую схему:

class Base { /* ... */ };

class Derived : public Base
{
public:
    void AdditionalFunctionality(int i){ /* ... */ }
};

typedef std::shared_ptr pBase;
typedef std::shared_ptr pDerived;

int main(void)
{
    std::vector v;
    v.push_back(pBase(new Derived()));

    pDerived p1(  std::dynamic_pointer_cast(v[0])  ); /* Copy */
    pDerived p2 = std::dynamic_pointer_cast(v[0]);    /* Assignment */

    p1->AdditionalFunctionality(1);
    p2->AdditionalFunctionality(2);

    /* A */

    return 0;
}

Здесь я расширяю базовый класс производным классом, который добавляет функциональность (метод AdditionalFunctionality ).

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

Хорошо,поэтому в этом коде я также использую контейнер STL для хранения этих указателей, который позволяет мне хранить указатели как на объекты типа Base, так и на объекты типа Derived, не разрезая объекты.

Второй вопрос, это имеет смысл, верно ? На самом деле я избегаю нарезки, используя указатели на объекты базового класса, а не сами объекты базового класса?

Если я «знаю», что определенный указатель относится к производному объекту, я использую std :: dynamic_pointer_cast для приведения интеллектуального указателя.

Третий вопрос: это компилируется без предупреждения и работает, но безопасно ли это? Действительно? Будет ли он нарушать подсчет ссылок общих указателей и не удастся удалить мои объекты или удалить их раньше, чем я ожидаю?

Наконец, я могу выполнить это приведение с помощью конструктора копирования. или через присвоение, как показано для p1 и p2. Есть ли предпочтительный / правильный способ сделать это?

Подобные вопросы:

  • Понижение значения shared_ptr до shared_ptr ? : Это очень близко, однако класс dervied не добавляет дополнительных функций, таких как у меня есть, поэтому я не уверен, что это полностью то же самое. Кроме того, он использует boost :: shared_ptr , где я использую std :: shared_ptr (хотя я понимаю, что boost donated shared_ptr в библиотеке std, поэтому они, вероятно, такие же).

Спасибо за вашу помощь.


Изменить:

Одна из причин, по которой я спрашиваю, состоит в том, что я понимаю, что можно сделать следующее (неправильно):

    /* Intentional Error */
    v.push_back(pBase(new Base()));
    pDerived p3( std::dynamic_pointer_cast(v[1]) );
    p3->AdditionalFunctionality(3); /* Note 1 */

Когда я пытаюсь преобразовать указатель на базовый объект в указатель на производный объект, а затем вызвать метод, который реализован только в производном классе. Другими словами, указанный объект не определяет (или даже не «знает» о методе).

Это не обнаруживается компилятором, но может вызвать segfault в зависимости от как определяется Дополнительная функция .

13
задан Community 23 May 2017 в 11:54
поделиться