Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Штерн В. - Основы C++. Методы программной инженерии - 2003

.pdf
Скачиваний:
267
Добавлен:
13.08.2013
Размер:
28.32 Mб
Скачать

I 830 I

Часть IV # Расширенное использование С-^Ф

Указатель двойной длины pd в состоянии осуществить доступ и изменить элемент данных объекта Account только потому, что адрес Account может ис­ пользоваться как операнд стандартного приведения. Если применяется операция static_cast, сделать это невозможно.

Account a1 ( "Jones",1122, 5000);

// создать объект

pd - (double*)(&a1);

//ok

*pd = 10000;

//ok

pd = static_cast<double*>(&a1);

/ / синтаксическая ошибка

Это не означает, что операция static_cast не может использоваться с указателя­ ми. Она не может применяться с указателями, преобразование которых не имеет смысла с точки зрения программной инженерии. Рассмотрим, например, класс SavingsAccount, открыто порожденный из класса Account.

class SavingsAccount : public Account

/ / фиксированная процентная ставка

double rate;

 

 

public:

 

 

 

SavingsAccount(const

char* name, int id, double bal)

: Account(name,

id, bal),

rate

(6.0) { }

void paylnterestO

 

 

/ / платить раз в месяц

{ balance += balance

* rate /

12 /

100; }

} ;

 

 

 

Объекты SavingsAccount могут выполнить все то же, что и объекты Account. Кроме того, они содержат больше элементов данных и больше функций-членов. Следовательно, указатель Account может ссылаться на объект SavingsAccount без каких-либо затруднений. Это безопасно и не требует какого-либо приведения, стандартного или любого другого.

Account

alC'Jones",1122,5000);

/ / создать объекты

