Russian Qt Forum

Программирование => С/C++ => Тема начата: Eten от Апрель 21, 2013, 15:44



Название: Трудности с подсчетом байтов памяти занимаемыми экземпляром класса.
Отправлено: Eten от Апрель 21, 2013, 15:44
Всем привет и доброго здравия.  ;)

Когда замерял с помощью sizeof свой класс, который хочется сжать по короче, то заметил, что с 40 байт получилось только 28 байт, переведя числовые переменные в указатели и строковые также. Булевная переменная занимает 1 байт, указатель 4 байта, перечисление тоже 4 байта.

Посчитал вручную получается 18 байтов (с учетом от базовых). А откуда 10 байтов выросли никак не пойму.

Пока пробовал сам разобраться понял, что функции и конструкторы на кол-во занимаемой памяти никак  не влияют, даже static и virtual (пробовал через комментирование).

Народ, можете просветить меня в том, что упустил из внимания? Что влияет на кол-во памяти, кроме переменных хранящихся внутри класса и являющихся его членами?!

З.Ы.
Разумеется сам класс вместе с его базовыми выкладываю ниже.

Классы, которые для него базовые:
Код:
#include <QString>

enum RFGroupDataTypes {
    eRFSimpleDataTypes,
    eRFArea
};

class IRFDataTypes
{
public:
    virtual ~IRFDataTypes() { }

    virtual qint32 id() const = 0;
    virtual void setId(qint32 id) = 0;
    virtual QString name() const = 0;
    virtual void setName(QString name) = 0;

    virtual RFGroupDataTypes groupDataTypes() const = 0;

protected:
    //если идентификатор равен -1, значит это либо временная переменная, либо константное значение
    qint32 *_id;
    //константные или временные значения имен не имеют, только переменные.
    QString *_name;
    //указание на принадлежность к группе типов данных.
    RFGroupDataTypes _groupdatatypes;
};

//Интерфейс простых типов данных
class IRFSimpleDataTypes : public IRFDataTypes
{
public:
    virtual QString valueToQString() const = 0;

protected:
    //это указывает на константное значение
    bool _constValue;
};

class RFSimpleDataTypes : public IRFSimpleDataTypes
{
public:
    RFSimpleDataTypes()
    {
        _id = new qint32(-1); _name = new QString("");
        _constValue = false;
        _groupdatatypes = eRFSimpleDataTypes;
    }

    qint32 id() const {return *_id;}
    void setId(qint32 id) {*_id = id;}
    QString name() const {return *_name;}
    void setName(QString name) {if (name != NULL) *_name = name; else *_name = "";}

    RFGroupDataTypes groupDataTypes() const {return _groupdatatypes;}

    bool constValue() const {return _constValue;}
    void setConstValue(bool constValue) {_constValue = constValue;}
};


Сам класс:
Код:
#include <limits>
#include <cmath>
#include <time.h>



//Основа Чисел (или просто Числовой)
class RFNumeric : public RFSimpleDataTypes
{
public:

    RFNumeric(double value = 0)
        : RFSimpleDataTypes()
    {
        _isTypeInteger = false;

        _value = new double;

        setValue(value);

    }

    RFNumeric(double value, bool isTypeInteger)
            : RFSimpleDataTypes()
    {
        setisTypeInteger(isTypeInteger);

        _value = new double;

        setValue(value);
    }


    QString valueToQString() const
    {
       int decpt, sign;

       QString s(fcvt(*_value, 18, &decpt, &sign));

       if (decpt > 0 && *_value < 0.0 ? *_value - ceil(*_value) : *_value - floor(*_value) == 0.0)
       {//здесь число без цифр отличных от нуля после десятичной запятой
           s.remove(decpt, s.length() - decpt);
       }
       else
       {//наоборот
           if (decpt > 0)
                s = s.insert(decpt,'.');
           else
           {
                QString s2 = "0.";
                for (int i = 0; i < decpt * (-1); i++) s2 += '0';
                s.insert(0, s2);
           }

           bool isNumberNull = true;

           while(isNumberNull)
           {
               if (s[s.length() - 1] == '0')
               {
                   s.remove(s.length() - 1, 1);
                   isNumberNull = true;
               }
               else
               {
                   isNumberNull = false;
               }
           }
       }

       //после преобразования числа в строку, проверяем его на отрицательность
       if (sign != 0)
            s = s.insert(0, '-');

       return s;
    }



    //целочисленное деление
    static RFNumeric rfnDiv(RFNumeric a, RFNumeric b)
    {
        return RFNumeric(ldiv(qint32(a), qint32(b)).quot, true);
    }

