У меня есть код cpp, где в классе c происходит из класса b, а класс b - из класса a.
Теперь класс b имеет некоторый открытый элемент данных. Поэтому я создаю экземпляр класса c на куче, передавая его указатель на другой класс как указатель на a, и там он понижает указатель на указатель класса b, а затем печатает общедоступные переменные класса b.
Является ли это действительным понижением. Я спрашиваю, потому что только изменение компилятора нарушило этот рабочий код.
Я включаю ниже фрагмент кода, который фиксирует проблему, которую я испытываю.
#include <iostream>
using namespace std;
class grand
{
};
class parent : public grand
{
public : parent(){i=0;}
int i;
parent(int j){ i = j;}
void set(int j){i = j;}
};
class child : public parent{
public: child(){};
};
void print ( grand* ptr)
{
parent *p = (parent*) ptr;
std::cout << std::endl << p->i << std::endl;
}
int main() {
// your code goes here
child c;
c.set(9);
print(&c);
return 0;
}
благодаря
Является ли это действительным понижением.
Да. Ваш бросок внутренне применяет static_cast
, который, согласно §5.2.9/11, даст вам правильный результат. Если аргумент для ptr
не указывает на parent
, результат отбрасывания не определен - и так будет выполняться следующий код.
Downcasting полиморфных типов в C++ работает через dynamic_cast
. Ваш grand
класс выше не является полиморфным - вы должны добавить по крайней мере виртуального деструктора к grand
чтобы сделать его полиморфным. В противном случае вы получите ошибку компилятора со следующим кодом.
parent *p = dynamic_cast<parent*>(ptr); // Once grand is polymorphic...
И проверьте, отличен ли результат p
от нуля. Только этот метод показывает (во время выполнения), работает ли бросок! Все остальные либо вызывают неопределенное поведение, либо неопределенные ненулевые значения.
Некоторые примечания:
print
должен принимать указатель на const
поскольку он не изменяет никаких элементов данных. Вместо того, чтобы использовать C-стиль, вы должны применить dynamic_cast
или хотя бы static_cast
если по какой-либо причине вы компилируете без RTTI (информация о типе времени выполнения).
Сценарий C-стиля совпадает с reinterpret_cast
, который, по сути, интерпретирует память, на которую указывает, как если бы там был создан объект типа parent
. Но каждый компилятор может иметь разную структуру памяти производных классов, поэтому может работать в некоторых обстоятельствах, но нет гарантии.
reinterpret_cast
- только последнее средство.
static_cast
, так как существует допустимый переход от grand
к parent
. Но этого все же следует избегать; он возвращается к reinterpret_cast
, давая ложные результаты времени выполнения, где static_cast
даст полезную ошибку во время компиляции.
Ваш код, как написано, действительно действителен, но есть куча замечаний, которые я хотел бы сделать. Обратите внимание, что он действителен только потому, что объект, который вы передаете для print
является parent
или дополнительным производным классом.
Затем обратите внимание, что, поскольку вы должны отдать его функции print
гораздо безопаснее просто менять подпись функции, чтобы взять parent
вместо grand
и тогда вам не нужно беспокоиться о кастинге.
Затем обратите внимание, что вероятной причиной вашей проблемы является то, что в файле, который выполняет бросок, компилятор не видит отношения между grand
и parent
поэтому ваш стиль C-стиля возвращается к reinterpret_cast
который не является тем, что вы хотите. Если вы не можете изменить подпись print
по крайней мере, измените ее на static_cast
(или, возможно, dynamic_cast
, но вы не можете сделать это в своем примере, потому что классы не являются полиморфными), чтобы компилятор не смог скомпилировать его не вижу отношения класса.
print
чтобы принимать толькоparent*
и позволить компилятору приводить за вас, потому что вы не можете быть уверены, что ваше приведение действительно в противном случае.