【问题标题】:Avoiding the memory overhead by using virtual inheritance通过使用虚拟继承避免内存开销
【发布时间】:2016-03-19 08:18:46
【问题描述】:

以下代码旨在为我的库提供有关用户类派生的基类的反射信息:

template <class Base1_ = void, class Base2_ = void, class Base3_ = void,
          class Base4_ = void>
struct ManagedNode;

// For classes that do not derive
template <> struct ManagedNode<void, void, void, void> {
    using Base1 = void; using Base2 = void; using Base3 = void;
    using Base4 = void;
};
// To avoid inaccessible base
// See http://stackoverflow.com/q/34255802/2725810
struct Inter0: public ManagedNode<>{};

// For classes that derive from a single base class
template <class Base1_>
struct ManagedNode<Base1_, void, void, void> : public Inter0,
                                               public Base1_ {
    using Base1 = Base1_;
};
// To avoid inaccessible base
template <class Base1_>
struct Inter1: public ManagedNode<Base1_>{};

// For classes that derive from two base classes
template <class Base1_, class Base2_>
struct ManagedNode<Base1_, Base2_, void, void> : public Inter1<Base1_>,
                                                 public Base2_ {
    using Base2 = Base2_;
};

// We can continue in the same manner for 3 and 4 base classes

这是一个示例用户代码:

struct A : public ManagedNode<> {
    int data1;
};

struct B : public ManagedNode<> {};

struct C : public ManagedNode<A, B> {};

int main() {
    C c;
    std::cout << sizeof(c) << std::endl;
    return 0;
}

这段代码产生 12 的输出,这意味着 c 包含 data1 成员 3 次!

我考虑使用虚拟继承来避免这种内存开销。如果我只是在每个继承的单词public 之前插入单词virtual,那么我会收到一个关于基类变得不可访问的警告......如果我把单词virtual 放在每个这样的地方,除了@ 987654330@,然后我得到 16 的输出——比以前更糟!

如果我在这里使用虚拟继承时会发生什么,我将非常感激。

编辑:为了完整起见,这里是插入了virtual 的版本(注释表明需要删除哪个virtual 才能编译代码):

template <class Base1_ = void, class Base2_ = void, class Base3_ = void,
          class Base4_ = void>
struct ManagedNode;

// For classes that do not derive
template <> struct ManagedNode<void, void, void, void> {
    using Base1 = void; using Base2 = void; using Base3 = void;
    using Base4 = void;
};
// To avoid inaccessible base
// See http://stackoverflow.com/q/34255802/2725810
struct Inter0: virtual public ManagedNode<>{}; // without the word virtual
                                               // in this line, the code compiles

// For classes that derive from a single base class
template <class Base1_>
struct ManagedNode<Base1_, void, void, void> : virtual public Inter0,
                                               virtual public Base1_ {
    using Base1 = Base1_;
};
// To avoid inaccessible base
template <class Base1_>
struct Inter1: virtual public ManagedNode<Base1_>{};

// For classes that derive from two base classes
template <class Base1_, class Base2_>
struct ManagedNode<Base1_, Base2_, void, void> : virtual public Inter1<Base1_>,
                                                 virtual public Base2_ {
    using Base2 = Base2_;
};

// Some user classes for testing the concept

struct A : public ManagedNode<> {
    int data1;
};

struct B : public ManagedNode<> {};

struct C : public ManagedNode<A, B> {};

int main() {
    C c;
    std::cout << sizeof(c) << std::endl;
    return 0;
}

这是编译器的输出:

temp.cpp: In instantiation of ‘struct ManagedNode<A, void, void, void>’:
temp.cpp:27:8:   required from ‘struct Inter1<A>’
temp.cpp:31:8:   required from ‘struct ManagedNode<A, B>’
temp.cpp:44:19:   required from here
temp.cpp:21:8: error: virtual base ‘ManagedNode<void, void, void, void>’ inaccessible in ‘ManagedNode<A, void, void, void>’ due to ambiguity [-Werror=extra]
 struct ManagedNode<Base1_, void, void, void> : virtual public Inter0,
        ^
