Какой полиморфный тип в С++?
В одной статье я обнаружил, что "static_cast используется для отливки не полиморфного типа, а dynamic_cast используется для литья полиморфного типа". Я понимаю, что int и double не являются полиморфными типами.
Однако я также обнаружил, что static_cast может использоваться между базовым классом и производным классом. Что означает полиморфный тип? Некоторые люди говорят, что полиморфный тип означает базовый класс с виртуальной функцией. Это правильно? Это единственная ситуация? Что еще? Может ли кто-нибудь это уточнить для меня?
Ответы
Ответ 1
Прежде всего, статья не совсем корректна. dynamic_cast проверяет тип объекта и может терпеть неудачу, static_cast не проверяет и в значительной степени требует, чтобы программист знал, что он делает (хотя он будет выдавать ошибки компиляции для некоторых вопиющих ошибок), но они могут использоваться как в полиморфные ситуации. (dynamic_cast имеет дополнительное требование, чтобы по крайней мере один из задействованных типов имел виртуальный метод.)
Полиморфизм в С++, в двух словах, использует объекты через отдельно определенный интерфейс. Этот интерфейс является базовым классом, и почти всегда это полезно только тогда, когда он имеет виртуальные методы.
Однако это редко, но возможно иметь полиморфизм без каких-либо виртуальных методов; часто это признак либо плохого дизайна, либо того, чтобы соответствовать внешним требованиям, и из-за этого нет способа дать хороший пример, который подойдет здесь. ( "Вы узнаете, когда использовать его, когда увидите его", - это, к сожалению, лучший совет, который я могу вам дать здесь.)
Пример полиморфизма:
struct Animal {
virtual ~Animal() {}
virtual void speak() = 0;
};
struct Cat : Animal {
virtual void speak() { std::cout << "meow\n"; }
};
struct Dog : Animal {
virtual void speak() { std::cout << "wouf\n"; }
};
struct Programmer : Animal {
virtual void speak() {
std::clog << "I refuse to participate in this trite example.\n";
}
};
Упражнение вышеприведенных классов немного, также см. мой общий factory пример:
std::auto_ptr<Animal> new_animal(std::string const& name) {
if (name == "cat") return std::auto_ptr<Animal>(new Cat());
if (name == "dog") return std::auto_ptr<Animal>(new Dog());
if (name == "human") return std::auto_ptr<Animal>(new Programmer());
throw std::logic_error("unknown animal type");
}
int main(int argc, char** argv) try {
std::auto_ptr<Animal> p = new_animal(argc > 1 ? argv[1] : "human");
p->speak();
return 0;
}
catch (std::exception& e) {
std::clog << "error: " << e.what() << std::endl;
return 1;
}
Также возможно использовать полиморфизм без наследования, поскольку это действительно метод дизайна или стиль. (Я отказываюсь использовать шаблон модного слова здесь: P)
Ответ 2
static_cast может выполнять преобразования между указателями на связанные классы, а не только от производного класса к его базе, но также от базового класса до его производного. Это гарантирует, что по крайней мере классы совместимы, если соответствующий объект преобразован, , но во время выполнения не выполняется проверка безопасности, чтобы проверить, является ли преобразованный объект фактически полным объектом целевого типа. Поэтому программист должен убедиться, что преобразование безопасно. С другой стороны, избегаются накладные расходы на проверку безопасности типа dynamic_cast.
static_cast также может использоваться для выполнения любого другого преобразования без указателей, которое также может выполняться неявно, например, стандартное преобразование между основными типами:
double d=3.14159265;
int i = static_cast<int>(d);
dynamic_cast может использоваться только с указателями и ссылками на объекты. Его цель - убедиться, что результат преобразования типа является допустимым полным объектом запрошенного класса.
Следовательно, dynamic_cast всегда бывает успешным, когда мы бросаем класс в один из его базовых классов.
// dynamic_cast
#include <iostream>
#include <exception>
using namespace std;
class CBase { virtual void dummy() {} };
class CDerived: public CBase { int a; };
int main () {
try {
CBase * pba = new CDerived;
CBase * pbb = new CBase;
CDerived * pd;
pd = dynamic_cast<CDerived*>(pba);
if (pd==0) cout << "Null pointer on first type-cast" << endl;
pd = dynamic_cast<CDerived*>(pbb);
if (pd==0) cout << "Null pointer on second type-cast" << endl;
} catch (exception& e) {cout << "Exception: " << e.what();}
return 0;
}
Замечание о совместимости: dynamic_cast требует, чтобы информация о типе времени выполнения (RTTI) отслеживала динамические типы. Некоторые компиляторы поддерживают эту функцию как параметр, который по умолчанию отключен. Это должно быть включено для проверки типа времени выполнения с помощью dynamic_cast для правильной работы.
Виртуальные функции отвечают за полиморфизм времени выполнения в С++. Класс, имеющий хотя бы одну виртуальную функцию, имеет полиморфный тип.
Подробнее....
Прочитайте этот. Было ясно написано, что A class that declares or inherits a virtual function is called a polymorphic class.
Ответ 3
Ну, ответ прост. Класс, имеющий хотя бы одну виртуальную функцию, называется полиморфным типом. Это может быть только деструктор.
Итак, это "полиморфный тип".
struct Test {
virtual ~Test();
};
Ответ 4
Я думаю, что полная фраза - это "полиморфное литье". Вы правы, что static_cast работает с типами, не связанными с наследованием (double-int и т.д.), А остальные ответы указывают на то, как работают броски.
Я не думаю, что это утверждение подразумевало существование мифического полиморфного типа - просто, что static_cast также работает с несвязанными типами. Заявление было немного запутанным, хотя и хорошо прояснить.
Ответ 5
Я думаю, что мы всегда определяем/объявляем деструктор любого класса как виртуального, особенно в дереве наследования. Тогда мы можем сказать, что почти все классы в дереве наследования являются полиморфными.