2009-06-30 3 views

ответ

1

От here:

В объектно-ориентированном программировании виртуальная функция или виртуальный метод функция или метод, поведение которого может быть переопределен в пределах наследования класса с помощью функции с тем же подпись.

7

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

4

например. проксирования. т.е. методы перезаписи во время выполнения. Например, NHibernate использует это для поддержки ленивой загрузки.

1

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

Нет проблем - объявите метод виртуальным, добавьте базовую реализацию в класс Params и переопределите его в производных классах. Теперь вы можете просто пересечь массив и вызвать метод через ссылку - будет вызван правильный метод.

class Params { 
public: 
    virtual void Manipulate() { //basic impl here } 
} 

class DerivedParams1 : public Params { 
public: 
    override void Manipulate() { 
     base.Manipulate(); 
     // other statements here 
    } 
}; 

// more derived classes can do the same 

void ManipulateAll(Params[] params) 
{ 
    for(int i = 0; i < params.Length; i++) { 
     params[i].Manipulate(); 
    } 
} 
3

Используется для определения производного класса, что функция может быть переопределена.

MSDN имеет хороший пример here.

3

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

Основной пример:

public class Shape 
{ 
    // A few example members 
    public int X { get; private set; } 
    public int Y { get; private set; } 
    public int Height { get; set; } 
    public int Width { get; set; } 

    // Virtual method 
    public virtual void Draw() 
    { 
     Console.WriteLine("Performing base class drawing tasks"); 
    } 
} 

class Circle : Shape 
{ 
    public override void Draw() 
    { 
     // Code to draw a circle... 
     Console.WriteLine("Drawing a circle"); 
     base.Draw(); 
    } 
} 
class Rectangle : Shape 
{ 
    public override void Draw() 
    { 
     // Code to draw a rectangle... 
     Console.WriteLine("Drawing a rectangle"); 
     base.Draw(); 
    } 
} 
class Triangle : Shape 
{ 
    public override void Draw() 
    { 
     // Code to draw a triangle... 
     Console.WriteLine("Drawing a triangle"); 
     base.Draw(); 
    } 
} 
+2

", а базовый класс вызовет метод производного класса." это довольно запутанное заявление в паре с образцом кода; текст указывает, что base.Draw приведет к вызову метода Draw производных классов, который, как мне кажется, приведет к исключению StackOverflowException. Я понимаю, но новичок может этого не сделать. Пример кода как таковой действительно хорош; бег, он ясно покажет, что произойдет. –

71

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

using System; 
class TestClass 
{ 
    public class Dimensions 
    { 
     public const double pi = Math.PI; 
     protected double x, y; 
     public Dimensions() 
     { 
     } 
     public Dimensions (double x, double y) 
     { 
     this.x = x; 
     this.y = y; 
     } 

     public virtual double Area() 
     { 
     return x*y; 
     } 
    } 

    public class Circle: Dimensions 
    { 
     public Circle(double r): base(r, 0) 
     { 
     } 

     public override double Area() 
     { 
     return pi * x * x; 
     } 
    } 

    class Sphere: Dimensions 
    { 
     public Sphere(double r): base(r, 0) 
     { 
     } 

     public override double Area() 
     { 
     return 4 * pi * x * x; 
     } 
    } 

    class Cylinder: Dimensions 
    { 
     public Cylinder(double r, double h): base(r, h) 
     { 
     } 

     public override double Area() 
     { 
     return 2*pi*x*x + 2*pi*x*y; 
     } 
    } 

