c++ - 获取多态对象的大小

标签 c++ polymorphism sizeof crtp

我希望能够获得多态对象的大小。此刻我得到了这个:

struct Base {
    virtual std::size_t size() const {
        return sizeof(*this);
    }
};

struct Derived : Base {
    virtual std::size_t size() const {
        return sizeof(*this);
    }
};

这实际上是复制和粘贴。我想做得更好。假设我真的很讨厌宏,而 CRTP 似乎是唯一明智的方法。让我们试一试:

struct SizedBase {
    virtual std::size_t size() const = 0;
};

template <typename Type>
struct Sized : virtual SizedBase {
    std::size_t size() const override {
        return sizeof(Type);
    }
};

struct Base : Sized<Base> {}; 
struct Derived : Base, Sized<Derived> {};

这看起来好多了,但遗憾的是格式不正确:Derived包含 size() 的两个最终覆盖来自 Base来自 Sized<Derived> .我们可以通过通过继承来解决这个问题 Sized :

struct SizedBase {
    virtual std::size_t size() const = 0;
};

template <typename Type, typename... SizedBases>
struct Sized : virtual SizedBase, SizedBases... {
    std::size_t size() const override {
        return sizeof(Type);
    }
};

struct Base : Sized<Base> {}; 
struct Derived : Sized<Derived, Base> {}; 

这按预期工作,但是在多重继承的情况下会有些困惑,并且禁止更改基的可访问性/虚拟性。

那么,有没有更好的办法呢?

最佳答案

并不是说任何人都应该真正使用它,但是......

template <typename>
struct None1 {};
template <typename>
struct None2 {};

template <typename T>
struct PrivateBase { using Tpriv = T; using Tprot = None1<T>; using Tpub = None2<T>; };
template <typename T>
struct ProtectedBase { using Tpriv = None1<T>; using Tprot = T; using Tpub = None2<T>; };
template <typename T>
struct PublicBase { using Tpriv = None1<T>; using Tprot = None2<T>; using Tpub = T; };

template <typename K>
struct TriBase : private K::Tpriv, protected K::Tprot, public K::Tpub {};

template <typename T, typename ... Bases>
struct Sized : private Bases::Tpriv..., protected Bases::Tprot..., public Bases::Tpub...
{
    virtual size_t size() { return sizeof(T); }
};


struct Foo : Sized<Foo> {};

struct X{};
struct Y{};

struct Bar : Sized<Bar, PrivateBase<X>, ProtectedBase<Y>, PublicBase<Foo>> {};

int main ()
{
    Bar b;
    Foo* f = &b;
    X* x = &b; // error : private base
    Y* y = &b; // error : protected base
}

虚拟继承留给读者作为练习。

基类的顺序没有保留,但无论如何你都不应该依赖它。

可以像这样实现对生产更友好的东西(这是一个粗略的草图):

#include <cstdlib>
#include <typeinfo>
#include <unordered_map>
#include <memory>
#include <iostream>

struct myinfo
{
    size_t size;
    // any other stuff
};

using TypeInfoRef = std::reference_wrapper<const std::type_info>;
struct Hasher 
{
    std::size_t operator()(TypeInfoRef code) const
    {
        return code.get().hash_code();
    }
};

struct EqualTo 
{
    bool operator()(TypeInfoRef lhs, TypeInfoRef rhs) const
    {
        return lhs.get() == rhs.get();
    }
};

static std::unordered_map<TypeInfoRef, myinfo, Hasher, EqualTo> typemap;

template <typename K>
struct typemap_initializer
{
    typemap_initializer()
    {
        typemap[typeid(K)] = myinfo{sizeof(K)};
    }
};

struct Base
{
    virtual ~Base() {}
    size_t size() { return typemap[typeid(*this)].size; }
    template<typename K, typename... Arg>
        friend K* alloc(Arg...);
  private:
    void* operator new(size_t sz) { return ::operator new(sz); }
};

    template<typename K, typename... Arg>
K* alloc(Arg... arg)
{
    static typemap_initializer<K> ti;
    return new K(arg...);
}

struct Foo : Base {int a;};
struct Bar : Foo {int b; int c;};

int main ()
{
    Foo* f = alloc<Foo>();
    Bar* g = alloc<Bar>();

    std::cout << f->size() << std::endl;
    std::cout << g->size() << std::endl;
}

当然要放弃熟悉的Foo* foo = new Foo语法,但在无处不在的时代std::make_shared<>这不是什么大问题。

关于c++ - 获取多态对象的大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25961717/

相关文章:

c++ - 获取将在 Catch 中运行的部分

c++ - 在包含正整数和负整数的 vector 中找到最小的缺失整数?

c++ - 有没有办法像我的代码一样在C++中调用多态反射?

C++多态和虚函数

c - 获取指针数据的大小

c++ - 具有菱形继承结构的子类的大小似乎很奇怪

c++ - 与字符串文字不同,为什么具有单独字符的字符数组不以空终止符结尾?

c++ - 多个线程可以安全地同时向同一个变量写入相同的值吗?

c++ - 从字符串中给定结束索引的字符串复制子字符串

java - 涉及子类的多态性