SavingsAccount a2("SmithM133, 3000);

 

Account

*ра = &а2;

/ / сохранить преобразование, приведение не требуется

Указатель SavingsAccount не должен обозначать объект Account, потому что он может отправить объекту сообш,ение, на которое базовый объект, возможно, будет не в состоянии ответить.

SavingsAccount *psa - pa;

/ / синтаксическая ошибка

Конечно, если указатель Account фактически ссылается на объект SavingsAccount, то присваивание (преобразование) имеет смысл. Однако сообщите об этом с помош,ью приведения.

psa = (SavingsAccount *)ра;

/ / явное приведение все сделало

Это именно та ситуация, в которой операция static_cast использует свое непри­ ятие указателей. Она может использоваться для данного преобразования вместо стандартного приведения.

psa = static_cast<SavingsAccount*>(pa)

/ / это просто замечательно

Операция static_cast может использоваться в тех ситуациях, где преобразо­ вание небезопасно. Здесь, например, указатель SavingsAccount устанавливается так, что ссылается на объект Account, а операция static_cast не согласна со способом выполнения операции стандартного приведения.

psa = static_cast<SavingsAccount*>(&a1);

/ / это просто замечательно

Обобщим преимущества этого приведения перед стандартным приведением. Во-первых, оно состоит из нескольких слов, благодаря чему его легче заметить в программе. Во-вторых, оно более требовательно, чем стандартные приведения.

Глава 18 • Программирование с обработкой исключительных ситуаций

831

Бьерн Страуструп, автор C++, говорил, что чем реже используются приведения, тем лучше, и все мешаюидее применению приведения является полезным.

Операция

reinterpret.cast

 

 

 

 

 

Операция reinterpret_cast спроектирована для

выполнения всего того, что

могут делать стандартные операции приведения, но без ограничений, накладывае­

мых операцией static_cast.

 

 

 

 

 

 

Операция reinterpret_cast может применяться, когда программист уверен,

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

В приведенном выше примере целый указатель р ссылается на значение двойной

длины. В последней строке указатель двойной длины q присваивается значению р.

Компилятору неизвестно, что указатель р фактически указывает на значение

двойной длины. Программист сообш^ает это компилятору с помош,ью операции

reinterpret_cast.

 

 

 

 

 

double

у = 42;

 

 

 

 

 

int

*р = reinterpret_cast<int*>(&y);

 

/ /

потенциальная проблема

double

*q = reinterpret_cast<double*>(p);

 

/ /

р указывает на значение

 

 

 

 

 

 

/ /

двойной длины

cout «

"The answer is " «

*q «

endl;

 

/ /

выводит на печать 42!

 

Этого же результата можно достичь, используя стандартные приведения int*

и double*.

 

 

 

 

 

double у = 42;

 

 

 

 

 

int

*р = (int*)&y;

 

.

/ /

целый р указывает на значение

 

 

 

 

 

/ /

двойной длины: проблема

double

*q = (double*)(р);

 

 

/ /

ok, потому что р указывает

 

 

 

 

 

/ /

на значение двойной длины

cout «

"The answer is " «

*q «

endl;

/ /

выводит на печать 42!

 

Считается, что операция reinterpret_cast лучше, чем стандартные операции

приведения, потому что она более заметна.

 

 

 

 

Обратите внимание, что здесь не может использоваться static_cast. Она мо­

жет преобразовывать значения различных типов, но не указатели. Кроме того,

операция static_cast является переносимой, потому что компилятор проверяет,

используются ли соответствуюилие типы (как числовые типы), операция преобра­

зования или конструктор преобразования.

 

 

 

 

Операция reinterpret_cast не гарантирует свою переносимость. Она выби­

рает последовательность бит в исходном выражении и интерпретирует их в соот­

ветствии с правилами типа назначения.

 

 

 

 

Это приведение должно использоваться как можно реже. Если требуется при­

менять

приведение, используйте

операцию

reinterpret_cast, а не стандартное

приведение.

Операция const^cast

Операция const_cast может аннулировать свойство объявления константой для значения или объекта-константы. Синтаксис ее такой же, как и у других современных операций приведения C+ + , включая указание типа назначения в угловых скобках и исходного выражения в скобках.

nonConstValue = const_cast<TypeName>(const\/alue);

Ее синтаксис и семантика более строгие, чем у других операций приведения. Все, что она может сделать,— это удалить свойство const у исходного значения constValue, поэтому становится возможным присвоение значения константы

832

Часть IV * Расширенное использование C*f^

constValue значению nonConstValue, которое неявляется константой. 1ип попConstValue должен быть именно TypeName, тип constValue — const TypeName.

Рассмотрим следующий пример. Поскольку переменная d определяется как const, обычный указатель не может на нее указывать.

const double d -42;

// ошибка: чтобы предотвратить *pd = 21

double *pd = &d;

Указатель на значение-константу может указывать на переменную d, но он

не меняет свое значение.

 

const double d = 42;

// ok, HO неочень практично

const double *pd = &d;

*pd =21;

// синтаксическая ошибка: указатель на const

Операция const_cast прибегает куловке. Она удаляет требование константы и открывает возможностьдля изменения значения, которое определяется какconst.

const double d = 42;

 

 

 

double *pd = const_cast<double*>(&d);

/ /

удалить

const

*pd =21;

/ /

теперь

все ok

cout « "The answer is " << *pd « endl;

/ /

выводит 21

Это фокус, который невозможно сделать даже для приведения стандартных типов С. Единственной ситуацией, где это необходимо, является сопровождение программы, где переменная была определена как const в соответствии с новыми условиями. Вместо изменения существующего определения можно добавить новый код, где переменная меняется с использованием операции const_cast.

Использование операции const_cast удаляет защиту. Указатель не должен быть указателем на константу. Он может быть разыменован при изменении объекта, на который он указывает. Рассмотрим класс Account.

class Account {

 

 

// базовый класс иерархии

protected:

 

 

// защищенные данные

double balance;

 

 

int

pin;

 

 

// идентификационный номер

char owner[40];

 

 

 

public:

 

int id, double bal)

// общее

Account(const char* name

{

strcpy(owner,

name);

 

// инициализация полей данных

balance = bal; pin = id; }}

 

operator double

() const

// общий для обоих счетов

{return balance; } operator int () const

{return pin; }

operator const char* () const

 

{ return owner; }

 

void operator -= (double amount)

 

{ balance -=amount; }

 

void operator += (double amount)

// безусловное приращение

{ balance +=amount; }

} ;

 