    //% (остаток от целочисленного деления)
    static RFNumeric rfnMod(RFNumeric a, RFNumeric b)
    {
        return RFNumeric(ldiv(qint32(a), qint32(b)).rem, true);
    }

    //возведение в степень
    static RFNumeric rfnPow(RFNumeric a, RFNumeric b)
    {
        return RFNumeric(pow(double(a), double(b)), a.isTypeInteger() && b.isTypeInteger());
    }

    //квадратный корень
    static RFNumeric rfnSqrt(RFNumeric a)
    {
        return RFNumeric(sqrt(double(a)), a.isTypeInteger());
    }

    //модуль числа
    static RFNumeric rfnAbs(RFNumeric a)
    {
        return RFNumeric(std::abs(double(a)), a.isTypeInteger());
    }

    //минимальное число
    static RFNumeric rfnMin(RFNumeric a, RFNumeric b)
    {
        double d1 = double(a), d2 = double(b);
        return d1 < d2 ? RFNumeric(d1, a.isTypeInteger()) : RFNumeric(d2, b.isTypeInteger());
    }

    //максимальное число
    static RFNumeric rfnMax(RFNumeric a, RFNumeric b)
    {
        double d1 = double(a), d2 = double(b);
        return d1 > d2 ? RFNumeric(d1, a.isTypeInteger()) : RFNumeric(d2, b.isTypeInteger());
    }

    //десятичный логарифм
    static RFNumeric rfnLog10(RFNumeric a)
    {
        return RFNumeric(log10(double(a)));
    }

    //натуральный логарифм
    static RFNumeric rfnLog(RFNumeric a)
    {
        return RFNumeric(log(double(a)));
    }

    //экспонента в степени "a"
    static RFNumeric rfnExp(RFNumeric a)
    {
        return RFNumeric(exp(double(a)));
    }

    //косинус
    static RFNumeric rfnCos(RFNumeric a)
    {
        return RFNumeric(cos(double(a)));
    }

    //синус
    static RFNumeric rfnSin(RFNumeric a)
    {
        return RFNumeric(sin(double(a)));
    }

    //тангенс
    static RFNumeric rfnTan(RFNumeric a)
    {
        return RFNumeric(tan(double(a)));
    }

    //арктангенс
    static RFNumeric rfnCtan(RFNumeric a)
    {
        return RFNumeric(1.0 / tan(double(a)));
    }

    //арккосинус
    static RFNumeric rfnACos(RFNumeric a)
    {
        return RFNumeric(acos(double(a)));
    }

    //арксинус
    static RFNumeric rfnASin(RFNumeric a)
    {
        return RFNumeric(asin(double(a)));
    }

    //арктангенс
    static RFNumeric rfnATan(RFNumeric a)
    {
        return RFNumeric(atan(double(a)));
    }

    //арккатангенс
    static RFNumeric rfnACtan(RFNumeric a)
    {
        return RFNumeric(1.0 / atan(double(a)));
    }

    //округление в сторону нуля до целого
    static RFNumeric rfnFloor(RFNumeric a)
    {
        return RFNumeric(floor(double(a)));
    }

    //округление в большую сторону до целого
    static RFNumeric rfnCeil(RFNumeric a)
    {
        return RFNumeric(ceil(double(a)));
    }

    //округление до целого
    static RFNumeric rfnRound(RFNumeric a)
    {
        return RFNumeric(floor(double(a) + 0.5));
    }

    //выделение дробной части
    static RFNumeric rfnFractional(RFNumeric a)
    {
        double d = double(a);
        return d < 0.0 ? RFNumeric(d - ceil(d)) : RFNumeric(d - floor(d));
    }

    //отсечение дробной части
    static RFNumeric rfnInteger(RFNumeric a)
    {
        double d = double(a);
        return d < 0.0 ? RFNumeric(ceil(d)) : RFNumeric(floor(d));
    }

    //целочисленный рандом от 0 и до a-1
    static RFNumeric rfnRandom(RFNumeric a)
    {
        srand ( time(NULL) );
        return RFNumeric(rand() % qint32(a), false);
    }

    //рандом от 0 до 1
    static RFNumeric rfnRnd()
    {
        srand ( time(NULL) );
        return RFNumeric(double(rand() % 1001)/1000.0);
    }


    RFNumeric operator+(RFNumeric b)
    {
        return RFNumeric(this->value() + b.value(), this->isTypeInteger() && b.isTypeInteger());
    }

    RFNumeric operator-(RFNumeric b)
    {
        return RFNumeric(this->value() - b.value(), this->isTypeInteger() && b.isTypeInteger());
    }

    RFNumeric operator*(RFNumeric b)
    {
        return RFNumeric(this->value() * b.value(), this->isTypeInteger() && b.isTypeInteger());
    }

