程序员什么时候使用空基优化(EBO)

发布于 2024-10-05 01:14:58 字数 954 浏览 1 评论 0原文

我正在阅读有关空基优化(EBO)的内容。在阅读时,我的脑海中出现了以下问题:

  1. 它对派生类没有任何贡献(无论是功能方面还是数据方面)时,使用空类作为基类有什么意义?

  2. 这篇文章,我读了这个:

//S为空
类结构 T : S
{
     int x;
};

[...]

请注意,我们没有丢失任何数据或 代码准确性:当您创建 S 类型的独立对象, 对象的大小仍然是 1(或更大) 前;仅当 S 为基时 另一个类的类执行其内存操作 足迹缩小到零。实现 这种节省的影响,想象一下 包含 125,000 个的向量 对象。 仅 EBO 就节省了一半 兆字节内存!

这是否意味着如果我们不使用“S”作为“T”的基类,我们将必然消耗双兆字节的内存?我认为,这篇文章比较了两种不同的场景,我认为这是不正确的。

我想知道一个真实的场景,当EBO被证明是有用的时。(意味着,在同样的场景下,如果我们不使用EBO,我们必然会不知所措!)。

请注意,如果您的答案包含这样的解释:

重点是空类具有非零大小,但在派生或派生时它可以具有零大小,那么我不会问这个,因为我已经知道了。我的问题是,为什么有人首先会从空类中派生出他的类? 即使他没有导出并简单地编写了他的类(没有任何空基),他是否会以任何方式感到困惑?

I was reading about Empty Base Optimization(EBO). While reading, the following questions popped up in my mind:

  1. What is the point of using Empty class as base class when it contributes nothing to the derived classes (neither functionality-wise, nor data-wise)?

  2. In this article, I read this:

//S is empty
class struct T : S
{
      int x;
};

[...]

Notice that we didn’t lose any data or
code accuracy: when you create a
standalone object of type S, the
object’s size is still 1 (or more) as
before; only when S is used as base
class of another class does its memory
footprint shrink to zero. To realize
the impact of this saving, imagine a
vector that contains 125,000
objects. The EBO alone saves half a
megabyte of memory!

Does it mean that if we don't use "S" as base class of "T", we would necessarily consume double of megabyte of memory? I think, the article compares two different scenarios which I don't think is correct.

I would like to know a real scenario when EBO can proven to be useful.(means, in the same scenario, we would necessarily be at loss IF we don't use EBO!).

Please note that if your answer contains explanations like this :

The whole point is that an empty class has non-zero size, but when derived or deriving it can have zero size, then I'm NOT asking that, as I know that already. My question is, why would anyone derive his class from an empty class in the first place? Even if he doesn't derive and simply writes his class (without any empty base), is he at loss in ANY way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

把时间冻结 2024-10-12 01:14:58

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 的类,只需不增加类的大小开销< code>Test :这是一个完美的例子,不要为你不使用的东西付费

EBO is important in the context of policy based design, where you generally inherit privately from multiple policy classes. If we take the example of a thread safety policy, one could imagine the pseudo-code :

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 */ }
};

Given a policy based-design class such as :

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

Using the class with a MTUnsafePolicy simply add no size overhead the class Test : it's a perfect example of don't pay for what you don't use.

蓝戈者 2024-10-12 01:14:58

EBO 中的“优化”意味着使用基类的情况可以优化为使用比使用相同类型的成员更少的内存。也就是说,

struct T : S 
{
      int x;
};

如果

struct T
{
      S s;
      int x;
};

您的问题是为什么

struct T
{
      int x;
};

您有一个空类(作为成员或作为基类),那是因为您使用了它的成员函数。空意味着它没有数据成员,而不是它根本没有任何成员。使用模板编程时经常会发生这样的事情,其中​​基类有时是“空”(没有数据成员),有时不是。

The "Optimization" in the EBO means the case when you use base class can be optimized to use less memory than if you use a member of the same type. I.e. you compare

struct T : S 
{
      int x;
};

with

struct T
{
      S s;
      int x;
};

not with

struct T
{
      int x;
};

If your question is why would you have an empty class at all (either as a member, or as a base), it is because you use its member functions. Empty means it has no data member, not that it does not have any members at all. Things like this are often done when programming with templates, where the base class is sometimes "empty" (no data members) and sometimes not.

栩栩如生 2024-10-12 01:14:58

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 由于填充字节)。

EBO isn't really an optimization (at least not one that you do in the code). The whole point is that an empty class has non-zero size, but when derived or deriving it can have zero size.

This is the most usual result:

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;
}

Output:

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

To the edit:
The optimization is, that if you actually do derive (for example from a functor, or from a class that has only static members), the size of your class (that is deriving) won't increase by 1 (or more likely 4 or 8 due to padding bytes).

心如狂蝶 2024-10-12 01:14:58

