Подтвердить что ты не робот

Зачем использовать виртуальные функции?

Возможный дубликат:
Может ли кто-нибудь объяснить виртуальные методы С++?

У меня есть вопрос относительно виртуальных функций С++.

Почему и когда мы используем виртуальные функции? Может ли кто-нибудь дать мне реальную реализацию или использование виртуальных функций?

4b9b3361

Ответ 1

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

Классический пример - это когда у вас есть базовый класс Shape и конкретные формы (классы), которые вытекают из него. Каждый конкретный класс переопределяет (реализует виртуальный метод) под названием Draw().

Иерархия классов выглядит следующим образом:

Class hierarchy

Следующий фрагмент показывает использование примера; он создает массив указателей класса Shape, каждый из которых указывает на отдельный объект производного класса. Во время выполнения вызов метода Draw() приводит к вызову метода, переопределенного этим производным классом, и конкретный Shape рисуется (или отображается).

Shape *basep[] = { &line_obj, &tri_obj,
                   &rect_obj, &cir_obj};
for (i = 0; i < NO_PICTURES; i++)
    basep[i] -> Draw ();

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

Вышеприведенный пример хорошего открытого закрытого принципа известного SOLID.

Ответ 2

Подумайте о классе животных, а из него - кошка, собака и корова. Класс животных имеет

virtual void SaySomething()
{
    cout << "Something";
}

функция.

Animal *a;
a = new Dog();
a->SaySomething();

Вместо того, чтобы печатать "Что-то", собака должна сказать "Кора", кошка должна сказать "Мяу". В этом примере вы видите, что a является Собакой, но есть несколько раз, когда у вас есть указатель на животных и не знаю, какое это животное. Вы не хотите знать, какое это животное, вы просто хотите, чтобы животное что-то говорило. Таким образом, вы просто называете виртуальную функцию, и кошки скажут "мяу", и собаки скажут "лай".

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

Ответ 3

Вы используете виртуальные функции, когда вам нужно обрабатывать разные объекты одинаковым образом. Это называется полиморфизмом. Представьте, что у вас есть базовый класс - что-то вроде классической формы:

    class Shape
    {
        public:
           virtual void draw() = 0;
           virtual ~Shape() {}
    };

    class Rectange: public Shape
    {
        public:
            void draw() { // draw rectangle here } 
    };


    class Circle: public Shape
    {
        public:
           void draw() { // draw circle here }
    };

Теперь вы можете иметь вектор разных форм:

    vector<Shape*> shapes;
    shapes.push_back(new Rectangle());
    shapes.push_back(new Circle());

И вы можете рисовать все формы следующим образом:

    for(vector<Shape*>::iterator i = shapes.begin(); i != shapes.end(); i++)
    {
          (*i)->draw();
    }

Таким образом вы рисуете разные фигуры с помощью одного виртуального метода - draw(). Правильная версия метода выбирается в зависимости от времени выполнения информации о типе объекта за указателем.

Примечание Когда вы используете виртуальные функции, вы можете объявить их как pure virtual (например, в классе Shape, просто поместите "= 0" после метода proto). В этом случае вы не сможете создать экземпляр объекта с чистой виртуальной функцией, и он будет называться абстрактным классом.

Также обратите внимание на "виртуальный" перед деструктором. В случае, если вы планируете работать с объектами через указатели на их базовые классы, вы должны объявить деструктор виртуальным, поэтому, когда вы вызываете "delete" для указателя базового класса, будет вызвана вся цепочка деструкторов и утечек памяти не будет.

Ответ 4

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

По сути, это обычно называется "Принципом замещения Лискова", названным в честь Барбары Лисков, который говорил об этом около 1983 года.

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

Это не единственный способ. Существуют всевозможные "обратные вызовы", которые могут принимать "blob" данных, и вы можете иметь таблицы обратных вызовов, зависящие от блока заголовка в данных, которые поступают, например. процессор сообщений. Для этого нет необходимости использовать виртуальную функцию, на самом деле то, что вы, вероятно, используете, - это то, как v-таблица реализуется только с одной записью (например, класс с одной виртуальной функцией).