    RFNumeric operator/(RFNumeric b)
    {
        return RFNumeric(this->value() / b.value(), this->isTypeInteger() && b.isTypeInteger());
    }

    void operator++(int)
    {
        if (isTypeInteger())
            *_value = checkingInt32Borders(*_value + 1);
        else
            *_value = checkingFloatBorders(*_value + 1);
    }

    void operator--(int)
    {
        if (isTypeInteger())
            *_value = checkingInt32Borders(*_value - 1);
        else
            *_value = checkingFloatBorders(*_value - 1);
    }

    bool operator==(RFNumeric b)
    {
        return this->value() == double(b);
    }

    bool operator!=(RFNumeric b)
    {
        return this->value() != double(b);
    }

    bool operator<=(RFNumeric b)
    {
        return this->value() <= double(b);
    }

    bool operator>=(RFNumeric b)
    {
        return this->value() >= double(b);
    }

    bool operator<(RFNumeric b)
    {
        return this->value() < double(b);
    }

    bool operator>(RFNumeric b)
    {
        return this->value() > double(b);
    }

    operator QString() {return valueToQString();}

    operator double() {return checkingFloatBorders(value());}

    operator qint32() {return checkingInt32Borders(*_value);}

    //оператор ниже позволяет передать указателю значения из экземпляра класса
    operator RFNumeric*()
    {
        RFNumeric* prfn = new RFNumeric();

        prfn->setId(id());
        prfn->setConstValue(constValue());
        prfn->setName(name());
        prfn->setisTypeInteger(isTypeInteger());
        prfn->setValue(value());

        return prfn;
    }


private:

    long double int32Max() const {return 2147483647;}

    long double int32Min() const {return -2147483647;}

    long double floatMax() const {return 2.147483647e9;}

    long double floatMin() const {return 2.147483647e-9;}

    double checkingFloatBorders (double value) const
    {
        //В этой функции +/-inf приравниваются к max и min границам.
        double d = fabs(value);
        bool usign = value >= 0.0;

        if (d >= floatMax())
            return (usign) ? floatMax() : -floatMax();

        if (d <= floatMin() && d > 0.0)
            return (usign) ? floatMin() : -floatMin();

        return value;
    }

    qint32 checkingInt32Borders(double value) const
    {
        if (value >= int32Max()) return int32Max();

        if (value <= int32Min()) return int32Min();

        return qint32(value);
    }


    void setisTypeInteger(bool isTypeInteger) {_isTypeInteger = isTypeInteger;}

    //Если истинно, то значение принимается и выдается (при печати числа на экране)
    // с отсечение дробной части числа и соблюдением границ типа qint32.
    bool isTypeInteger() const {return _isTypeInteger;}

    void setValue(double value)
    {
        if (isTypeInteger())
            value = checkingInt32Borders(value);
        else
            value = checkingFloatBorders(value);

        *_value = value;
    }

    double value() const {return checkingFloatBorders(*_value);}

    double *_value;

    bool _isTypeInteger;
};



Название: Re: Трудности с подсчетом байтов памяти занимаемыми экземпляром класса.
Отправлено: Igors от Апрель 21, 2013, 16:48
Во-первых
Цитировать
Размер структуры НЕ равен сумме размеров всех ее членов.
Есть выравнивание (alignment) для членов. Напр адрес члена типа int будет равняться на 4 (если align 4 и выше)

Во-вторых, если есть хоть 1 virtual добавляется "указатель на VMT" - как бы личные данные копилятора используемые для вызова виртуальных методов.


Название: Re: Трудности с подсчетом байтов памяти занимаемыми экземпляром класса.
Отправлено: Bepec от Апрель 21, 2013, 16:56
И добавим гениальную систему умных хранилищ Qt. Где любая строка лишь указатель на место хранения структуры (неточная информация, либо информация описанная не теми словами, см. PS). А если добавляем пару ваших указателей, то  размер вашего класса sizeOf неподвластен :D

PS точно не помню, но когда то пытался узнать сколько занимает QString :D Оказалось всегда 4 байта :D А вот если получить указатель на char, то намного больше.

PPS чтоб получить полный размер вашего класса, нужно тогда пройтись по всем членам класса, имеющим указатели (или являющиеся указателями), сложить размер их объектов (так же возможна ситуация аля указатель на указатель на указатель :D ) и добавить размер вашего класса.


Название: Re: Трудности с подсчетом байтов памяти занимаемыми экземпляром класса.
Отправлено: Eten от Апрель 21, 2013, 20:15
Хм, спасибо. Видимо, пока не по зубам мне это. Будем расти, может позднее получится.  ;)