Чтение онлайн

на главную - закладки

Жанры

Программирование. Принципы и практика использования C++ Исправленное издание
Шрифт:

// если свободной памяти нет,

// возвращает 0

void free; // возвращает последнее значение,

// возвращенное функцией get

int available const; // количество доступных байтов

private:

// память для char[N] и данные, позволяющие определить, какие

// объекты извлечены из стека, а какие нет (например,

// указатель на вершину)

};

Поскольку функция

get
возвращает указатель
void*
, ссылающийся на требуемое количество байтов, мы должны конвертировать эту память в тип, требуемый для наших объектов. Этот стек можно использовать, например, так.

Stack<50*1024> my_free_store; // 50K памяти используется как стек

void* pv1 = my_free_store.get(1024);

int* buffer = static_cast<int*>(pv1);

void* pv2 = my_free_store.get(sizeof(Connection));

Connection* pconn = new(pv2) Connection(incoming,outgoing,buffer);

Использование оператора

static_cast
описано в разделе 17.8. Конструкция
new(pv2)
называется синтаксисом размещения. Она означает следующее: “Создать объект в ячейке памяти, на которую ссылается указатель
pv2
”. Сама по себе эта конструкция не размещает в памяти ничего. Предполагается, что в классе Connection есть конструктор со списком аргументов (
incoming,outgoing,buffer
). Если это условие не выполняется, то программа не скомпилируется.

Естественно, наш шаблонный класс

Stack
представляет собой всего лишь один из вариантов общей идеи о стеке. Например, если ограничения на использование памяти не такие строгие, то мы можем определить стек, в котором количество доступных байтов задается конструктором.

25.4. Адреса, указатели и массивы

Предсказуемость требуется в некоторых встроенных системах, а надежность — во всех. Это заставляет нас избегать некоторых языковых конструкций и методов программирования, уязвимых для ошибок (в контексте программирования встроенных систем). В языке С++ основным источником проблем является неосторожное использование указателей.

Выделим две проблемы.

• Явные (непроверяемые и опасные) преобразования.

• Передача указателей на элементы массива.

Первую проблему можно решить, строго ограничив использование явных преобразований типов (приведения). Проблемы, связанные с указателями и массивами, имеют более тонкие причины, требуют понимания и лучше всего решаются с помощью (простых) классов или библиотечных средств (например, класса array; см. раздел 20.9). По этой причине в данном разделе мы сосредоточимся на решении второй задачи.

25.4.1. Непроверяемые преобразования

Физические ресурсы (например, регистры контроллеров во внешних устройствах) и их основные средства управления в низкоуровневой системе имеют конкретные адреса. Мы должны указать эти адреса в наших программах и присвоить этим данных некий тип. Рассмотрим пример.

Device_driver* p = reinterpret_cast<Device_driver*>(0xffb8);

Эти преобразования описаны также в разделе 17.8. Именно этот вид программирования требует постоянного использования справочников. Между ресурсом аппаратного обеспечения — адресом регистра (выраженного в виде целого числа, часто шестнадцатеричного) —

и указателями в программном обеспечении, управляющим аппаратным обеспечением, существует хрупкое соответствие. Вы должны обеспечить его корректность без помощи компилятора (поскольку эта проблема не относится к языку программирования). Обычно простой (ужасный, полностью непроверяемый) оператор
reinterpret_cast
, переводящий тип
int
в указатель, является основным звеном в цепочке связей между приложением и нетривиальными аппаратными ресурсами.

Если явные преобразования (

reinterpret_cast
,
static_cast
и т.д.; см. раздел A.5.7) не являются обязательными, избегайте их. Такие преобразования (приведения) бывают необходимыми намного реже, чем думают программисты, работающие в основном на языках C и C++ (в стиле языка С).

25.4.2. Проблема: дисфункциональный интерфейс

Как указывалось в разделе 18.5.1, массив часто передается функции как указатель на элемент (часто как указатель на первый элемент). В результате он “теряет” размер, поэтому получающая его функция не может непосредственно определить количество элементов, на которые ссылается указатель. Это может вызвать много трудноуловимых и сложно исправимых ошибок. Здесь мы рассмотрим проблемы, связанные с массивами и указателями, и покажем альтернативу. Начнем с примера очень плохого интерфейса (к сожалению, встречающегося довольно часто) и попытаемся его улучшить.

void poor(Shape* p, int sz) // плохой проект интерфейса

{

for (int i = 0; i<sz; ++i) p[i].draw;

}

void f(Shape* q, vector<Circle>& s0) // очень плохой код

{

Polygon s1[10];

Shape s2[10];

// инициализация

Shape* p1 = new Rectangle(Point(0,0),Point(10,20));

poor(&s0[0],s0.size); // #1 (передача массива из вектора)

poor(s1,10); // #2

poor(s2,20); // #3

poor(p1,1); // #4

delete p1;

p1 = 0;

poor(p1,1); // #5

poor(q,max); // #6

}

Функция
poor
представляет собой пример неудачной разработки интерфейса: она дает вызывающему модулю массу возможностей для ошибок и не оставляет никаких надежд защититься от них на этапе реализации.

ПОПРОБУЙТЕ

Прежде чем читать дальше, попробуйте выяснить, сколько ошибок вы можете найти в функции

f
? В частности, какой из вызовов функции
poor
может привести к краху программы?

На первый взгляд данный вызов выглядит отлично, но это именно тот вид кода, который приносит программистам бессонные ночи отладки и вызывает кошмары у инженеров по качеству.

1. Передается элемент неправильного типа (например,

poor(&s0[0],s0.size
). Кроме того, вектор
s0
может быть пустым, а в этом случае выражение
&s0[0]
является неверным.

Поделиться:
Популярные книги

Возрождение Феникса. Том 2

Володин Григорий Григорьевич
2. Возрождение Феникса
Фантастика:
фэнтези
попаданцы
альтернативная история
6.92
рейтинг книги
Возрождение Феникса. Том 2

Вперед в прошлое 3

Ратманов Денис
3. Вперёд в прошлое
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Вперед в прошлое 3

Сумеречный Стрелок 4

Карелин Сергей Витальевич
4. Сумеречный стрелок
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Сумеречный Стрелок 4

Аномалия

Юнина Наталья
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Аномалия

Весь цикл «Десантник на престоле». Шесть книг

Ланцов Михаил Алексеевич
Десантник на престоле
Фантастика:
альтернативная история
8.38
рейтинг книги
Весь цикл «Десантник на престоле». Шесть книг

Диверсант

Вайс Александр
2. Фронтир
Фантастика:
боевая фантастика
космическая фантастика
5.00
рейтинг книги
Диверсант

Кодекс Охотника. Книга XVI

Винокуров Юрий
16. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Охотника. Книга XVI

Дело Чести

Щукин Иван
5. Жизни Архимага
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Дело Чести

Граф

Ланцов Михаил Алексеевич
6. Помещик
Фантастика:
альтернативная история
5.00
рейтинг книги
Граф

Я Гордый часть 2

Машуков Тимур
2. Стальные яйца
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я Гордый часть 2

Начальник милиции 2

Дамиров Рафаэль
2. Начальник милиции
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Начальник милиции 2

Авиатор: назад в СССР 10

Дорин Михаил
10. Покоряя небо
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Авиатор: назад в СССР 10

Курсант: Назад в СССР 11

Дамиров Рафаэль
11. Курсант
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Курсант: Назад в СССР 11

Адепт. Том 1. Обучение

Бубела Олег Николаевич
6. Совсем не герой
Фантастика:
фэнтези
9.27
рейтинг книги
Адепт. Том 1. Обучение