当程序员想要向客户端公开一些数据而不增加客户端类大小时使用它。空类可以包含枚举和类型定义或一些客户端可以使用的定义。使用此类的最明智的方法是私有继承此类。这将从外部隐藏数据,并且不会增加班级规模。

Its used when programmers want to expose some data to client without increasing the client class size. The empty class can contain enums and typedefs or some defines which the client can use.The most judicious way to use such a class it it to,inherit such a class privately. This will hide the data from outside and wil not increase your class size.

つ低調成傷 2024-10-12 01:14:58

可以有没有任何成员变量的空类,但可以充当实用程序类的成员函数(staticnon static),我们称之为 EmptyClass。现在我们可以有这样的情况:我们想要创建一个类(我们称之为 SomeClass),它与 EmptyClass 具有包含关系,但不是“is-a”关系。一种方法是在 SomeClass 中创建一个 EmptyClass 类型的成员对象,如下所示:

class EmptyClass  
{
public:
    void someFun1();
    static int someUtilityFun2();
};
//sizeof(EmptyClass) = 1


class SomeClass
{
 private:
    EmptyClass e;
    int x;
};
//sizeof(SomeClass) = 8

现在,由于某些对齐要求,编译器可能会向 SomeClass 添加填充,并且它的大小现在是 8 字节。更好的解决方案是让 SomeClassEmptyClass 私有派生,这样 SomeClass 将可以访问 EmptyClass 的所有成员函数 并且不会通过填充增加额外的大小。

class SomeClass : private EmptyClass
{
private:
    int x;
} 
//sizeof(SomeClass) = 4

There can be empty classes which do not have any member variables, but member functions (static or non static) which can act as utility classes, lets call this EmptyClass. Now we can have a case where we want to create a class (let's call it SomeClass) which have a containment kind of relation with EmptyClass, but not 'is-a' relation. One way is to create a member object of type EmptyClass in SomeClass as follows:

class EmptyClass  
{
public:
    void someFun1();
    static int someUtilityFun2();
};
//sizeof(EmptyClass) = 1


class SomeClass
{
 private:
    EmptyClass e;
    int x;
};
//sizeof(SomeClass) = 8

Now due to some alignment requirements compilers may add padding to SomeClass and its size is now 8 bytes. The better solution is to have a SomeClass derive privately from EmptyClass and in this way SomeClass will have access to all member functions of EmptyClass and won't increase the extra size by padding.

class SomeClass : private EmptyClass
{
private:
    int x;
} 
//sizeof(SomeClass) = 4
清音悠歌 2024-10-12 01:14:58

大多数时候,空基类要么以多态方式使用(本文提到),作为“标记”类,要么作为异常类(尽管这些通常派生自 std::exception,它不为空)。有时,开发一个以空基类开始的类层次结构是有充分理由的。

如果其中一个元素为空,Boost.CompressedPair 使用 EBO 来缩小对象的大小。

Most of the time, an empty base class is either used polymorphically (which the article mentions), as "tag" classes, or as exception classes (although those are usually derived from std::exception, which is not empty). Sometimes there is a good reason to develop a class hierarchy which begins with an empty base class.

Boost.CompressedPair uses the EBO to shrink the size of objects in the event that one of the elements is empty.

可是我不能没有你 2024-10-12 01:14:58

EASTL 有一个很好的解释为什么他们需要 EBO,在他们链接的论文/信用中也对此进行了深入解释

EASTL has a good explanation as to why they needed EBO, its also explained in-depth in the paper they link to/credit

落花随流水 2024-10-12 01:14:58

EBO 不是程序员影响的东西,并且/或者如果程序员选择不从空基类派生,他就会受到惩罚。

编译器控制是否

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

获得 sizeof(X) == sizeof(Y) 。如果这样做,编译器就会实现 EBO,如果没有,则不会。

从来没有任何情况 sizeof(Y) > > sizeof(X) 将会发生。

EBO is not something the programmer influences, and/or the programmer would be punished for if (s)he chose not to derive from an empty base class.

The compiler controls whether for:

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

you get sizeof(X) == sizeof(Y) or not. If you do, the compiler implements EBO, if not, it doesn't.

There never is any situation where sizeof(Y) > sizeof(X) would occur.

不一样的天空 2024-10-12 01:14:58

我能想到的主要好处是dynamic_cast。您可以获取指向 S 的指针,并尝试将其动态强制转换为从 S 继承的任何内容 - 假设 S 提供像虚拟析构函数这样的虚拟函数,它几乎必须作为基类执行此操作。例如,如果您要实现动态类型语言,您可能希望或需要每种类型都从基类派生,纯粹是为了类型擦除存储以及通过dynamic_cast 进行类型检查。

The primary benefit I can think of is dynamic_cast. You can take a pointer to S and attempt to dynamic_cast it to anything that inherits from S- assuming that S offers a virtual function like a virtual destructor, which it pretty much must do as a base class. If you were, say, implementing a dynamically typed language, you may well wish or need for every type to derive from a base class purely for the purposes of type-erased storage, and type checking through dynamic_cast.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文