temp.cpp: In instantiation of ‘struct Inter1<A>’:
temp.cpp:31:8:   required from ‘struct ManagedNode<A, B>’
temp.cpp:44:19:   required from here
temp.cpp:27:8: error: virtual base ‘ManagedNode<void, void, void, void>’ inaccessible in ‘Inter1<A>’ due to ambiguity [-Werror=extra]
 struct Inter1: virtual public ManagedNode<Base1_>{};
        ^
temp.cpp: In instantiation of ‘struct ManagedNode<A, B>’:
temp.cpp:44:19:   required from here
temp.cpp:31:8: error: virtual base ‘ManagedNode<void, void, void, void>’ inaccessible in ‘ManagedNode<A, B>’ due to ambiguity [-Werror=extra]
 struct ManagedNode<Base1_, Base2_, void, void> : virtual public Inter1<Base1_>,
        ^
temp.cpp:44:8: error: virtual base ‘ManagedNode<void, void, void, void>’ inaccessible in ‘C’ due to ambiguity [-Werror=extra]
 struct C : public ManagedNode<A, B> {};
    ^

【问题讨论】:

  • 您是从 SO 问题组装整个项目吗?我的意思是,问题没有限制,但是一个长周末的 15 个问题表明你可能应该花更多的时间自己研究和试验。对社区有用的好问题通常会以较低的速度出现。
  • 我很欣赏这一点,但是您寻找的很多信息已经在 SO 和其他地方(例如这里是 one example),如果您进行更多研究,您肯定会发现很多耐心点。
  • 您可以更快地得到答案
  • 是的,一般来说我们确实希望如此。发布一个新问题应该是absolute last resort。 (在这个例子中,我想我搜索了“size empty class base”或类似的东西。可能需要一些尝试和术语排列,但这就是我建议耐心等待的原因。)
  • @GabrielVince 我没有看到任何有用的评论。

标签: c++ inheritance virtual-inheritance


【解决方案1】:

这意味着C 包含data1 成员三次!

C 比预期大并不是这个原因。

问题是您的所有类都继承自ManagedNode&lt;&gt;,因此,由于每个对象都必须具有唯一的地址和类型组合,因此在最终结构中添加了一个偏移量。

布局 C:

- 0x00: ManagedNode<> // From Inter0
- 0x04: ManagedNode<> // From A
- 0x04: int           // From A
- 0x08: ManagedNode<> // From B

注意:ManagedNode&lt;&gt; 为空。

【讨论】:

  • 虚拟继承的全部目的不就是确保基的每个成员只被继承一次(即通过唯一的路径)吗?
  • 你的意思是说我在C中没有几个A子对象,即使不使用虚拟继承?这是噩梦,我完全糊涂了。我在哪里可以详细了解所有这些内容?
  • @AlwaysLearning 不,当然不是。这张海报似乎没有正确阅读问题。
  • 为什么ManagedNode&lt;&gt; 被多次包含,而data1 却没有?此外,如果是这种情况,则必须启动空基类优化(请参阅stackoverflow.com/a/11049434/2725810
  • @AlwaysLearning: AB 直接继承自 ManagedNode&lt;&gt;,对于 C 有更多间接性。这里有一条不允许空基类优化的规则:每个ManagedNode&lt;&gt; 应该有不同的地址。
【解决方案2】:

经过一番努力(请参阅 Jarod42 的答案的 cmets),我想出了以下非常幼稚的解决方案。它有一些重复的代码,但避免了基类中歧义的所有麻烦:

// For classes that do not derive
template<> struct ManagedNode<void, void, void, void> {
    using Base1 = void;
};

// For classes that derive from a single base class
template <class Base1_>
struct ManagedNode<Base1_, void, void, void> : public Base1_ {
    using Base1 = Base1_;
    using Base2 = void;
};

// For classes that derive from two base classes
template <class Base1_, class Base2_>
struct ManagedNode<Base1_, Base2_, void, void>
    : public Base1_, public Base2_ {
    using Base1 = Base1_;
    using Base2 = Base2_;
    using Base3 = void;
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-09
    • 2021-10-05
    • 2014-06-29
    • 2015-09-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多