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

时间:2011-10-04 02:09:23

标签: c++ inheritance

当您不知道派生类型时,是否可以使用基类指针查找派生类对象的大小。

谢谢。

4 个答案:

答案 0 :(得分:27)

没有直接的方法,但你可以编写一个虚拟的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可以添加到现有的层次结构中,而不会引入多重继承。就个人而言,我很少使用超过一个级别的继承,所以我不会为增加的复杂性而烦恼,但你的里程可能会有所不同。

答案 1 :(得分:3)

我不认为可以这样做,因为sizeof适用于编译时类型。您可以在基类中定义虚拟Size函数,并为每个派生类重写它。

答案 2 :(得分:0)

由于C ++中缺乏反射,一般情况下任意类都不可能实现这一点。但是有一些解决方法。您可以像其他人建议的那样编写虚拟size()方法。你也可以使用奇怪的重复模板模式,也就是继承Register<T>,但我不推荐它,vtable每个对象花费4个字节,T的子类报告错误的大小并纠正它导致多重继承。< / p>

最好的方法是使用类来注册,存储和查询动态大小信息,而无需修改要查询的类:

编辑:事实证明,由于typeid的语义不一致,它仍然需要带有vtable的类,请参阅注释。

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

答案 3 :(得分:0)

考虑到@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();