Литмир - Электронная Библиотека
A
A

}

 

Программирование. Принципы и практика использования C++ Исправленное издание - _002.png
 Хорошо, интерфейс функции
poor()
очень плох, но можно ли рассматривать этот код с точки зрения встроенной системы; иначе говоря, следует ли беспокоиться о таких проблемах в приложениях, для которых важным является безопасность или производительность? Можем ли мы объявить этот код опасным при программировании обычных систем и просто сказать им: “Не делайте так”. Многие современные встроенные системы основаны на графическом пользовательском интерфейсе, который практически всегда организован в соответствии с принципами объектно-ориентированного программирования. К таким примерам относятся пользовательский интерфейс устройств iPod, интерфейсы некоторых мобильных телефонов и дисплеи операторов в системах управления полетами. Кроме того, контроллеры аналогичных устройств (например, множество электромоторов) образуют классические иерархии классов. Другими словами, этот вид кода — и, в частности, данный вид объявлений функции — вызывает особые опасения. Нам нужен более безопасный способ передачи информации о коллекциях данных, который не порождал бы значительных проблем.

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Итак, мы не хотим передавать функциям встроенные массивы с помощью указателей и размера массива. Чем это заменить? Проще всего передать ссылку на контейнер, например, на объект класса vector. Проблема, которая возникла в связи с интерфейсом функции

void poor(Shape* p, int sz);

исчезает при использовании функции

void general(vector<Shape>&);

Если вы программируете систему, в которой допускаются объекты класса

std::vector
(или его эквиваленты), то просто последовательно используйте в интерфейсах класс
vector
(или его эквиваленты) и никогда не передавайте встроенный массив с помощью указателя и количества элементов.

Если вы не можете ограничиться использованием класса

vector
или его эквивалентов, то оказываетесь на территории, где не бывает простых решений, — даже несмотря на то, что использование класса (
Array_ref
) вполне очевидно.

25.4.3. Решение: интерфейсный класс

К сожалению, во многих встроенных системах мы не можем использовать класс

std::vector
, потому что он использует свободную память. Мы можем решить эту проблему, либо предложив особую реализацию класса
vector
, либо (что более просто) используя контейнер, напоминающий класса
vector
, но не содержащий его механизма управления памятью. Прежде чем описать такой интерфейсный класс, перечислим его желательные свойства.

 

Программирование. Принципы и практика использования C++ Исправленное издание - _002.png
 • Он ссылается на объекты в памяти (он не владеет объектами, не размещает их, не удаляет и т.д.).

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

  • Он знает точный тип своих элементов (а значит, не может порождать ошибки, связанные с типами).

  • Его несложно передать (скопировать) как пару (указатель, счетчик).

  • Его нельзя неявно преобразовать в указатель.

  • Он позволяет легко выделить поддиапазон в целом диапазоне.

  • Его легко использовать как встроенный массив.

Свойство “легко использовать как встроенный массив” можно обеспечить лишь приблизительно. Если бы мы сделали это совершенно точно, то вынуждены были бы смириться с ошибками, которых стремимся избежать.

Рассмотрим пример такого класса.

template<class T>

class Array_ref {

public:

  Array_ref(T* pp, int s) :p(pp), sz(s) { }

  T& operator[ ](int n) { return p[n]; }

  const T& operator[ ](int n) const { return p[n]; }

  bool assign(Array_ref a)

  {

    if (a.sz!=sz) return false;

    for (int i=0; i<sz; ++i) { p[i]=a.p[i]; }

    return true;

  }

  void reset(Array_ref a) { reset(a.p,a.sz); }

  void reset(T* pp, int s) { p=pp; sz=s; }

  int size() const { return sz; }

  // операции копирования по умолчанию:

  // класс Array_ref не владеет никакими ресурсами

  // класс Array_ref имеет семантику ссылки

private:

  T* p;

  int sz;

};

Класс

Array_ref
близок к минимальному.

• В нем нет функций

push_back()
(для нее нужна динамическая память) и
at()
(для нее нужны исключения).

• Класс Array_ref имеет форму ссылки, поэтому операция копирования просто копирует пары (

p, sz
).

• Инициализируя разные массивы, можем получить объекты класса

Array_ref
, которые имеют один и тот же тип, но разные размеры.

• Обновляя пару (

p, size
) с помощью функции
reset()
, можем изменить размер существующего класса
Array_ref
(многие алгоритмы требуют указания поддиапазонов).

• В классе

Array_ref
нет интерфейса итераторов (но при необходимости этот недостаток легко устранить). Фактически концепция класса
Array_ref
очень напоминает диапазон, заданный двумя итераторами.

Класс

Array_ref
не владеет своими элементами и не управляет памятью, он просто представляет собой механизм для доступа к последовательности элементов и их передачи функциям. Иначе говоря, он отличается от класса
array
из стандартной библиотеки (см. раздел 20.9).

Для того чтобы облегчить создание объектов класса

Array_ref
, напишем несколько вспомогательных функций.

template<class T> Array_ref<T> make_ref(T* pp, int s)

{

  return (pp) ? Array_ref<T>(pp,s):Array_ref<T>(0,0);

353
{"b":"847443","o":1}