获取多态对象的大小

Get size of polymorphic object

本文关键字:对象 多态 获取      更新时间:2023-10-16

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

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包含BaseSized<Derived>size()的两个最终重写器。我们可以通过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<>时代,这并不是什么大问题。