Рассмотрим следующий план:
class Base { /* ... */ };
class Derived : public Base
{
public:
void AdditionalFunctionality(int i){ /* ... */ }
};
typedef std::shared_ptr<Base> pBase;
typedef std::shared_ptr<Derived> pDerived;
int main(void)
{
std::vector<pBase> v;
v.push_back(pBase(new Derived()));
pDerived p1( std::dynamic_pointer_cast<Derived>(v[0]) ); /* Copy */
pDerived p2 = std::dynamic_pointer_cast<Derived>(v[0]); /* Assignment */
p1->AdditionalFunctionality(1);
p2->AdditionalFunctionality(2);
/* A */
return 0;
}
Здесь я расширяю базовый класс производным классом, который добавляет функциональность (метод AdditionalFunctionality
).
Первый вопрос, это нормально? Я прочитал много вопросов, в которых говорится, что это не нормально, и вам следует объявить дополнительную функциональность в базовом классе (часто предлагается сделать их чистыми виртуальными методами в базовом классе). Однако я не хочу этого делать. Я хочу расширить функциональность базового класса, а не просто реализовать его по-другому. Есть ли лучшее решение для достижения этой цели?
Хорошо, поэтому в этом коде я также использую контейнер STL для хранения этих указателей, который позволяет мне хранить указатели как на объекты типа Base, так и на объекты типа Derived, не разрезая объекты.
Второй вопрос, это имеет смысл, правда? Фактически, я избегаю нарезки, используя указатели на объекты базового класса, а не сами объекты базового класса?
Если я «знаю», что определенный указатель относится к производному объекту, я использую std::dynamic_pointer_cast
для приведения интеллектуального указателя.
Третий вопрос: это компилируется без предупреждения и работает, но безопасно ли это? Действительный? Будет ли он нарушать подсчет ссылок общих указателей и не сможет delete
мои объекты или delete
их раньше, чем я ожидаю?
Наконец, я могу выполнить это приведение с помощью конструктора копирования или присваивания, как показано для p1 и p2. Есть ли предпочтительный / правильный способ сделать это?
Похожие вопросы:
- Понижение уровня shared_ptr ‹Base› до shared_ptr ‹Derived›?: это очень близко , однако класс dervied не добавляет дополнительных функций, как у меня, поэтому я не уверен, что это полностью то же самое. Кроме того, он использует
boost::shared_ptr
, где я используюstd::shared_ptr
(хотя я понимаю, что boost пожертвовал shared_ptr библиотеке std, поэтому они, вероятно, такие же).
Спасибо за помощь.
Изменить:
Одна из причин, по которой я спрашиваю, заключается в том, что я понимаю, что можно сделать следующее (неправильно):
/* Intentional Error */
v.push_back(pBase(new Base()));
pDerived p3( std::dynamic_pointer_cast<Derived>(v[1]) );
p3->AdditionalFunctionality(3); /* Note 1 */
Когда я пытаюсь преобразовать указатель на объект Base в указатель на объект Derived, а затем вызвать метод, который реализован только в классе Derived. Другими словами, указанный объект не определяет (или даже не «знает» о методе).
Это не обнаруживается компилятором, но может вызвать segfault в зависимости от того, как определено AdditionalFunctionality
.
pDerived p2
не является присваиванием, он по-прежнему копирует (или, по крайней мере, вызывает конструктор копирования, если это то, что вы имеете в виду подcopy
иassignment
) - person Kiril Kirov   schedule 09.06.2011v.push_back(pBase(new Derived()));
компилируется. Фактически, пока я понижаю значение указателей, которые изначально были преобразованы в повышении, программа работает правильно. - person jedwards   schedule 09.06.2011v[1]
, это вызовет ошибку seg, да, посколькуv[1]
end
итератор. Но это могла быть просто опечатка. - person Kiril Kirov   schedule 09.06.2011pDerived
непусты, перед их разыменованием. - person ildjarn   schedule 09.06.2011/* A */
в исходный код. Значит, индекс 1 находится в пределах допустимого диапазона. - person jedwards   schedule 09.06.2011