Нарезка при вставке указателя производного класса в вектор базового класса

Мой базовый класс называется Account, а производный класс Businessaccount имеет дополнительную переменную int с именем x, а также метод получения (int getx()) для нее. Предполагается ли нарезка в следующей ситуации? Это, очевидно, происходит в моем случае:

vector<shared_ptr<Account>> vec;
shared_ptr<Businessaccount> sptr = make_shared<Businessaccount>();

vec.push_back(sptr);

После этого, если я сделаю это:

(*vec.at(0)).getx();

там написано, что у class<Account> нет участника с именем getx()!

Я был бы признателен, если бы кто-нибудь сказал мне, почему это происходит и как это исправить.


person Infecto    schedule 12.05.2017    source источник
comment
Это не нарезка, но компилятор, конечно, прав в том, что указатель, возвращенный из at(), не может быть напрямую использован для доступа к функциям-членам производных классов.   -  person MikeMB    schedule 12.05.2017
comment
Есть ли у Account член по имени getx()?   -  person David Schwartz    schedule 12.05.2017
comment
Типичным способом вызова метода через указатель является оператор ->. vect.at(0)->getx();.   -  person François Andrieux    schedule 12.05.2017


Ответы (3)


(*vec.at(0)) вернет Account, который не знает о x. Вам нужно преобразовать указатель Account в Businessaccount, чтобы сослаться на этот член.

shared_ptr<BusinessAccount> bA = dynamic_pointer_cast<BusinessAccount>(vec.at(0));
bA->getx();
person lcs    schedule 12.05.2017
comment
@FatihBAKIR dynamic_pointer_cast - person lcs; 12.05.2017

Нет, в этой ситуации нарезки не происходит, ваш указатель просто преобразуется в указатель на базовый класс, т.е. ваши данные те же самые, меняется только тип указателя. Когда происходит нарезка, вы теряете все данные производного класса.

Чтобы решить проблему, вам нужно либо предоставить виртуальный метод в базовом классе, который будет правильно реализован в Businessaccount, либо использовать dynamic_cast или static_cast (если вы уверены, что объект имеет тип Businessaccount по другому поводу). Хотя использование такого приведения обычно является признаком плохо спроектированной программы.

person Slava    schedule 12.05.2017

В C++ статический и динамический тип объекта, на который указывает указатель, могут различаться.

Ваш статический тип того, на что указывают общие указатели в этом векторе, — Account.

Динамический тип того, на что указывают общие указатели в этом векторе, различается. В вашем случае вы помещаете в него Businessaccount.

Когда вы хотите получить доступ к методам или вызвать их, вам предоставляется доступ только к методам статического типа.

Статический тип — это то, что вы доказали компилятору, тип которого содержится в этой строке.

Если вы знаете лучше, вы можете сделать static_cast<Businessaccount*>(vec.at(0).get())->getx(). Делая это, вы обещаете компилятору, что у вас есть определенные сведения о том, что данные в этом месте на самом деле являются Businessaccount. Если вы ошибаетесь, поведение вашей программы не определено (если вам повезет, вы получите сбой).

Вы также можете использовать RTTI (информация о типе времени выполнения), чтобы спросить, является ли конкретный объект определенным подтипом (в некоторых случаях, когда базовый класс имеет виртуальный метод).

Account* paccount = vec.at(0).get();
Businessaccount* pba = dynamic_cast<Businessaccount*>(paccount);
if (pba)
  pba->getx();

приведенный выше проверяет, является ли paccount на самом деле Businessaccount*, и если это так, вызывает для него getx. Если нет, то ничего не делает.

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

В некоторых языках сценариев и компиляции байт-кода они позволяют вам уйти и вызвать getx и перейти к сбою/сгенерировать исключение/и т. д., если этого метода нет.

Вместо этого C++ позволяет вам использовать то, что вы утверждали (через систему типов), а затем позволяет вам написать свой собственный обработчик, если вы хотите динамическую проверку типов.

person Yakk - Adam Nevraumont    schedule 12.05.2017