Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
билеты информатика.rtf
Скачиваний:
39
Добавлен:
02.05.2015
Размер:
6.9 Mб
Скачать

Представление о префиксной (до) и постфиксной (после) операциях увеличения

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

++variable; variable++;

Так как первый оператор появляется до переменной, он называется префиксным оператором увеличения.Аналогично этому, второй оператор появляется после переменной и называется постфиксным оператором увеличения. Вам необходимо знать, что C++ трактует эти два оператора по-разному. Например, рассмотрим следующий оператор присваивания:

current_count = count++;

Этот оператор присваивания указывает C++ присвоить текущее значение count переменной current_count. В дополнение к этому постфиксный оператор увеличения заставляет C++ увеличить текущее значение count.Использование постфиксного оператора в этом случае делает показанный выше оператор эквивалентным следующим двум операторам:

current_count = count;

count = count + 1;

Теперь рассмотрим следующий оператор присваивания, который использует префиксный оператор увеличения:

current_count = ++count;

В этом случае оператор присваивания указывает C++ сначала увеличить значение count, а затем присвоить результат переменной current_count. Использование префиксного оператора увеличения делает показанный выше оператор эквивалентным следующим двум операторам:

count = count + 1;

current_count = count;

Важно освоить префиксную и постфиксную операции увеличения, так, как они будут встречаться вам в большинстве программ на C++. Следующая программа PRE_POST.CPP иллюстрирует использование префиксной и постфиксной операций увеличения:

#include <iostream.h>

void main(void)

{    int small_count = 0;    int big_count = 1000;    cout << "small_count равно " << small_count << endl;    cout << "small_count++ производит " << small_count++ << endl;    cout << "конечное значение small_count равно " << sniall_count << endl;    cout << "big_count равно " << big_count << endl;    cout << "++big_count производит " << ++big_count << endl;    cout << "конечное значение big_count равно " << big_count << endl; }

Когда вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:

С:\> PRE_POST <ENTER>

small_count равно 0

small_count++ производит 0

конечное значение small_count равно 1

big_count равно 1000

++big_count производит 1001

конечное значение big_count равно 1001

С переменной small_count программа использует постфиксную операцию увеличения. В результате программа выводит текущее значение переменной (0), а затем увеличивает его на 1. С переменной big_countпрограмма использует префиксную операцию увеличения. В результате программа сначала увеличивает значение переменной (1000 + 1), а затем выводит результат (1001). Найдите время, чтобы отредактировать эту программу, и сначала измените постфиксную операцию на префиксную, а затем префиксную на постфиксную. Откомпилируйте и запустите программу, обращая внимание на то, как изменение операции изменяет вывод.

С++ обеспечивает также операции уменьшения

Как вы уже знаете, двойной знак плюс (++) представляет собой оператор увеличения C++. Подобным образом двойной знак минус (--) соответствует оператору уменьшения C++, который уменьшает значение переменной на 1. Как и в случае с операцией увеличения, C++ поддерживает префиксный и постфиксный операторы уменьшения. Следующая программа DECCOUNT.CPP иллюстрирует использование оператора уменьшения C++:

#include <iostream.h>

void main(void)

{    int small_count = 0;    int big_count = 1000;    cout << "small_count равно " << small_count << endl;    cout << "small_count-- производит " << small_count-- << endl;    cout << "конечное значение small_count равно " “ small_count << endl;    cout << "big_count равно " << big_count << endl;    cout << "--big_count производит " << --big_count << endl;    cout << "конечное значение big_count равно " << big_count << endl; }

Когда вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:

С:\> DECCOUNT <ENTER”

small_count равно 0

small_count-- производит 0

конечное значение small_count равно -1

big_count равно 1000

big_count производит 999

конечное значение big_count равно 999

Как видите, префиксный и постфиксный операторы уменьшения C++ работают так же, как и соответствующие операторы увеличения, с той лишь разницей, что они уменьшают значение переменной на 1.

