Подтвердить что ты не робот

Найти размер объекта производного класса с помощью указателя базового класса

Можно ли найти размер объекта производного класса с использованием указателя базового класса, если вы не знаете производного типа.

Спасибо.

4b9b3361

Ответ 1

Нет прямого пути, но вы можете написать виртуальный 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 используют такую ​​схему, так что микшины могут быть добавлены в существующую иерархию без введения множественного наследования. Лично я редко использую более одного уровня наследования, поэтому я не беспокоюсь о добавленной сложности, но ваш пробег может отличаться.

Ответ 2

Я не думаю, что это можно сделать, потому что sizeof работает с типами времени компиляции. Вы можете определить виртуальную функцию Size в базовом классе и переопределить ее для каждого производного класса.

Ответ 3

Из-за отсутствия отражения в С++ это вообще не возможно с произвольными классами по прихоти. Однако есть некоторые обходные пути. Вы можете написать метод virtual size(), как предложили другие. Вы также можете использовать шаблон Curiously Recurring Template, который также наследуется от Register<T>, но я бы не рекомендовал его, vtable стоит 4 байта на объект, подклассы T сообщают неправильный размер и исправление его приводит к множественному наследованию.

Лучший способ - использовать класс для регистрации, хранения и запроса информации о динамическом размере без изменения класса, который вы хотите запросить:

EDIT. Как оказалось, из-за несогласованной семантики typeid ему все еще нужны классы с vtables, см. комментарии.

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

Ответ 4

Учитывая хороший ответ @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();