Какой самый краткий, но точный способ описать, что такое виртуальная функция в C ++? - PullRequest
6 голосов
/ 20 марта 2011

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

Если я проконсультируюсь с Википедией, я увижу определение виртуальной функции:

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

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

Если меня попросят описать, что такое виртуальная функция, неофициально, я скажу что-то о указателях, таких как «это метод, такой, что когда вы вызываете его через указатель базового класса, вызывается версия, определенная в производном классе». вместо этого, если указатель фактически указывает на экземпляр производного класса ". Это не похоже на очень элегантное описание концепции. Я знаю, что люди говорят, что именно так "полиморфизм" достигается в C ++ (насколько я понимаю, полиморфизм - это, примерно, вся идея организации объектов в иерархии), но я не знаю более причудливого способа понять или объяснить механизм, чем пройти через пример с указателями.

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

Ответы [ 11 ]

6 голосов
/ 20 марта 2011

Я всегда думал, что эта цитата отражает суть виртуальных функций:

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

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

Основная причина, по которой мне нравитсяэто утверждение заключается в том, что оно описывает виртуальные функции таким образом, чтобы отделить их от программирования.Вы можете объяснить виртуальные функции нетехническому человеку, используя это определение, приведя некоторые конкретные аналогии.Например, идею «включения света» можно представить как виртуальную функцию, потому что фактическая механика того, что происходит при включении света, полностью зависит от конкретного источника света, который вы используете (лампа накаливания? Флуоресцентный светодиод?), но концептуальная идея одинакова в каждом конкретном случае.Конечно, это не идеальная аналогия, но я думаю, что она достаточно хорошо отражает суть.

В общем, ИМХО, если вас когда-либо просят описать что-то неформально, постарайтесь как можно дальше дистанцироватьсяот конкретного языка программирования, который вы используете, и, если возможно, от компьютеров вообще.Попытайтесь придумать наиболее общие условия, в которых применяется эта концепция, а затем опишите ее на этом уровне.С другой стороны, я преподаю вводные курсы CS, и поэтому у меня есть некоторый уклон в сторону этой области, поэтому я понятия не имею, насколько это применимо в условиях собеседования.: -)

2 голосов
/ 20 марта 2011

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

Нет, это неверно. В этом случае функция переопределяется, а не переопределяется.

1 голос
/ 20 марта 2011

Разница заключается в том, как компилятор решает, какую реализацию «привязать» к вызову метода при компиляции исходного кода. Для виртуального слияния выбранная реализация основана на конкретном типе фактического самого объекта, а не на типе переменной . Это означает, что когда вы приводите объект к базовому классу или интерфейсу, реализация, которая будет выполняться, все равно будет реализацией, определенной для производного класса, которым на самом деле является объект.

В коде puesdo

// for a virtual function
public class Animal { public virtual void Move() { Print "An animal Moved." } }
public class Dog: Animal  { public void Move() { Print "A Dog Moved." } }

Animal x = new Dog();
x.Move()  // this will print "A Dog Moved."

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

// for a non-virtual function
public class Animal { public void Move() { Print "An animal Moved." } }
public class Dog: Animal  { public void Move() { Print "A Dog Moved." } }

Animal x = new Dog();
x.Move() // this will print "An animal Moved."
1 голос
/ 20 марта 2011

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

Это не случайно. Концепция виртуальных функций работает только для указателей или ссылок.


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

class Polygon
{
    public:
    virtual float area()
    {
        std::cout << "\n No formula in general \n" ;
    }
    virtual ~Polygon();
};

class Square
{
    public:
    float area()
    {
        std::cout << "\n Side*Side \n" ; 
    }
    ~Square();
}

Polygon* obj = new Square ;
obj -> area() ;  // Ok, Square::area() is called. 

Square obj1;
Polygon& temp = obj1 ; // Ok, Square::area() is called

Square obj2;
Polygon temp1 = obj2 ; // Polygon::area() is called because of object slicing.
1 голос
/ 20 марта 2011

Ваше неофициальное определение - хорошее резюме того, что делает виртуальный указатель. Похоже, вы также хотите описать, как это работает, но поскольку стандарт C ++ не определяет, любое описание «как» будет специфичным для конкретной реализации, а не для языка C ++ в целом.

Стандарт C ++ полностью посвящен поведению и почти ничего не говорит о реализации. Есть даже правило «как если бы», которое допускает любую альтернативную реализацию, которая обеспечивает такое же видимое поведение.

0 голосов
/ 19 апреля 2018

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

0 голосов
/ 20 марта 2011

Хотя для использования виртуальных функций в C ++ необходимы указатели, я бы сказал, что указатели не связаны с этой идеей, а объяснения, которые не зависят от указателей, более понятны. Я думаю, что templatetypedef и Charles_Bretana достигли главной идеи.

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

0 голосов
/ 20 марта 2011

Виртуальная функция - это та, в которой вызываемый абонент решает поведение. Не виртуальная функция - это функция, в которой вызывающая сторона решает поведение.

Это достаточно кратко?

0 голосов
/ 20 марта 2011

Идея

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

Чуть раньше:

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

C ++ notes

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

0 голосов
/ 20 марта 2011

Если собеседование касается ваших знаний в C ++, я думаю, что ссылаться на указатель не грех.

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

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

...