При попытке вызова функции-члена, неконстанты (например, operator+=()) в объекте const класса Account компилятор отбросит этот код.

const Account alC'Jones", 1122, 5000. 0);

// создать объект

a1 += 1000.0;

// синтаксическая ошибка

Глава 18 • Программирование с обработкой исключительных ситуаций

I 833 I

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

const Account a1("Jones",1122,5000.0);

/ /

создать объект

 

Account *ра = &a1;

/ /

синтаксическая

ошибка

Если указатель ссылается на объект-константу, он должен быть определен как указатель на объект-константу. Это разрешается, но указатель не используется для изменения состояния объекта.

const

Account

a1("Jones",1122,5000.0) ;

/ /

создать объект

const

Account

*pa = &a1;

/ /

ok

*pa += 1000.0;

 

/ /

синтаксическая ошибка

Однако обычный указатель можно установить так, чтобы он указывал на объект const, воспользовавшись операцией const_cast.

const Account alC'Jones", 1122, 5000.0);,

/ /

создать объект

Account *ра = const_cast<Account*>(&al);

/ /

ok

*pa += 1000.0;

/ /

это допускается

В результате состояние объекта-константы изменяется. Его баланс теперь со­ ставляет $6000, но над объектом не была выполнена явная операция.

Единственное, что операция const_cast может сделать, это удалить заш,иту const. Она не может выполнить какой-либо дополнительный тип преобразований. Если тип nonConstValue (результат приведения) отличается от типа ConstValue, а преобразование типа необходимо, то оно должно быть выполнено как отдельный дополнительный шаг.

Например, операция преобразования const char*() класса Account возвращает символьный указатель, который не может (и не должен) использоваться для изме­ нения содержимого символьного массива в объекте Account.

const

