c ++ виртуальные функции - PullRequest
4 голосов
/ 31 июля 2010

Просто интересно, что будет быстрее?Если бы у меня был такой базовый класс

Class Base
{
virtual void Draw()
{
//something...
}
};

, тогда у меня был бы такой массив Base:

Base Array[255];

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

Теперь альтернативно, если бы я точно знал, какие производные я мог бы сделать, это могло бысделать это так

class Base
{
int ID;
};

затем массив Base, как раньше: Base Array [255];

И затем создать функции Draw в производных:

class Der1 : Base
{
void Draw()
{
}
};

class Der2 : Base
{
void Draw()
{
}
};

Теперь это решение, конечно, не позволяет мне просто перебирать массив и вызывать Draw для каждого объекта.Вместо этого нужно было бы сделать что-то вроде этого.

void CallDraw()
{
for (int i = 0; i < 255; i++)
{
switch(Array[i].ID)
{
case 1: //Der1
   ( (Der1) Array[i] ) . Draw(); 
case 2: //Der2
   ( (Der2) Array[i] ) . Draw();
}

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

И есть ли что-то еще, что следует принять во внимание?(может быть, чистота кода, но я бы предпочел показать, какой у меня тип класса в коде, поэтому я не обеспокоен приведениями.

Ответы [ 7 ]

4 голосов
/ 31 июля 2010

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

Кроме того, в вашем объявлении Array вам нужно использовать указатели:

Base *Array[255];

В противном случае, вВ C ++ (в отличие от Java) массив может только содержать экземпляры Base и не содержать объектов производных классов.Использование указателя больше похоже на ссылки Java и позволяет помещать экземпляры производного класса в массив.

2 голосов
/ 31 июля 2010

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

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

1 голос
/ 31 июля 2010

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

Виртуальный вызов «медленнее», но более читабелен.На самом деле, вряд ли есть какое-либо преимущество в девиртуализации таких звонков.Если вы пытаетесь оптимизировать свой код, держу пари, что есть лучшие места, на которые стоит обратить внимание.

1 голос
/ 31 июля 2010

Полагаю, вы спрашиваете из любопытства - не потому, что вы на самом деле пытаетесь реализовать его с помощью переключателя.

Вы должны сделать свой массив Base*, а не Base

Виртуальный вызов - это поиск в массиве и вызов функции. Переключатель часто реализуется как простой скачок с постоянным временем. Итак, они оба O (1), но я думаю, что виртуальный вызов будет быстрее, так как компиляторы C ++ будут оптимизированы для такой скорости, и не гарантируется, что переключатель будет O (1).

0 голосов
/ 10 мая 2012

Я предлагаю использовать виртуальную функцию для реализации вашей логики по следующим причинам

(1) Я не знаю, сколько случаев (Реализация случая) вы используете, но если завтра ваш производныйклассы простираются более чем на 50 или даже больше, и в итоге вы будете писать только свои случаи. То, что вы пытаетесь сделать вручную, предоставляя идентификаторы для производных классов, является задачей компилятора, который сам вставляет код инициализации vptr иПолитика VTABLE.best - «Не реализовывать то, что компилятор может реализовать сам».Поэтому в будущем, независимо от того, сколько классов вы извлекаете из базы, ваш код будет лучше, быстрее и более читаемым благодаря способу виртуальной функции.

(2) При использовании виртуальной функции вам нужно всегда всегда сделать функцию виртуальной в базовом классе и получить доступ к функции производного класса (с тем же именем) с помощью upcasting (назначив указатель / ссылку на объект производного классаобъект базового класса). Вы не указали в своей реализации указатель или ссылку на базовый класс, это должен быть Base * Arr [100] или Base & Arr [100]

То, что вы делаете во второмэто разрезание объекта, при котором вы отсекаете базовую часть от объекта.

rgds, Softy

0 голосов
/ 31 июля 2010

Я бы категорически утверждал, что для чистоты кода и удобства сопровождения вы придерживаетесь использования виртуальных функций. Нет никакой реальной причины избегать их. На данный момент они довольно эффективны (языки, подобные Java, используют их по умолчанию для всех нестатических методов). Если у вас действительно был код, который должен был быть безумно эффективным, и вы пытались извлечь из него каждую последнюю инструкцию, то вам нужно было бы написать как версию с виртуальными функциями, так и версию без нее и профилировать ее. Кроме этого, не беспокойтесь об этом. Вы пытаетесь делать то, что делает сам язык, фактически не используя встроенный в язык способ, и это редко хорошая идея. Это может быть даже медленнее - скажет только профилирование. Просто используйте виртуальные функции и не беспокойтесь об этом.

Кроме того, Base Array[255]; не будет работать, потому что все ваши объекты будут срезаны и будут только базовыми объектами, а не производными типами. Вам нужно использовать указатели: Base* Array[255];.

0 голосов
/ 31 июля 2010

Перейти на виртуальные функции.Период.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...