Таблицы виртуальных функций
Давайте начнем с некоторого фона виртуальных таблиц функций и того, как они работают ( source ):
[20.3] В чем разница между виртуальным и не виртуальным
функции-члены называются?
Не виртуальные функции-члены разрешаются статически. Это
функция-член выбирается статически (во время компиляции) на основе
тип указателя (или ссылки) на объект.
Напротив, виртуальные функции-члены разрешаются динамически (при
время выполнения). То есть функция-член выбирается динамически (при
времени выполнения) в зависимости от типа объекта, а не от типа
указатель / ссылка на этот объект. Это называется «динамическое связывание».
Большинство компиляторов используют какой-либо вариант следующей техники: если
объект имеет одну или несколько виртуальных функций, компилятор помещает скрытые
указатель на объект, называемый «виртуальный указатель» или «v-указатель». это
v-указатель указывает на глобальную таблицу, называемую «виртуальная таблица» или
"V-таблицы."
Компилятор создает v-таблицу для каждого класса, который имеет хотя бы один
виртуальная функция. Например, если класс Circle имеет виртуальные функции
для draw () и move () и resize () будет ровно одна v-таблица
ассоциируется с классом Circle, даже если там был круг gazillion
объекты, и v-указатель каждого из этих объектов круга будет указывать
К Кругу V-стол. Сам V-стол имеет указатели на каждый из
виртуальные функции в классе. Например, V-таблица Circle будет
иметь три указателя: указатель на Circle :: draw (), указатель на
Circle :: move () и указатель на Circle :: resize ().
Во время отправки виртуальной функции система времени выполнения следует
v-указатель объекта на v-таблицу класса, затем следует
соответствующий слот в v-таблице для кода метода.
Накладные расходы на указанную выше технику являются номинальными: дополнительная
указатель на объект (но только для объектов, которые нужно будет выполнять динамически
привязка), плюс дополнительный указатель на метод (но только для виртуального
методы). Накладные расходы также довольно номинальны: по сравнению с
нормальный вызов функции, вызов виртуальной функции требует двух дополнительных
выборки (одна для получения значения v-указателя, вторая для получения
адрес метода). Ни одно из этих действий во время выполнения не происходит с
не виртуальные функции, так как компилятор разрешает не виртуальные
функционирует исключительно во время компиляции в зависимости от типа
указатель.
Моя проблема, или как я сюда попал
Сейчас я пытаюсь использовать что-то подобное для базового класса cubefile с шаблонно оптимизированными функциями загрузки, которые будут реализованы по-разному для разных типов кубов (некоторые сохраняются по пикселям, некоторые по изображению и т. Д.).
Код:
virtual void LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
Я бы хотел, чтобы он был, но он не скомпилируется из-за виртуальной шаблонной комбинации:
template<class T>
virtual void LoadCube(UtpBipCube<T> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
В итоге я переместил объявление шаблона на уровень класса . Это решение заставило бы программы знать о конкретных типах данных, которые они будут читать, прежде чем они их прочитают, что недопустимо.
Решение
предупреждение, это не очень красиво, но оно позволило мне удалить повторяющийся код выполнения
1) в базовом классе
virtual void LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
2) и в детских классах
void LoadCube(UtpBipCube<float> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }
void LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }
void LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }
template<class T>
void LoadAnyCube(UtpBipCube<T> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1);
Обратите внимание, что LoadAnyCube не объявлен в базовом классе.
Вот еще один ответ о переполнении стека с обходным решением:
нужен обходной путь к виртуальному шаблону .