Account a1("Jones'M122, 5000.0);

/ /

создать объект

 

char

*с2 = static_cast<const char*>(a1);

/ /

синтаксическая

ошибка

Значение этого указателя может быть присвоено только указателю на константу. Такой указатель не применяется для изменения состояния объекта Account.

const

Account alC'Jones",1122,5000.0);

/ /

создать объект

const

char *c2 = static_cast<const char*>(a1);

/ /

это ok

strcpy(c2,"Jones");

/ /

синтаксическая ошибка

Использование операции const_cast в объекте Account не помогает, поскольку значение цели и исходное значение имеют разные типы.

const

Account alC'Jones",1122,5000.0);

/ /

создать объект

char

*с2 = const_cast<char*>(a1);

/ /

синтаксическая ошибка

Поскольку операция const_cast может выполнить только одно задание, со­ вершенно правильно было бы вначале преобразовать объект-константу Account в указатель на константу, используя static_cast или стандартное приведение, а затем преобразовать указатель на константу в обычный указатель с гюмош,ью const_cast.

const

Account alC'Jones",1122,5000.0);

 

/ /

создать объект

const

char *c1 = static_cast<const char*>(a1);

/ /

это ok

char

*c2 = const_cast<char*>(c1);

 

/ /

и это ok

strcpy(c2,"Jones");

/ / не является синтаксической ошибкой

Имя владельца изменяется без явной обработки объекта константы Account.

834 Чость IV • Расширенное использование C^-f

Операция dynamic^cast

Операция dynamic.cast является элементом набора компонентов C+ + , кото­ рые поддерживают информацию о типах в процессе исполнения (run-time-type information — RTTI). Остальными элементами этого набора компонентов являют­ ся операция typeid и структура type_info.

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

Операция dynamic_cast использует тот же синтаксис, что и другие операции приведения. Аргумент-указатель (или ссылка) заключается в круглые скобки, а тип назначения, в который преобразуется аргумент, указывается в угловых скобках. Если аргумент-указатель действительно принадлежит запрошенному типу назначения, операция возвращает аргумент-указатель на объект без изменений. Кроме того, она отправляет указатель на объект, если аргумент-указатель ссы­ лается на объект класса, порожденного из типа назначения. В противном случае она возвращает нуль, и программа может проверить значение.

Чтобы этот метод работал, иерархия классов должна содержать как виртуаль­ ные, так и не виртуальные функции. Он не работает для наследования без вир­ туальных функций.

Рассмотрим упрощенный класс Account, который содержит виртуальную функ­ цию displayO. Она отображает содержимое объекта Account.

class

Account {

 

 

 

 

/ /

базовый класс иерархии

protected:

 

 

 

 

 

 

 

 

double balance;

 

 

 

 

/ /

защищенные данные

int

pin;

 

 

 

 

 

 

/ /

идентификационный номер

char

owner[40];

 

 

 

 

 

 

public:

 

 

 

 

 

 

 

 

Account(const

char*

name,

int

id, double

bal)

/ / общий

{

strcpvCowner,

name);

 

 

/ /

инициализация полей данных

 

balance

= bal;

pin

= id;}

 

 

 

v i r t u a l void

displayO

 

 

/ /

виртуальная функция для RTTI

{ c o u t . s e t f ( i o s : : f i x e d , i o s : : f l o a t f i e l d ) ;

cout.precision(2);

 

cout «setw(6)

«

pin «

setw(20) « balance

 

 

 

«

" "

«

owner « e n d l ;

}

 

 

void

operator

-=

(double amount)

 

 

{ balance -= amount;

}

 

 

 

 

void operator += (double amount)

 

 

{

balance

+= amount;

}

 

 

/ /

безусловное приращение

Производный класс SavingsAccount добавляет метод paylnterestO, являю­ щийся дополнительным, и переопределяет базовый метод display() таким обра­ зом, что отображается дополнительный элемент данных interest.

class SavingsAccount

: public

Account {

 

double rate,

interest;

/ /

накопленные проценты

public:

 

 

 

 

SavingsAccount(const

char*

name, int id, double bal)

 

: Account(name,

id,

bal),

rate (6.0), interest(O) {

}

void paylnterestO

 

 

/ /

выплата раз в месяц

{double pay = balance * rate / 12 / 100; balance += pay; interest += pay; }

Глава 18 # Программирование с обработкой исключительных ситуаций

v i r t u a l

void displayO

 

 

 

 

 

{ c o u t . s e t f ( i o s : : f i x e d , i o s : : f l o a t f i e l d ) ;

cout

. precision(2);

cout

«setw(6)

«

pin

«

setw(8) «

interest

« setw(12)

 

« balance

«

" "

«

owner «

endl;

}

 

} ;

Здесь определяются объекты базового и производного классов. Кроме того, определяется указатель Account и используется операция dynamic_cast для его установки сначала на базовый объект, а затем на производный объект. При ис­ пользовании операции dynamic_cast следует проверить, является ли указатель нулевым. Если да, то ответ на вопрос отрицательный. Если указатель не нулевой, ответ утвердительный. Объект, который обозначает указатель, может использо­ ваться как объект класса, определенного в операции dynamic_cast.

Account a1 ( "Jones", 1122,5000) ;

/ /

создать объекты

SavingsAccount а2 (

"Smith",1133,3000);

 

 

Account

*pa = dynamic_cast<Account *>(&a1);

/ / o k

i f

(pa

== 0)

 

 

 

 

cout

«

"Null pointer\n";

 

 

else

 

 

 

 

 

 

pa->display();

 

/ /

Jones

pa = dynamic_cast<SavingsAccount *>(&a2);

/ /

ok

i f

(pa == 0)

cout «

"Null pointer\n";

 

 

else

 

 

 

 

 

 

pa->display();

 

/ /

Smith

В этом примере ответы на вопрос не очень важны, поскольку указатель, исполь­ зуемый как цель присваивания, является базовым. Первое приведение возвра­ щает указатель на объект a1, а второе приведение— на объект а2, который преобразуется в базовый указатель. Поскольку функция displayO является по­ лиморфной, она отображает данные в первом случае в базовом формате и во втором случае в производном формате. В этом фрагменте программы демонстри­ руется поведение операции dynamic_cast.

В следующем фрагменте программы базовый указатель снова используется как цель. Во-первых, он указывает на базовый объект, у которого запрашивается, может ли он выполнить операции Account. Ответ утвердительный, т. е. операция возвращает базовый указатель, ссылающийся на интересующий объект. Тем не менее сообщение displayO, посылаемое через этот указатель, указывает на данные в формате производного класса, потому что функция является виртуаль­ ной. Объект принадлежит производному классу. Затем проверяется, может ли объект a1 выполнить обязанности объекта SavingsAccount. Ответ отрицательный, это объект базового класса и операция возвращает нуль.

ра = dynamic_cast<Account

*>(&а2);

/ /

ок

i f

(pa

== 0)

 

 

 

 

cout

«

"Null pointer\n";

 

 

else

 

 

 

 

 

 

pa->display();

 

/ /

Smith

pa = dynamic_cast<SavingsAccount *>(&a1);

/ /

нулевой

i f

(pa == 0)

cout « "Null

pointer\n";

 

 

else

 

 

 

 

 

 

pa->display();

 

 

 

Следующий фрагмент программы намного интереснее, поскольку в нем ис­ пользуется указатель производного класса. Сначала проверяется, может ли объ­ ект a1 выполнить обязанности производного класса. Как и в предыдущем случае, ответ отрицательный. Совершенно все равно, является ли целью присваивания

836

Часть IV # Расширенное использование С*^^

базовый указатель (как в предыдуидем случае) или производный указатель (как в данном случае) — нуль всегда нуль. Затем проверяется, может ли объект а2 выполнить обязанности производного класса. Ответ, как и для самого первого фрагмента программы, положительный. В первом фрагменте результат преобра­ зовывался в базовый указатель, следовательно, мог вызывать только методы Account и виртуальные методы производного класса. Здесь преобразование от­ сутствует. Целью присваивания является производный указатель. Он может осуществлять доступ к базовым функциям, виртуальным функциям и функциям, определенным в производном классе.

SavingsAccount *psa = clynamic_cast<SavingsAccount

*>(&a1);

/ / О

i f

(psa

== 0)

 

 

 

 

cout

«

"Null pointer\n";

/ /

нулевой указатель

else

psa->clisplay();

/ /

без отображения

 

psa = dynamic_cast<SavingsAccount *>(&a2);

/ /

ok

 

i f

(psa

== 0)

 

 

 

else

cout

«

"Null pointer\n";

 

 

 

 

 

 

 

 

 

 

{ psa->paylnterest();

/ /

производный метод

 

psa->display(); }

/ /

Smith

 

Понятно,

что операция clynamic_cast представляет собой

мощный метод

проверки, может ли указанный объект выполнить требуемую операцию. Не все компиляторы поддерживают такую возможность. Те, которые поддерживают ее, могут не делать это по умолча«нию. Для использования данной возможности необходимо установить флаги компиляции или опции для явной поддержки возможностей RTTI.

Подобно оператору new, C + + поддерживает другой метод проверки, будет ли успешным приведение типов: генерация исключительной ситуации. Если указа­ тель не ссылается на объект класса, определенного в операции, то генерируется исключительная ситуация bad_cast. Это важно для ссылок. Указатели C + + мо­ гут как указывать, так и не указывать на объект, но ссылки C + + делают это всегда. Они не могут иметь нулевое значение. Для ссылок не нужно использо­ вать dynamic_cast, чтобы показать, что действительно указывает на объект клас­ са, определенного в приведении. Когда формальное утверждение оказывается неверным, соответствующим является генерация исключительной ситуации.

Операция typeid

Для определения того, к какому классу следует выполнить приведение базово­ го указателя, используется операция typeid. С помощью операции typeid можно выполнить одно из двух: проверить имя класса параметра или уточнить, относится ли к данному классу объект, на который установлен указатель.

В отличие от операций приведения типа операция typeid работает с парамет­ ром-объектом, а не с параметром-указателем. Она возвращает ссылку на объект type_inf о библиотечного класса. Реализация этого вызова зависит от компилято­ ра. Однако среди своих компонентов класса она всегда включает функцию-член name(). Эта функция возвращает символьный массив, т. е. снова зависит от реали­ зации. Чаще всего это имя класса, к которому принадлежит параметр операции или имя класса с предшествующим зарезервированным словом class.

Некоторые реализации компилятора определяют конструкторы type_inf как закрытые. В таком случае программа C + + не может создать объект класса type_inf 0. Вместо этого она должна послать сообщение в возвращаемом значении оператора typeid.

Глава 18 # Программирование с обработкой исключительных ситуаций

[ 837 |

Account a1("Jones",1122,5000);

// создать объекты

 

SavingsAccount a2("Smith",1133,3000);

// получить имя класса

 

const char *c1 = typeicl(al). name();

 

const char *c2 = typeicl(a2). name();

// выводит "class Account"

cout «

c1 «

endl-;

cout «

c2 «

endl;

// выводит "class SavingsAccount"

Операция typeid не может быть выражением некоторого класса, а также име­ нем класса, указанным как идентификатор (без кавычек). Это позволяет сравни­ вать результаты использования операции typeid с именем класса и с объектом. Если операция равенства возвращает true,то объект принадлежит к указанному классу.

if

(typeid(Account) == typeid(al))

//true

if

cout «

"a1 is Account\n";

// true

(typeid(SavingsAccount) ==typeid(a2))

if

cout «

"a2 isSavingsAccount\n" ;

// false

(typeid(Account) == typeid(a2))

if

cout «

"a2 is Account\n";

// false

(typeid(SavingsAccount) == typeid(al))

 

cout «

"a1 is SavingsAccount\n";

 

Bo время работы с набором неоднородных объектов, обозначенных базовыми

указателями, указатели, используемые как параметры в typeid, должны быть разыменованы.

ра = &а2;

i f

(typeid(Account) == typeid(*pa))

/ /

false

 

cout «

"pa points to Account\n";

 

 

i f

(typeid(SavingsAccount) ^^ typeid(*pa))

/ /

true

 

cout «

"pa points to SavingsAccount\n"

 

 

Обратите внимание, что эти операции не сравнивают объекты. Для структур С+Н- операции сравнения не определены. В них не сравниваются указатели. В от­ личие от других специальных приведений оператор typeid возвращает объект, а не указатель. Он применяет перегруженную операцию сравнения к объекту type_inf, который возвращается операцией typeid.

Операция typeid — мощное средство, которым легко злоупотребить. Исполь­ зуйте ее как можно реже.

Итоги

Возможности, рассмотренные в данной главе, относительно новые. Не все компиляторы и реализации библиотек их поддерживают. Советуем вам использо­ вать их с осторожностью.

Для выполнения исключительных ситуаций требуется затратить время и па­ мять. Это может оказаться важным для некоторых приложений. Важно правильно использовать исключительные ситуации для структуризации и упрощения переда­ чи управления в приложении.

Может показаться, что исключительные ситуации, которые генерируют значе­ ния встроенных типов, не слишком полезны. Ведь программы обработки исключи­ тельных ситуаций не могут отличить значения одинакового типа, сгенерированные в разных местах исходной программы. Проектирование классов для передачи ин­ формации исключительной ситуации от места обнаружения ошибки к месту ее исправления очень полезная и интересная задача.

838 Часть IV * Расширенное использование С+4-

Многие подтверждают, что использование исключительных ситуаций упрощает передачу управления в приложении и позволяет проектировщику отделить основ­ ную обработку от запутанных исключительных случаев. Возможно, использование управляющих структур if и switch действительно приводит в замешательство — исходный текст программы становится трудным для понимания. Имейте в виду, что сложная структура программы отражает трудные для понимания задачи. Несо­ мненным преимуществом использования стандартных управляющих структур для обработки различных случаев является то, что вся программа обработки находит­ ся в одном месте, она не разделена на части.

При использовании исключительных ситуаций программист, осуществляющий сопровождение, должен выполнять анализ решений, сделанных проектировщиком в отношении того, как разделить обработку на отдельные части. Эти решения часто сложны и имеют произвольный характер. Таким образом усложняется про­ грамма.

Представляется, что использование исключительных ситуаций оправдано в слу­ чае, когда программа проектируется таким образом, что ее часть, вьшолняющая исправление ошибок, не располагает информацией о той части программы, кото­ рая обнаруживает ошибку. Советуем вам использовать исключительные ситуации для передачи необходимой информации от одной части программы к другой. Одна­ ко помните, что необходимость передачи информации от одной части программы к другой возникает в том случае, когда принято решение о разделении обработки на две отдельные части. Пересмотр такого решения может исключить необходи­ мость чрезмерной обработки исключительных ситуаций.

Убедитесь, что использование исключительных ситуаций C + + не слишком усложняет работу программиста, осуществляющего сопровождение. Обозначьте все исключительные ситуации, которые каждая функция генерирует или передает от своего сервера. Не трогайте исключительные ситуации, которые функция не сможет сгенерировать. Снабжайте комментариями обработку исключительной си­ туации в исходном тексте программы и выделяйте документирование. Убедитесь, что каждая программа обработки исключительной ситуации проверена.

Программисты не имеют опыта использования операций приведения типов. Автор использует стандартные приведения С-типа, и кажется, что этого вполне достаточно. Конечно, можно неверно воспользоваться стандартными приведения­ ми типа. Стандартные приведения типов позволяют осуществлять преобразова­ ния указателей, которые не имеют смысла, а операция static_cast не позволит это сделать. Но преобразования, не имеющие смысла, обычно не делаются. Если такое преобразование потребуется, операция reinterpret_cast позволит сделать это настолько же легко, насколько допускает стандартное приведение типа.

Тем не менее большинство экспертов соглашаются, что за счет использования операции приведения типов повышается надежность программ. Они хорошо за­ метны и привлекают внимание программиста, осуществляющего сопровождение. Рекомендуем вам использовать их на практике.

п

^ ^ / ^

IB

 

 

 

олученные уроки

 

Темы данной главы

^C++ как традиционный язык программирования ь/ C++ как модульный язык

ь/ C++ как объектно-ориентированный язык

^C++ и его конкуренты

•^ Итоги

 

^jfy^

Ь1 прошли длинный путь. Стоит признать, что эта книга создава-

щ^Х

Шг

в ласьдолго. Иногда казалось, что последняя глава никогда не будет

\>^

 

^^написана. Однако пришло время завершить свой тяжелый труд.

Теперь можно оглянуться назад.

В этой главе не изучается новый синтаксис. Вместо этого попытаемся кратко обобш^ить основные характеристики этого великого, замечательного, запутанного языка С+ + . Теперь, когда вы усвоили весь предмет целиком и поняли, как разные компоненты состыковываются друг с другом, можно оценить, как много идей вложено в его проектирование и насколько осмотрительным нужно быть при его использовании.

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

Язык С+4- был создан как язык программной инженерии для построения боль­ ших компьютерных программ. Он преследовал несколько целей. С одной стороны, СН-+ старался быть языком программирования систем, от которых требуется вы­ сокая производительность: обеспечивая операции низкого уровня (например, сдвиги и побитовые логические операции) и поддерживая доступ к ресурсам компьютеров (например, регистры, изменчивые типы данных и арифметические операции над указателями). С другой стороны, C + + пытался облегчить разделе­ ние программ на независимые части, которые могли разрабатываться разными программистами, мало взаимодействуюш,ими друг с другом.

C + + является средством достижения нескольких конфликтуюш,их целей. Он был спроектирован:

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

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

Соседние файлы в предмете Программирование на C++