Что такое полиморфный тип в C++?

В одной из статей я обнаружил, что "static_cast используется для приведения неполиморфных типов, а dynamic_cast используется для приведения полиморфных типов". Я понимаю, что int и double не являются полиморфными типами.

Однако я также обнаружил, что static_cast может использоваться между базовым классом и производным классом. Что здесь означает полиморфный тип? Некоторые люди говорят, что полиморфный тип означает базовый класс с виртуальной функцией. Это правильно? Это единственная ситуация? Что-то еще? Кто-нибудь может уточнить это для меня больше?

5 ответов

Решение

Прежде всего, статья не совсем корректна. dynamic_cast проверяет тип объекта и может потерпеть неудачу, static_cast не проверяет и в значительной степени требует, чтобы программист знал, что они делают (хотя это вызовет ошибки компиляции для некоторых вопиющих ошибок), но оба они могут использоваться в полиморфных ситуациях. (dynamic_cast имеет дополнительное требование, чтобы по крайней мере один из задействованных типов имел виртуальный метод.)

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

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

Пример полиморфизма:

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";
  }
};

Небольшое упражнение для вышеперечисленных классов - также посмотрите мой общий пример с фабрикой:

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)

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 для правильной работы.

Виртуальные функции отвечают за полиморфизм во время выполнения в C++. Класс, имеющий хотя бы одну виртуальную функцию, имеет полиморфный тип.

Прочитайте больше....

Прочитайте это тоже. Было ясно написано, что A class that declares or inherits a virtual function is called a polymorphic class.

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

Таким образом, следующее является "полиморфным типом".

struct Test {
  virtual ~Test();
};

Я думаю, что полная фраза "полиморфное приведение типа". Вы правы, что static_cast работает с типами, которые не связаны наследованием (double - int и т. Д.), А ответы других указывают на то, как работает приведение типов.

Я не думаю, что это утверждение подразумевает существование мифического полиморфного типа - просто, что static_cast также работает с несвязанными типами. Это заявление немного сбивало с толку, и это хорошо уточнить.

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

Другие вопросы по тегам