Подтвердить что ты не робот

Когда программисты используют пустую базовую оптимизацию (EBO)

Я читал о Пустой Базовой Оптимизации (EBO). При чтении в голове возникли следующие вопросы:

  • В чем смысл использования пустого класса в качестве базового класса, когда он ничего не способствует производным классам (ни по функциональности, ни по данным)?

  • В этой статье я прочитал следующее:

//S пуст
class struct T: S
{
       int x;
};

[...]

Обратите внимание, что мы не потеряли никаких данных или точность кода: при создании автономный объект типа S, размер объектов по-прежнему равен 1 (или более), так как до; только когда S используется как основа класс другого класса выполняет свою память снижается до нуля. Чтобы реализовать влияние этой экономии, представьте себе вектор, содержащий 125 000 объекты. Только EBO экономит половину мегабайт памяти!

Означает ли это, что если мы не будем использовать "S" в качестве базового класса "T", мы бы обязательно потребляем вдвое меньше мегабайта памяти? Я думаю, в статье сравниваются два разных сценария, которые, как я считаю, не верны.

Я хотел бы знать реальный сценарий, когда EBO может оказаться полезным. (означает, что в том же сценарии мы обязательно будем потеряны, ЕСЛИ мы не будем использовать EBO!).

Обратите внимание, что если ваш ответ содержит такие пояснения:

Все дело в том, что пустой класс имеет ненулевой размер, но когда производный или выводимый он может иметь нулевой размер, я не прошу об этом, как я уже знаю. Мой вопрос в том, почему кто-нибудь вывел свой класс из пустого класса в первую очередь? Даже если он не выводит и просто пишет свой класс (без какой-либо пустой базы), находится ли он в убытке ЛЮБОЙ способ?

4b9b3361

Ответ 1

EBO важен в контексте политики на основе дизайна, где вы обычно наследуете конфиденциально от нескольких классов политики. Если мы возьмем пример политики безопасности потоков, можно представить себе псевдокод:

class MTSafePolicy
{
public:
  void lock() { mutex_.lock(); }
  void unlock() { mutex_.unlock(); }

private:
  Mutex mutex_;
};

class MTUnsafePolicy
{
public:
  void lock() { /* no-op */ }
  void unlock() { /* no-op */ }
};

Учитывая класс разработки на основе политик, например:

template<class ThreadSafetyPolicy>
class Test : ThreadSafetyPolicy
{
  /* ... */
};

Используя класс с MTUnsafePolicy, просто добавьте накладные расходы на размер класса Test: это прекрасный пример того, что вы не платите за то, что не используете.

Ответ 2

EBO - это не оптимизация (по крайней мере, не то, что вы делаете в коде). Все дело в том, что пустой класс имеет ненулевой размер, но когда производный или выводимый он может иметь нулевой размер.

Это самый обычный результат:

class A { };
class B { };

class C { };
class D : C { };

#include <iostream>
using namespace std;

int main()
{
        cout << "sizeof(A) + sizeof(B) == " << sizeof(A)+sizeof(B) << endl;
        cout << "sizeof(D) == " << sizeof(D) << endl;

        return 0;
}

Вывод:

sizeof(A) + sizeof(B) == 2
sizeof(D) == 1

К редактированию: Оптимизация заключается в том, что если вы действительно выполняете (например, из функтора или из класса, который имеет только статические члены), размер вашего класса (который выводится) не будет увеличиваться на 1 (или, более вероятно, 4 или 8 из-за заполнения байтов).

Ответ 3

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

struct T : S 
{
      int x;
};

с

struct T
{
      S s;
      int x;
};

не с

struct T
{
      int x;
};

Если ваш вопрос заключается в том, почему у вас есть пустой класс вообще (либо как член, либо как база), это потому, что вы используете его функции-члены. Пустой означает, что у него нет данных, а не на каких-либо членов. Такие вещи часто выполняются при программировании с помощью шаблонов, где базовый класс иногда "пуст" (нет элементов данных), а иногда и нет.

Ответ 4

Используется, когда программисты хотят выставить некоторые данные клиенту без увеличения размера класса клиента. Пустой класс может содержать перечисления и typedefs или некоторые определяет, что клиент может использовать. Самый разумный способ использовать такой класс, к которому он относится, наследует такой класс в частном порядке. Это скроет данные извне и не увеличит ваш размер класса.

Ответ 5

EASTL имеет хорошее объяснение того, зачем им нужен EBO, его также подробно объяснено в документе, который они ссылаются на/credit

Ответ 7

EBO - это не то, на что влияет программист, и/или программист будет наказан за то, что (а) он решил не извлекать из пустого базового класса.

Компилятор контролирует, для:

class X : emptyBase { int X; };
class Y { int x };

вы получаете sizeof(X) == sizeof(Y) или нет. Если вы это сделаете, компилятор реализует EBO, если нет, это не так.

Там никогда не будет ситуации, в которой sizeof(Y) > sizeof(X).

Ответ 8

В большинстве случаев пустой базовый класс используется либо полиморфно (что упоминается в статье), либо как классы "тега", либо как классы исключений (хотя обычно это происходит из std:: exception, который не является пустым), Иногда есть веская причина для разработки иерархии классов, которая начинается с пустого базового класса.

Boost.CompressedPair использует EBO для уменьшения размера объектов в случае, если один из элементов пуст.

Ответ 9

Основное преимущество, о котором я могу думать, - dynamic_cast. Вы можете взять указатель на S и попробовать dynamic_cast его на что-либо, что наследует от S-, предполагая, что S предлагает виртуальную функцию, такую ​​как виртуальный деструктор, который он в значительной степени должен выполнять в качестве базового класса. Если бы вы, скажем, внедряли динамически типизированный язык, вы можете пожелать или нуждаться в том, чтобы каждый тип выводился из базового класса исключительно для целей хранения с типом и проверял тип через dynamic_cast.