使用基类指针查找派生类对象的大小

find size of derived class object using base class pointer

本文关键字:对象 派生 基类 指针 查找      更新时间:2023-10-16

在不知道派生类对象类型的情况下,是否有可能使用基类指针来查找派生类对象的大小?

谢谢。

没有直接的方法,但是可以编写一个虚拟的size()方法,子类可以实现。中间模板类可以自动完成这些繁琐的工作。

struct base {
  virtual size_t size() const =0;
  virtual ~base() { }
};
template<typename T> 
struct intermediate : base {
  virtual size_t size() const { return sizeof(T); }
};
struct derived : intermediate<derived> 
{ };

这确实要求你的层次结构是多态的…然而,基于对象的动态类型而不是静态类型的请求行为是多态行为定义的一部分。因此,这不会在一般用例中添加v表,因为至少您可能已经有了虚析构函数。

这个特殊的实现将你的继承树限制在一个单一的层次,而没有进入多重继承[即,从derived派生的类型将不会得到自己对size的覆盖]。有一个稍微复杂一点的变体可以解决这个问题。

struct base { /*as before */ };
template<typename Derived, typename Base>
struct intermediate : Base {
  virtual size_t size() const { return sizeof(Derived); }
};
struct derived : intermediate<derived, base>
{ };
struct further_derived : intermediate<further_derived, derived>
{ };
基本上,这将在层次结构的每个实际层之间插入一个intermediate,每个层用适当的行为覆盖size,并从实际的基类型派生。
//what you want
base >> derived 
     >> more_deriveder
     >> most_derivedest
//what you get
base >> intermediate<derived, base> 
     >> derived >> intermediate<more_deriveder, derived> 
     >> more_deriveder >> intermediate<most_derivedest, more_deriveder> 
     >> most_derivedest

几个mixin类型库使用了这样的方案,这样mixin就可以被添加到现有的层次结构中,而不需要引入多重继承。就我个人而言,我很少使用超过一层的继承,所以我不会为增加复杂性而烦恼,但是您的情况可能会有所不同。

我不认为这可以做到,因为sizeof在编译时类型上工作。可以在基类中定义一个虚拟的Size函数,并为每个派生类重写它。

由于c++中缺乏反射,对于任意类来说,这通常是不可能的。不过也有一些变通办法。您可以像其他人建议的那样编写一个virtual size()方法。你也可以使用奇怪的循环模板模式,也就是从Register<T>继承,但我不推荐它,vtable每个对象花费4字节,T的子类报告不正确的大小,纠正它会导致多重继承。

最好的方法是使用一个类来注册、存储和查询动态大小信息,而不修改你想要查询的类:

EDIT:事实证明,由于typeid的语义不一致,它仍然需要带有虚表的类,参见注释。

#include <cstddef>
#include <exception>
#include <iostream>
#include <map>
#include <typeinfo>
using namespace std;
class ClassNotFoundException
: public exception
{};
class Register
{
    public:
        template <class T>
        static void reg (T* = NULL)
        {
            //  could add other qualifiers
            v[&typeid(T)] = sizeof(T);
            v[&typeid(const T)] = sizeof(T);
            v[&typeid(T*)] = sizeof(T);
            v[&typeid(const T*)] = sizeof(T);
        }
        template <class T>
        static int getSize (const T& x)
        {
            const type_info* id = &typeid(x);
            if( v.find(id) == v.end() ){
                throw ClassNotFoundException();
            }
            return v[id];
        }
        template <class T>
        static int getSize (T* x)
        {
            return getSize(*x);
        }
        template <class T>
        static int getSize (const T* x)
        {
            return getSize(*x);
        }
    protected:
        static map<const type_info*, int> v;
};
map<const type_info*, int> Register::v;
class A
{
    public:
        A () : x () {}
        virtual ~A () {}
    protected:
        int x;
};
class B
: public A
{
    public:
        B() : y () {}
        virtual ~B () {}
    protected:
        int y;
};
int main ()
{
    Register::reg<A>();
    Register::reg<B>();
    A* a = new B();
    const A* b = new B();
    cout << Register::getSize(a) << endl;
    cout << Register::getSize(b) << endl;
}

考虑到@Dennis Zickefoose的好答案,有一种情况,你可以实现多层继承,既不需要虚函数,也不需要在每层继承之间使用中间类,从而增加了复杂性。

当继承层次结构中的所有中间(非叶子)类都是抽象类时,也就是说,它们没有被实例化。

如果是这种情况,您可以编写非叶抽象类模板化(再次)派生的具体类型。

下面的例子演示了这一点:

template <class TDerived>
class Shape     // Base
{
public:
    float centerX;
    float centerY;
    int getSize()
    { return sizeof(TDerived); }
    void demo()
    {
        std::cout
            << static_cast<TDerived*>(this)->getSize()
            << std::endl;
    }
};
class Circle : public Shape<Circle>
{
public:
    float radius;
};
class Square : public Shape<Square>
{
    // other data...
};
template <class TDerived>
class Shape3D : public Shape<TDerived>
    // Note that this class provides the underlying class the template argument
    //   it receives itself, and note that Shape3D is (at least conceptually)
    //   abstract because we can't directly instantiate it without providing it
    //   the concrete type we want, and because we shouldn't.
{
public:
    float centerZ;
};
class Cube : public Shape3D<Cube>
{
    // other data...
};
class Polyhedron : public Shape3D<Polyhedron>
{
public:
    typedef float Point3D[3];
    int numPoints;
    Point3D points[MAX_POINTS];
    int getSize()   // override the polymorphic function
    { return sizeof(numPoints) + numPoints * sizeof(Point3D); }
    // This is for demonstration only. In real cases, care must be taken about memory alignment issues to correctly determine the size of Polyhedron.
};


示例用法:

Circle c;
c.demo();
Polyhedron p;
p.numPoints = 4;
p.demo();