ДРУГИЕ ОПЕРАТОРЫ С++

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

Таблица 5.2. Операции C++, которые вы можете встретить в программах.

Операция

Функция

%

Взятие по модулю или остаток; возвращает остаток целочисленного деления

~

Дополнение; инвертирует биты значений

&

Побитовое И

|

Побитовое включающее ИЛИ

^

Побитовое исключающее ИЛИ

<<

Сдвиг влево; сдвигает биты значения влево на указанное количество разрядов

>>

Сдвиг вправо; сдвигает биты значения вправо на указанное количество разрядов

Операторы приведения типов.

Операторы приведения типов (cast operators) позволяют изменять тип выражения: они вычисляют значение выражения, изменяют его тип и присваивают значение нового типа результату. В некоторых случаях это может привести к изменению внутреннего формата данных (изменению вида данных в памяти компьютера). Ниже приведены основные операторы приведения типов и способы их использования.

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

  • dynamic_cast. Во время выполнения программы проверяет, что объект, на который ссылается указатель, имеет определенный тип.

  • reinterpret_cast. Выполняет преобразование указателей на пустой тип (void *) в указатели на более конкретные типы (int *char * и т.д.)

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

Операторы приведения типов языка C поддерживают все эти операции, однако синтаксис является другим. Оператор приведения dynamic_cast является новым элементом ANSI C++.

Таким образом, ANSI C++ предоставляет четыре разных оператора приведения, каждый из которых обладает специфическими возможностями. Операторы C оставлены в C++ для обеспечения преемственности.

Оператор приведения старого стиля

Язык C и ранние версии C++ предоставляют всего один оператор приведения типов практически для всех ситуаций. В ANSI C++ этот важный оператор приведения также сохранен. Следующее выражение имеет то же значение, что и выражение expr, но оно приведено к типу, определенному в скобках:

(type) expr

Хотя выражение (type) expr теоретически имеет то же значение, что и выражение expr , действие оператора приведения типа может привести к преобразованию данных, которые изменят внутреннее представление числа в памяти.

Оператор приведения типа старого стиля также можно определить, используя альтернативный синтаксис:

type(expr)

Приведенный ниже пример позволяет вывести на печать текущее значение целого i в формате с десятичной точкой:

int i = 11;cout << (double) i;

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

Операторы приведения типов языка C можно использовать во всех ситуациях, в которых используются операторы приведения типов ANSI C++ за исключением dynamic_cast. Два следующих оператора выполняют одни и те же действия:

char *p = (char *) malloc(n);char *p = reinterpret_cast<char *>(malloc(n));

Следующая пара операторов, которые подавляют предупреждение компилятора, также выполняют одно и те же действие:

bool flag1 = (bool) 12;bool flag1 = static_cast<bool>(12);

 

Удаление модификатора const

Оператор приведения типа const_cast предусмотрен для облегчения вызова функций, которые должны использовать модификатор const, но не используют его. Такие функции работают с параметром типа указатель и никогда не изменяют данные, на которые ссылается этот указатель, даже если программист не определил параметр как const. Правила C++ запрещают передачу константного указателя в качестве фактического параметра такой функции, оператор const_cast предоставляет возможность обойти этот запрет.

Следующее выражение имеет то же значение, что и expr. Обозначенный тип type должен быть таким же, что и тип expr, кроме отсутствия модификатораconst или volatile. Как правило, type - это тип указателя:

const_cast<type>(expr)

В следующем примере функция display_num использует в качестве параметра указатель, но не данные, на которые он указывает (*p):

void display_num(double *p){  printf("The value is %6.3f\n", *p);}

Так как заданные посредством указателя p данные не изменяются, должна быть возможность передачи в качестве параметра const-указателя. Правила C++ запрещают это из-за несоответствия типов указателей:

const double x = 11.2;display_num(&x); // ошибка

