Я немного удивлен некоторыми ответами / комментариями ...
наследование несет некоторую стоимость (с точки зрения памяти)
Да. Дано:
namespace MON {
class FooBase {
public:
FooBase();
virtual ~FooBase();
virtual void f();
private:
uint8_t a;
};
class Foo : public FooBase {
public:
Foo();
virtual ~Foo();
virtual void f();
private:
uint8_t b;
};
class MiniFoo {
public:
MiniFoo();
~MiniFoo();
void f();
private:
uint8_t a;
uint8_t b;
};
class MiniVFoo {
public:
MiniVFoo();
virtual ~MiniVFoo();
void f();
private:
uint8_t a;
uint8_t b;
};
} // << MON
extern "C" {
struct CFoo {
uint8_t a;
uint8_t b;
};
}
в моей системе размеры следующие:
32 bit:
FooBase: 8
Foo: 8
MiniFoo: 2
MiniVFoo: 8
CFoo: 2
64 bit:
FooBase: 16
Foo: 16
MiniFoo: 2
MiniVFoo: 16
CFoo: 2
время выполнения для строительства или уничтожения объекта
дополнительная служебная функция и виртуальная диспетчеризация, где это необходимо (включая деструкторы, где это необходимо) это может стоить много, и некоторые действительно очевидные оптимизации, такие как встраивание, могут / не могут быть выполнены.
весь предмет гораздо сложнее, но это даст вам представление о затратах.
Если скорость или размер действительно важны, то вы часто можете использовать статический полиморфизм (например, шаблоны) для достижения превосходного баланса между производительностью и простотой программирования.
относительно производительности процессора, я создал простой тест, который создал миллионы этих типов в стеке и в куче и назвал f, результаты:
FooBase 16.9%
Foo 16.8%
Foo2 16.6%
MiniVFoo 16.6%
MiniFoo 16.2%
CFoo 15.9%
примечание: Foo2 происходит от foo
в тесте распределения добавляются к вектору, а затем удаляются. без этого этапа CFoo был полностью оптимизирован. как написал Джефф Деге в своем ответе, время распределения будет огромной частью этого теста.
Обрезка функций выделения и вектора создания / уничтожения вектора из выборки дает следующие числа:
Foo 19.7%
FooBase 18.7%
Foo2 19.4%
MiniVFoo 19.3%
MiniFoo 13.4%
CFoo 8.5%
, что означает, что виртуальные варианты занимают в два раза больше времени, чем CFoo для выполнения своих конструкторов, деструкторов и вызовов, а MiniFoo примерно в 1,5 раза быстрее.
пока мы находимся в распределении: если вы можете использовать один тип для своей реализации, вы также уменьшите количество выделений, которые вы должны сделать в этом сценарии, потому что вы можете выделить массив из 1M объектов, вместо того, чтобы создавать список 1M адресов, а затем заполняет его уникально новыми типами. Конечно, существуют специальные распределители, которые могут уменьшить этот вес. так как распределение / свободные времена являются весом этого теста, это значительно сократит время, которое вы тратите на выделение и освобождение объектов.
Create many MiniFoos as array 0.2%
Create many CFoos as array 0.1%
Также имейте в виду, что размеры MiniFoo и CFoo занимают 1/4 - 1/8 памяти на элемент, а непрерывное распределение устраняет необходимость хранить указатели на динамические объекты. Затем вы могли бы отслеживать объект в реализации несколькими способами (указатель или индекс), но массив также может значительно снизить требования по распределению на клиентах (uint32_t против указателя на 64-битную арку) - плюс вся бухгалтерская отчетность, требуемая системой для распределений (что важно при работе с таким большим количеством небольших распределений).
В частности, размеры в этом тесте потребляются:
32 bit
267MB for dynamic allocations (worst)
19MB for the contiguous allocations
64 bit
381MB for dynamic allocations (worst)
19MB for the contiguous allocations
это означает, что требуемая память была уменьшена более чем на десять, а время, потраченное на выделение / освобождение, значительно лучше!
Реализация статической диспетчеризации по сравнению со смешанной или динамической диспетчеризацией может быть в несколько раз быстрее. Обычно это дает оптимизаторам больше возможностей для просмотра большей части программы и соответствующей ее оптимизации.
На практике динамические типы имеют тенденцию экспортировать больше символов (методов, dtors, vtables), что может заметно увеличить размер двоичного файла.
Предполагая, что это ваш реальный вариант использования, вы можете значительно улучшить производительность и использование ресурсов. я представил ряд основных оптимизаций ... на тот случай, если кто-то поверит, что изменение дизайна таким способом можно квалифицировать как "микро-оптимизация".