Я не согласен с @sechastain
.
Инлайнинг далеко не автоматический. Независимо от того, определен ли метод на месте или используется подсказка (ключевое слово inline
или __forceinline
), компилятор - единственный, кто решает, будет ли в действительности вставка, и использует для этого сложную эвристику. Однако один конкретный случай заключается в том, что он не должен выполнять встроенный вызов, когда виртуальный метод вызывается с использованием диспетчеризации во время выполнения, именно потому, что диспетчеризация и вставка во время выполнения несовместимы.
Чтобы понять точность "использования диспетчеризации во время выполнения":
IClassInterface* i = /**/;
i->LOL(); // runtime dispatch
i->QueueClass::LOL(); // compile time dispatch, inline is possible
@0xDEAD BEEF
: Я считаю ваш дизайн хрупким, если не сказать больше.
Использование приведений в стиле C здесь неправильно:
QueueClass* p = /**/;
IClassInterface* q = p;
assert( ((void*)p) == ((void*)q) ); // may fire or not...
Принципиально нет гарантии того, что 2 адреса равны: это определяется реализацией и вряд ли будет противостоять изменениям.
Если вы хотите иметь возможность безопасно приводить указатель void*
к указателю IClassInterface*
, тогда вам нужно создать его из IClassInterface*
первоначально, чтобы компилятор C ++ мог выполнять правильную арифметику указателей в зависимости от макета объектов.
Конечно, я также подчеркну, чем использование глобальных переменных ... вы, вероятно, знаете это.
Как по причине отсутствия? Честно говоря, я не вижу ничего, кроме ошибки в компиляторе / компоновщике. Я видел встроенное определение функций virtual
несколько раз (точнее, метод clone
), и оно никогда не вызывало проблем.
РЕДАКТИРОВАТЬ : Поскольку "правильная арифметика указателей" не так хорошо понятна, вот пример
struct Base1 { char mDum1; };
struct Base2 { char mDum2; };
struct Derived: Base1, Base2 {};
int main(int argc, char* argv[])
{
Derived d;
Base1* b1 = &d;
Base2* b2 = &d;
std::cout << "Base1: " << b1
<< "\nBase2: " << b2
<< "\nDerived: " << &d << std::endl;
return 0;
}
А вот что было напечатано:
Base1: 0x7fbfffee60
Base2: 0x7fbfffee61
Derived: 0x7fbfffee60
Не разница между значениями b2
и &d
, даже если они относятся к одному объекту. Это можно понять, если подумать о расположении объекта в памяти.
Derived
Base1 Base2
+-------+-------+
| mDum1 | mDum2 |
+-------+-------+
При преобразовании из Derived*
в Base2*
компилятор выполнит необходимую настройку (в данном случае увеличит адрес указателя на один байт), чтобы указатель в итоге эффективно указывал на Base2
часть Derived
а не к части Base1
, ошибочно интерпретируемой как объект Base2
(что было бы неприятно).
Именно поэтому следует избегать использования приведения в стиле C при понижении скорости. Здесь, если у вас есть указатель Base2
, вы не можете интерпретировать его как указатель Derived
. Вместо этого вам придется использовать static_cast<Derived*>(b2)
, который уменьшит указатель на один байт, чтобы он правильно указывал на начало объекта Derived
.
Управление указателями обычно называют арифметикой указателей. Здесь компилятор автоматически выполнит правильную настройку ... при условии, что он знает тип.
К сожалению, компилятор не может выполнить их при преобразовании из void*
, поэтому разработчик должен убедиться, что он правильно с этим справляется. Простое правило состоит в следующем: T* -> void* -> T*
с одинаковым типом, появляющимся с обеих сторон.
Поэтому вы должны (просто) исправить свой код, заявив: IClassInterface* globalMember
, и у вас не возникнет проблем с переносимостью. Вероятно, у вас все еще будут проблемы с обслуживанием, но это проблема использования C с ОО-кодом: C не знает о каких-либо объектно-ориентированных вещах.