c++ - ser - yo no sirvo para programar
¿Cuándo usan los programadores la optimización de la base vacía(EBO)? (8)
EBO es importante en el contexto del diseño basado en políticas , donde generalmente se hereda de varias clases de políticas. Si tomamos el ejemplo de una política de seguridad de subprocesos, uno podría imaginar el pseudocódigo:
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 */ }
};
Dada una clase de diseño basado en políticas como:
template<class ThreadSafetyPolicy>
class Test : ThreadSafetyPolicy
{
/* ... */
};
Usar la clase con un MTUnsafePolicy
simplemente no agrega gastos generales de tamaño a la clase Test
: es un ejemplo perfecto de no pagar por lo que no usas .
Estaba leyendo sobre la optimización de la base vacía (EBO). Mientras leía, surgieron en mi mente las siguientes preguntas:
¿Cuál es el punto de usar la clase vacía como clase base cuando no contribuye a las clases derivadas (ni a la funcionalidad ni a los datos) ?
En este artículo , leí esto:
// S está vacío
clase estructura T: S
{
int x;
};[...]
Tenga en cuenta que no perdimos ningún dato ni precisión de código: cuando crea un objeto independiente de tipo S, el tamaño del objeto sigue siendo 1 (o más) como antes; solo cuando S se usa como clase base de otra clase su huella de memoria se reduce a cero. Para darse cuenta del impacto de este ahorro, imagine un vector que contiene 125,000 objetos. ¡El EBO solo ahorra medio megabyte de memoria!
¿Significa que si no usamos "S" como clase base de "T", necesariamente consumiríamos el doble de megabyte de memoria? Creo que el artículo compara dos escenarios diferentes que no creo que sean correctos.
Me gustaría conocer un escenario real en el que EBO pueda demostrar ser útil (significa que, en el mismo escenario, estaremos necesariamente en una pérdida si NO usamos EBO).
Tenga en cuenta que si su respuesta contiene explicaciones como esta:
El punto es que una clase vacía tiene un tamaño distinto de cero, pero cuando se deriva o se deriva puede tener un tamaño cero, entonces NO estoy preguntando eso, como ya lo sé. Mi pregunta es, ¿por qué alguien derivaría su clase de una clase vacía en primer lugar? Incluso si él no deriva y simplemente escribe su clase (sin ninguna base vacía), ¿está perdido de alguna manera?
EBO no es algo que el programador influya, y / o el programador sería castigado si él (s) eligiera no derivar de una clase base vacía.
El compilador controla si para:
class X : emptyBase { int X; };
class Y { int x };
usted obtiene sizeof(X) == sizeof(Y)
o no. Si lo hace, el compilador implementa EBO, si no, no lo hace.
Nunca hay una situación en la que se produzca sizeof(Y) > sizeof(X)
.
EBO no es realmente una optimización (al menos no la que haces en el código). El punto es que una clase vacía tiene un tamaño distinto de cero, pero cuando se deriva o se deriva puede tener un tamaño cero.
Este es el resultado más habitual:
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;
}
Salida:
sizeof(A) + sizeof(B) == 2
sizeof(D) == 1
Para la edición: la optimización es que si realmente deriva (por ejemplo, de un functor o de una clase que solo tiene miembros estáticos), el tamaño de su clase (que se deriva) no aumentará en 1 (o más probable 4 u 8 debido a los bytes de relleno).
El principal beneficio que se me ocurre es dynamic_cast. Puede llevar un puntero a S e intentar dinamizarlo a cualquier cosa que se herede de S, suponiendo que S ofrece una función virtual como un destructor virtual, que prácticamente debe hacer como una clase base. Por ejemplo, si implementara un lenguaje de tipo dinámico, es posible que desee o necesite que cada tipo se derive de una clase base únicamente para el almacenamiento de borrado de tipo y la comprobación de tipo a través de dynamic_cast.
La "Optimización" en la EBO significa que el caso cuando usa la clase base se puede optimizar para usar menos memoria que si usara un miembro del mismo tipo. Es decir, comparas
struct T : S
{
int x;
};
con
struct T
{
S s;
int x;
};
no con
struct T
{
int x;
};
Si su pregunta es por qué tendría una clase vacía (como miembro o como base), es porque usa sus funciones miembro. Vacío significa que no tiene ningún miembro de datos, no que no tenga ningún miembro en absoluto. Las cosas como esta a menudo se hacen al programar con plantillas, donde la clase base a veces está "vacía" (sin miembros de datos) y otras no.
La mayoría de las veces, una clase base vacía se usa de forma polimórfica (como lo menciona el artículo), como clases de "etiqueta" o como clases de excepción (aunque generalmente se derivan de std :: exception, que no está vacía). A veces hay una buena razón para desarrollar una jerarquía de clases que comienza con una clase base vacía.
Boost.CompressedPair utiliza el EBO para reducir el tamaño de los objetos en caso de que uno de los elementos esté vacío.
Se usa cuando los programadores desean exponer algunos datos al cliente sin aumentar el tamaño de la clase del cliente. La clase vacía puede contener enums y typedefs o algunas definiciones que el cliente puede usar. La forma más sensata de usar una clase de este tipo es heredarla de forma privada. Esto ocultará los datos del exterior y no aumentará el tamaño de su clase.
EASTL tiene una buena explicación de por qué necesitaban EBO, también se explica en profundidad en el documento al que se vinculan / crédito