2013-07-04 2 views
3

Мне любопытно и применен оператор sizeof() на некоторых стандартных библиотечных классах C++. Вот что я заметил:sizeof() на стандартной библиотеке C++

int main() 
{ 
    vector<double> v1; 
    set<double> s1; 
    map<double,double> m1; 

    stack<char> st; 
    queue<char> q; 

    vector<char> v2; 
    set<char> s2; 
    map<char,char> m2; 

    cout<<sizeof(v1)<<" "<<sizeof(s1)<<" "<<sizeof(m1)<<endl; 
    cout<<sizeof(v2)<<" "<<sizeof(s2)<<" "<<sizeof(m2)<<endl; 
    cout<<sizeof(q)<<" "<<sizeof(st)<<endl; 
    return 0; 
} 

Выход на моей системе (64-разрядная версия):

12 24 24 
12 24 24 
40 40 

Я знаю, что std::set использует красно-черное дерево для реализации. Таким образом, каждый узел двоичного дерева имеет два указателя (по 8 байтов), а значение (8 байтов, всего 24) выглядит хорошо.

  1. std::map (также использует красно-черных деревьев) имеет дополнительный ключ, но еще 24 байт? Зачем?

  2. Почему std::queue и std::stack принимают 40 байт, а std::vector занимает всего 12 байт?

  3. Почему char и double не влияет на размер класса? Это из-за шаблонов?

+3

'sizeof' не является функцией, а является оператором и ключевым словом, определенным на языке C++. –

+0

@BasileStarynkevitch Я обновил вопрос – banarun

+2

Зачем вам ожидать 'sizeof (container) == sizeof (node)'? –

ответ

5

Оператор sizeof даст вам размер тип.

Теперь, если бы я сделал очень упрощенную версию std::vector<T> здесь (обратите внимание, что это не делает НИКАКОГО, как это делает реализация REAL, и это слишком упрощено, чтобы действительно работать - и я пропускаю много битов, которые вам действительно нужны в реальной):

template<typename T> 
class vector<T> 
{ 
    public: 
     typedef size_t size_type; 
    private: 
    T* mdata; 
    size_type msize; 
    size_type mreserved; 
    public: 
    vector() { mreserved = msize = 0; mdata = 0; } 
    vector(size_type sz) { msize = 0; mreserved = sz; mdata = new T[sz](); } 
    void push_back(const T& v) 
    { 
     if (msize + 1 > mreserved) grow(mreserved * 2); 
     mdata[msize+1] = v; 
     msize++; 
    } 
    size_type size() const { return msize; } 
    // bunch of other public functions go here. 
    private: 
    void grow(size_type newsize) 
    { 
     if (newsize < 8) newsize = 8; 
     T* newdata = new T[newsize]; 
     for(size_type i = 0; i < msize; i++) newdata[i] = mdata[i]; 
     swap(mdata, newdata); 
     delete [] mdata; 
     mreserved = newsize; 
    } 
    }; 

как вы можете видеть, размер фактического класса является идентичным (он содержит один и тот же размер и количество элементов) независимо от того, насколько большой набор сохраненных данных. Другими словами, sizeof(vector<int>) является постоянным. Размер данных, хранящихся за mdata, конечно меняется, но sizeof не знает (не знает), что.

6

Реализация этих классов - это черный ящик, нет способа сообщить, какие данные или частные члены они содержат. Это полностью зависит от реализации.

Единственный способ узнать, что все байты в экземпляре объекта, это прочитать исходный код.

+0

Разве sizeof() не определяет размер класса независимо от его личных или общественных интересов? – banarun

+0

@banarun: Да, это так. –

+1

@banarun Да, 'sizeof' возвращает размер всех членов, но вы не знаете, что они собой представляют. И только потому, что класс имеет размер 24 байта, это не значит, что у него есть 3 указателя. –

2

Важно помнить, что sizeofоценивается во время компиляции: так что это не так, в любом смысле, динамическое.

Его задача - вернуть размер класса/структуры/простых старых данных; ничего больше.

Именно поэтому он всегда дает те же результаты для ваших векторов, множеств и карт.

2

Вы, кажется, считаете, что размер класса контейнера может увеличиться с количеством потребляемых элементов, но это физически невозможно, так как все типы имеют фиксированный размер.

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

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

4

Этот образец исходного кода даст вам идею:

struct A 
{ 
    int* p; //4 bytes 
    A(int n) 
    { 
     p = new int[n]; 
    } 
}; 

int main() 
{ 
    A x1(10); 
    A x2(100); 
    cout << boolalpha << (sizeof(x1) == sizeof(x2)); //prints true 
} 

Причина заключается в том, что А содержит только указатель. Размер указателя всегда один и тот же (обычно 4). Не имеет значения, на что указывает указатель - динамический массив из 1000 или 1000000. Это все равно 4.

Char или double не влияет на размер, поскольку указатель на char имеет тот же размер, что и указатель на double.

Для станд :: массив, который на самом деле содержит массив, а не указатель, это будет иметь значение (как тип и размер массива):

cout << boolalpha << ((sizeof(std::array<int, 10>) == sizeof(std::array<int, 11>)); //false! 
cout << boolalpha << ((sizeof(std::array<int, 10>) == sizeof(std::array<long double, 10>)); //false! 
Смежные вопросы