    public static void Main() 
    { 
     double r = 3.0, h = 5.0; 
     Dimensions c = new Circle(r); 
     Dimensions s = new Sphere(r); 
     Dimensions l = new Cylinder(r, h); 
     // Display results: 
     Console.WriteLine("Area of Circle = {0:F2}", c.Area()); 
     Console.WriteLine("Area of Sphere = {0:F2}", s.Area()); 
     Console.WriteLine("Area of Cylinder = {0:F2}", l.Area()); 
    } 
} 

Edit: Вопросы в комментарии
Если я не использую виртуальный ключевое слово в базовом классе, он будет работать?

Если вы используете ключевое слово override в своих классах потомков, это не сработает. Вы будете генерировать ошибку компилятора CS0506 'function1': не может переопределить унаследованный элемент 'function2', потому что он не помечен как «виртуальный», «абстрактный» или «переопределенный»

Если вы не используете переопределение, вы получите CS0108 warning 'desc.Method()' скрывает унаследованный элемент 'base.Method()' Используйте новое ключевое слово, если было предназначено скрытие.

Чтобы обойти это, поставьте ключевое слово new перед методом: скрывается.

например.

new public double Area() 
    { 
    return 2*pi*x*x + 2*pi*x*y; 
    } 

..И является обязательным, чтобы переопределить виртуальный метод в производном классе?
Нет, если вы не переопределите метод, класс descendent будет использовать метод, на который он наследует.

+3

Что делать, если я не использую виртуальное ключевое слово в базовом классе? это будет работать? и является ли он обязательным переопределять виртуальный метод в классе, который выучил? – Preeti

+4

@ Pre Я ответил на ваши вопросы в комментариях –

+1

спасибо John.now это ясно. – Preeti

3

Это позволяет достичь позднего связывания, что означает определение во время выполнения, а не во время компиляции, когда будет вызван элемент объекта. См. Wikipedia.

55

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

.: например

class Animal { 
    public void eat() {...} 
} 

class FlyingAnimal : Animal { 
    public void eat() {...} 
} 

Animal a = new FlyingAnimal(); 

Animal класс имеет функцию eat(), которая обычно описывает, как следует есть животное (например, поместить объект в рот и проглотить).

Однако класс FlyingAnimal должен определить новый метод eat(), поскольку у летающих животных есть определенный способ съесть.

Таким образом, вопрос, который приходит на ум, состоит в следующем: после того, как я объявил переменную a типа Animal и asigned это новый объект типа FlyingAnimal, что a.eat() будет делать? Какой из двух методов называется?

Ответ здесь: потому что a имеет тип Animal, он назовет метод Animal. Компилятор немой и не знает, что вы собираетесь назначить объект другого класса переменной a.

Здесь приводится ключевое слово virtual: если вы объявляете метод как virtual void eat() {...}, вы в основном говорите компилятору «будьте осторожны, что я делаю здесь некоторые умные вещи, с которыми вы не можете справиться, потому что вы не так умны ». Поэтому компилятор не будет пытаться связать вызов a.eat() с любым из двух методов, но вместо этого он сообщает системе сделать это во время выполнения!

Таким образом, только когда код выполняется, то система будет выглядеть на a «s тип контента не на его заявленного типа и выполняет FlyingAnimal» метод s.

Вы можете удивиться: почему, черт возьми, я хочу это сделать? Почему бы не сказать с самого начала FlyingAnimal a = new FlyingAnimal()?

Причиной этого является то, что, например, вы можете иметь много производных классов от Animal: FlyingAnimal, SwimmingAnimal, BigAnimal, WhiteDog и т.д.И в какой-то момент вы хотите определить мир, содержащий много Animal с, так что вы говорите:

Animal[] happy_friends = new Animal[100]; 

У нас есть мир с 100 счастливых животных. Вы инициализировать их в какой-то момент:

... 
happy_friends[2] = new AngryFish(); 
... 
happy_friends[10] = new LoudSnake(); 
... 

И в конце концов, вы хотите, чтобы все, чтобы поесть, прежде чем они идут спать. Поэтому вы хотите сказать:

for (int i=0; i<100; i++) { 
    happy_friends[i].eat(); 
} 

Так как вы можете видеть, у каждого животного есть свой метод еды. Только с помощью виртуальных функций вы можете достичь этой функции. В противном случае каждый будет вынужден «съесть» точно таким же образом: как описано в самой общей функции eat внутри класса Animal.

EDIT: Данное поведение является фактически по умолчанию общедоступными языками высокого уровня, такими как Java.

+6

Так почему бы не превратить Animal в интерфейс? Потому что функциональность базы животных, которую могут использовать все Животные? – Djorge

+1

@Djorge Точно. –

+1

спасибо очень хорошее объяснение. Лучший ответ! :) – wenn32

1

Использование виртуальных функций в C#

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

Когда производный класс наследует базовый класс, объект производного класса является ссылкой либо на производный класс, либо на базовый класс.

Виртуальные функции разрешаются поздно компилятором (т.е. время выполнения связывания)

virtual в базовом классе, внедрение в наиболее производного класса функции называется в соответствии с фактическим типом объекта, указанного , независимо от объявленного типа указателя или ссылки. Если это не virtual, метод разрешен early, и вызываемая функция выбирается в соответствии с объявленным типом указателя или ссылки.

Смежные вопросы