Программирование >>  Обобщенные обратные вызовы 

1 ... 40 41 42 [ 43 ] 44 45 46 ... 84


Таблица 21.3. Реальные расходы памяти на хранение объекта, рассматриваемые в предположении, что sizeof (string) == 16, указатели и int имеют размер 4 байта, и выделение памяти происходит 16-байтовыми блоками

Контейнер

Теоретический размер данных узла

Реальный размер блока выделенной для узла памяти (с учетом выравнивания и накладных расходов при вьшелении памяти)

list<char>

9 байтов

16 байтов

set<char>, mu1tiset<char>

13 байтов

16 байтов

list<int>

12 байтов

16 байтов

set<int>, multiset<int>

16 байтов

16 байтов

1ist<stri ng>

24 байта

32 байта

set<string>, multiset<stri ng>

28 байтов

32 байта

В табл. 21.3 приведены результаты простого анализа с использованием рассмотренных выше предположений. Вы можете повторить эксперимент на своей платформе со своим компилятором - просто подставив собственные значения. Чтобы написать программу, которая определяет реальные накладные расходы при выделении блока определенного размера на вашей платформе, обратитесь к приложению 3 в книге Джона Бентли (Jon Bcntlcy) [BentleyOO].

Благодаря табл. 21.3 мы немедленно обнаруживаем один интересный результат: во многих случаях - примерно для 75% всех возможных размеров типа т - накладные расходы при использовании 1 i st и set/mul ti set в pace м атр и ва с м о й среде оказываются одинаковы. Более того, вот результат, который еще интереснее, - list<char> и set<int> имеют в данной среде одни и тс же реальные накладные расходы, несмотря на то, что последний контейнер хранит данные большего размера и требует большего количества дополнительной информации для каждого узла.

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

Резюме

Для каждого вида контейнера определяются свои компромиссы между расходами памяти и производительностью. При использовании vector и set можно быстро решить те задачи, которые невозможно столь же эффективно решить при использовании 1 ist - например, поиск за время 0(log N); при использовании vector можно сделать веши, невозможные при применении list или set - например, произвольный доступ. Вставка элемента в средину легко выполняется в 1 i st, менее эффективно-в set, и очень медленно - в vector. Словом, примеров таких по-разному выполняющихся задач очень много. Большая гибкость зачастую требует больших накладных расходов памяти, но если учесть выравнивание данных и возможные стратегии распределения памяти, то различие может оказаться куда меньшим, чем вы думаете! Вопросы выравнивания данных и оптимизации использования памяти рассматривались также в книге [SutterOO].

Если содержимое вектора отсортировано. Задача 21. Контейнеры в памяти. Часть 2: какие они на самом деле?



> Рекомендация

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



Задача 22, Новый взгляд на new.

Часть 1: многоликий оператор new Сложность: 4

Любой класс, предоставляющий собственный оператор new или new[], должен также обеспечить соответствующие версии обычного оператора new, размещающего new и new, не генерирующего исключений. В противном случае у пользователей вашего класса могут возникнуть ненужные проблемы.

Вопрос ДЛЯ новичка

1. Какие три варианта оператора new описаны в стандарте С++? Вопрос для профессионала

2. Что такое оператор new, специфичный для класса, и как им следует пользоваться? Опишите, в каких случаях вы должны быть особо осторожны при предоставлении собственных, специфичных для класса операторов new и delete.

3. Какой именно оператор new вызывается в приведенном далее коде в строках, пронумерованных от 1 до 4?

class Base {

public:

static void* operator new(std::si2e t, const FastMemoryA);

class Derived : public Base {

...

Derived* pi = new Derived; 1

Derived* p2 = new (std::nothrow) Derived; 2

void* p3 = /* некоторая область памяти, достаточная

для размещения Derived */ ; new (рЗ) Derived; 3

FastMemory f;

Derived* p4 = new (f) Derived; 4

Решение

в этой и следующей задачах я хочу сформулировать и обосновать два основных совета.

Любой класс, предоставляющий собствснный оператор new или new[], должен также обеспечить соответствующие версии обычного оператора new, размещающего new и new, не генерирующего исключений. В противном случае у пользователей вашего класса могут возникнуть ненужные проблемы.

Избегайте использования new(nothrow) и убедитесь, что, когда вы проверяете отказ new, вы действительно проверяете именно то, что хотите проверить.

Советы могут показаться неожиданными, поэтому давайте детально их проанализируем. Для простоты я не упоминаю отдельно оператор new для массивов, но все сказанное об операторе new для одного объекта, относится и к нему.



1 ... 40 41 42 [ 43 ] 44 45 46 ... 84

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика