Какой полиморфный тип в С++?

В одной статье я обнаружил, что "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

Я думаю, что мы всегда определяем/объявляем деструктор любого класса как виртуального, особенно в дереве наследования. Тогда мы можем сказать, что почти все классы в дереве наследования являются полиморфными.