C++ даёт возможность определять встраиваемые (inline) функции и методы. Реализация таких функций будет встраиваться в другие функции по месту вызова (за исключением некоторых случаев, когда это невозможно). Как правило, в конечном исполняемом файле или динамической библиотеке отдельной реализации таких функций не генерируется.
Другой замечательный механизм, предоставляемый языком C++ — это виртуальные методы. Эти методы участвуют в полиморфизме. Вы может вызывать такие функции из производного класс по указателю или ссылке на базовый. Достигается это путём помещения указателя на конкретную реализацию в таблицу виртуальных функций, которая помещается компилятором в известное ему место относительно полиморфного указателя или ссылке (конкретная реализация не стандартизирована и зависит от компилятора).

Стандарт C++ позволяет применять модификатор virtual к inline функциям. Тут мы наталкиваемся на неоднозначность. Как в таблицу виртуальных функций попадёт указатель, если тело функции не генерируется? Ответ на этот вопрос также не стандартизирован и зависит от компилятора. Давайте попробуем разобраться.
Итак, допустим у нас есть такой код

extern "C" void exFunction(int);

class Base
{
public:
virtual int doSomething(int x, int y)
{
return x + y;
}
};

class Child : public Base
{
public:
virtual int doSomething(int x, int y)
{
return x * y;
}
};

extern "C" void run()
{
Base base;
int result = base.doSomething(1, 2);
exFunction(result);
Child child;
result = child.doSomething(2, 2);
exFunction(result);
}

Функция exFunction нужна лишь для того, чтобы компилятор не соптимизировал наши вычисления.
Я буду использовать компилятор g++ 4.7.2 и операционную систему Debian GNU/Linux 7.0 (wheezy) 64 bit.
Сохраним исходный текст в файл test.cpp соберём динамическую библиотеку с помощью команды

g++ -g -O2 -shared -fPIC  test.cpp

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

objdump -d a.out
00000000000006e0 <run>:
6e0: 48 83 ec 08 sub $0x8,%rsp
6e4: bf 03 00 00 00 mov $0x3,%edi
6e9: e8 e2 fe ff ff callq 5d0 <exFunction@plt>
6ee: bf 04 00 00 00 mov $0x4,%edi
6f3: 48 83 c4 08 add $0x8,%rsp
6f7: e9 d4 fe ff ff jmpq 5d0 <exFunction@plt>

Как видим, компилятор не обратил внимание на модификатор virtual и просто встроил inline методы в код, соптимизировав сложение и умножение.
Теперь добавим в код полиморфизма.

extern "C" void exFunction(int);

class Base
{
public:
virtual int doSomething(int x, int y)
{
return x + y;
}
};

class Child : public Base
{
public:
virtual int doSomething(int x, int y)
{
return x * y;
}
};

extern "C" void run()
{
Base base;
int result = base.doSomething(1, 2);
exFunction(result);
Child child;
result = child.doSomething(2, 2);
exFunction(result);
Base * polymorphic = &child;
result = polymorphic->doSomething(3, 3);
exFunction(result);
}

Скомпилируем и посмотрим на дизассемблированный код.

0000000000000a50 <run>:
a50: 48 83 ec 18 sub $0x18,%rsp
a54: bf 03 00 00 00 mov $0x3,%edi
a59: e8 e2 fe ff ff callq 940 <exFunction@plt>
a5e: 48 8b 05 e3 03 20 00 mov 0x2003e3(%rip),%rax # 200e48 <_DYNAMIC+0x228>
a65: bf 04 00 00 00 mov $0x4,%edi
a6a: 48 83 c0 10 add $0x10,%rax
a6e: 48 89 04 24 mov %rax,(%rsp)
a72: e8 c9 fe ff ff callq 940 <exFunction@plt>
a77: 48 8b 04 24 mov (%rsp),%rax
a7b: 48 89 e7 mov %rsp,%rdi
a7e: ba 03 00 00 00 mov $0x3,%edx
a83: be 03 00 00 00 mov $0x3,%esi
a88: ff 10 callq *(%rax)
a8a: 89 c7 mov %eax,%edi
a8c: e8 af fe ff ff callq 940 <exFunction@plt>
a91: 48 83 c4 18 add $0x18,%rsp
a95: c3 retq
a96: 90 nop
a97: 90 nop
a98: 90 nop
a99: 90 nop
a9a: 90 nop
a9b: 90 nop
a9c: 90 nop
a9d: 90 nop
a9e: 90 nop
a9f: 90 nop

0000000000000aa0 <_ZN4Base11doSomethingEii>:
aa0: 8d 04 16 lea (%rsi,%rdx,1),%eax
aa3: c3 retq
aa4: 90 nop
aa5: 90 nop
aa6: 90 nop
aa7: 90 nop
aa8: 90 nop
aa9: 90 nop
aaa: 90 nop
aab: 90 nop
aac: 90 nop
aad: 90 nop
aae: 90 nop
aaf: 90 nop

0000000000000ab0 <_ZN5Child11doSomethingEii>:
ab0: 89 f0 mov %esi,%eax
ab2: 0f af c2 imul %edx,%eax
ab5: c3 retq
ab6: 90 nop
ab7: 90 nop

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

Вообще говоря, компилятор всегда будет генерировать тело inline функции, если где-либо в программе требуется её адрес (явно или неявно). При этом, Вы можете расчитывать на встраивание virtual inline функции, если не используете для её вызова указатель или ссылку.

Я провёл подобное исследование с Visual C++, и оказалось, что этот компилятор ведёт себя аналогичным образом.