Чтобы обойти этот запрет, нужно лишить указатель модификатора const. Следующая операция позволяет устранить запрет:

const double x = 11.2;display_num(const_cast<double *>(&x));

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

Оператор dynamic_cast предоставляет возможность проверить, что указатель базового класса указывает на объект определенного производного класса. Оператор dynamic_cast проверяет тип объекта во время выполнения программы, используя информацию RTTI.

Следующее выражение должно возвращать ссылку на заданный тип type. Во время выполнения задачи объект, указанный в аргументе expr, должен иметь определенный тип type или тип, производный от типа type, иначе приведение типа не выполняется и возвращается нулевой указатель:

dynamic_cast<type *>(expr)

Проверка типа во время выполнения программы

Оператор dynamic_cast также можно использовать со ссылочными типами. Если приведение типа не выполняется из-за того, что expr имеет тип , не совпадающий с типом type или производным от него, то вырабатывается исключение bad_cast .

Для этого оператора приведения типов существует единственное ограничение: при использовании его для приведения к указателю на производный класс (что является его главным назначением), класс выражения expr должен иметь, по крайней мере, одну виртуальную функцию. По этой причине операторdynamic_cast часто называется оператором полиморфного приведения типа (polymorphic cast). Также возможно применение оператора dynamic_castдля приведения к указателю базового класса без каких-либо ограничений. Если типы не связаны вообще, компилятор не допускает преобразования.

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

Например, следующий фрагмент программы определяет базовый класс B и производный класс D, а затем присваивает его адрес указателю базового класса:

class B{public:  virtual void func1(int);};class D{public:  void func2(void);};// ...D od;B *pb = &od;

В последней строке этого примера указателю на класс B присваивается адрес объекта производного класса D. Следующая функция использует оператор dynamic_cast, чтобы проверить, действительно ли параметр типа B * указывает на объект класса D. Если это так, то вызывается функция func2, которая определена только в классе D и классах, производных от D:

