使用虚拟继承避免内存开销

时间:2015-12-13 22:21:05

标签: c++ inheritance virtual-inheritance

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

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成员三次!

我考虑过使用虚拟继承来避免这种内存开销。如果我只是在每个继承的单词virtual之前插入单词public,那么我会收到关于基类变得无法访问的警告...如果我在每个这样的单词中加上单词virtual除了Inter0的声明之外,我得到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> {};
    ^

2 个答案:

答案 0 :(得分:2)

  

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

C大于预期的原因并非如此。

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

布局C:

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

注意:ManagedNode<>为空。

答案 1 :(得分:0)

经过多次努力(见Jarod42的评论和答案),我提出了以下非常天真的解决方案。它有一些重复的代码,但避免了基类中含糊不清的所有麻烦:

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