Помислете за следната схема:
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›? : Това е много близо , но извлеченият клас не добавя допълнителна функционалност като моята, така че не съм сигурен, че е напълно същият. Освен това използва
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 */
Когато се опитвам да намаля указател към основен обект към указател на производен обект и след това извиквам метод, който е внедрен само в производния клас. С други думи, посоченият обект не дефинира (или дори не е "наясно" с метода).
Това не се улавя от компилатора, но може да причини 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 fault, да, тъй катоv[1]
еend
итератор. Но може да е просто печатна грешка. - person Kiril Kirov   schedule 09.06.2011pDerived
не са празни, преди да ги дереферирате. - person ildjarn   schedule 09.06.2011/* A */
в оригиналния код. Така че индексът 1 е в диапазона. - person jedwards   schedule 09.06.2011