void process_B(B *arg){  D *pd;  pd = dynamic_cast<D *>(arg);  if (pd) pd->func2(void);  // ...}

Если arg указывает на объект класса D или класса, производного от D, то преобразование будет выполнено, и функция process_B вызовет функциюD::func2 через указатель pd. Иначе операция преобразования не выполняется и pd присваивается значение NULL.

Приведение типа указателя

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

Следующий оператор возвращает то же значение выражения, что и выражение expr, но приведенное к заданному типу type. Этот тип, который обычно является типом указателя, может отличаться от типа выражения expr только интерпретацией (способом использования). Сами данные и их внутреннее представление в памяти при этом не изменяются.

reinterpret_cast<type>(expr)

Этот оператор нельзя использовать для удаления модификаторов const и volatile из выражения expr, так как это могло бы привести к возможности приведения типов с помощью оператора reinterpret_cast между указателями, а также между указателями и целыми числами без каких-либо ограничений.

Вообще оператор reinterpret_cast никогда не изменяет внутреннее представление данных, на которые ссылается указатель, определяемый выражениемexpr . Тем не менее, когда тип указателя и тип данных, на которые он указывает, не соответствуют друг другу, результат может быть просто драматичен.

Наиболее часто этот оператор используется для преобразования значения, возвращаемого как void *, к более конкретному типу. В C++ нельзя присвоить указатель, объявленный как void*, указателю другого типа без приведения типа. Например,

char *p = reinterpret_cast<char *>(malloc(100));

Хотя возвращенное значение функции malloc не обязательно переводить в другой тип указателя немедленно, такое приведение должно быть выполнено перед первым обращением к выделенной области памяти. Одним из преимуществ оператора new перед функцией malloc является отсутствие необходимости приведения типов.

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

Преобразование типа между родственными объектами или указателями

Оператор static_cast - это оператор приведения типов между родственными объектами или типами указателей. Участвующие в этой операции классы должны быть связаны через наследственность, конструктор или функцию преобразования. Оператор static_cast также работает с простейшими числовыми типами данных.

Следующее выражение имеет то же значение, что и выражение expr, но позволяет переводить его в тип type:

static_cast<type>(expr)

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

Оператор static_cast используется в следующих случаях:

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

  • приведение к простейшим типам данных;

  • приведение выражения типа A к типу B, когда B имеет соответствующий конструктор из типа A или A имеет функцию преобразования в B.

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

В основном оператор static_cast используется для приведения сложных типов к простым: при работе с простыми типами он подавляет предупреждение компилятора:

long j = 17;short i = static_cast<short>(j);

Этим как бы говорится: "Да, я на самом деле хочу это сделать". При этом компилятор не выдает предупреждения о возможной потере данных. При этом необходимо быть уверенным в том, что данные не будут слишком велики для приведения в другой тип.

Таким же образом возможно преобразование типов из указателя базового класса в производный без каких-либо ограничений. Но так как никакой проверки во время выполнения программы не делается, то забота о поддержке данных в должном виде возлагается на программиста (производный класс содержит все члены базового плюс члены производного класса). Например, в следующем примере B является базовым классом класса D:

B *pb;// ...D *pd = static_cast<D *>(pb);

В данном случае объект, на который указывает pb, фактически имеет тип D или производный от него. Однако при этом значения всех членов класса D, не входящих в класс B, не определены, и обращение к ним приведет к возникновению ошибок. В такой ситуации программист сам должен определить все недостающие члены с помощью присваивания. Можно пойти и в обратном направлении - выполнить присваивание указателю на базовый класс. В этом случае два следующих оператора эквивалентны.

pb = static_cast<D *>(pd);pb = pd;

Другие случаи, в которых оператор static_cast может быть полезен, редко встречаются в программах. Например, с его помощью можно последовательно выполнить несколько преобразований типов данных. В следующем примере A и B - такие классы, в которых A имеет функцию преобразования в B, а B - функцию преобразования к типу int. Тогда объект типа A может быть преобразован в тип int следующим образом:

A oa;int i = static_cast<int>(static_cast<B>(oa));

Без использования этой цепочки преобразований (и при отсутствии функции преобразования A в тип int) преобразование oa в целое было бы невозможным.

На первый взгляд кажется, что при использовании оператора static_cast программисту придется выполнять больше работы, чем при программировании старыми методами. Однако при этом становятся доступными возможности ANSI C++ по разделению операторов приведения типа, что значительно облегчает просмотр больших программ.

Оператор static_cast также полезен при применении различных форматов данных или при необходимости вывода данных в формате, отличном от того, который определен для данного типа по умолчанию. Например, нужно вывести целое число i в формате с плавающей точкой:

int i = 25;cout << static_cast<double>(i);

Заметим, что для вывода на экран адреса строки надо использовать оператор reinterpret_cast, так как имя строки имеет тип указателя, а не простейший тип:

char str[] = "Hello";cout << hex << reinterpret_cast<int>(str);

Операция присваивания в С++

Операция присваивания в языке программирования C++ обозначается знаком '='. Как и другие операторы в C++, она может бытьперегружена.

Операция присваивания копированием - особый вид операции присваивания, используемый для присваивания объектов одного класса друг другу. Является одним из особых членов-функций и генерируется автоматически компилятором в случае, если нет явного объявления программистом. Код, сгенерированный компилятором, выполняет поверхностное копирование.

Операция присваивания копированием отличается от конструктора копирования тем, что должен очищать члены-данные цели присваивания (и правильно обрабатывать самоприсваивание), тогда как конструктор копирования присваивает значения неинициализированным членам-данным.[1] Например:

My_Array first; // инициализация конструктором по умолчанию

My_Array second = first; // инициализация конструктором копирования

second = first; // присваивание операцией присваивания копированием