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

Опорный конспект

.pdf
Скачиваний:
41
Добавлен:
28.03.2015
Размер:
1.95 Mб
Скачать

которая определяет тип аргумента и возвращает ссылку на объект типа const typeinfo, описывающий этот тип. В качестве аргумента typeid можно также использовать и имя некоторого типа. В этом случае typeid вернет ссылку на объект const typeinfo этого типа. Класс typeinfo содержит опера- ции-функции operator== и operator!=, которые используются для сравнения типов объектов. Класс typeinfo также содержит компонентную функцию name (), возвращающую указатель на строку, содержащую имя типа аргумента (см. рис. 20.1.).

Операция typeid имеет две формы: typeid (выражение),

typeid (имя_типа)

Если операнд операции typeid суть разыменованный нулевой указатель, порождается исключение bad_typeid.

20.2. Специальные операции приведения типов

Стандарт ANSI определяет специальный синтаксис операций приведения типа, позволяющий программисту воспользоваться преимуществами RTTI и, кроме того, указать точно, что он хочет получить в результате таких операций. Новых операций приведения четыре: dynamic_cast, static_cast, reinterpret_cast и const_cast.

Здесь необходимо вспомнить, для чего вообще может служить приведение типа. Можно назвать следующие случаи:

1.Чтобы изменить действительное представление данных либо поведение объекта, на который ссылается некоторый указатель. Простейшее приведение такого рода — преобразование целого типа в вещественный;

2.Чтобы изменить лишь интерпретацию компилятором некоторых данных, не меняя их действительного (физического) представления. Таково, например, приведение типа int к типу unsigned и наобо-

рот;

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

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

Операция reinterpret_cast

Синтаксис данной формы операции приведения имеет вид: reinterpret_cast <целевой_тиn> (аргумент)

163

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

Целевой_тип может быть типом ссылки, указателя, целым, перечислимым или вещественным типом.

Если целевой_тип тип указателя или ссылки, то аргумент может быть указателем или ссылкой, а также числовой (вещественной, целой, перечислимой) переменной; когда целевым типом является числовой тип, то операнд может быть указателем или ссылкой.

Операция возвращает значение целевого типа.

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

#include <iostream> using namespace std;

class A { public:

int i;

A(){i=10;}virtual ~A(){}} ; class B: public A {

public: int i;

B(int ii): i(ii) {}

B(A&): i(11)

{

cout << "Derived conversion constructor... ";

}

};

int main(){ int i = 7;

int *ip = &i;

int temp = reinterpret_cast<int>(ip); cout << "Pointer value is "<< ip << endl;

cout << "Representation of a pointer as int is " << hex

<< temp << endl;

cout << "Convert it back and dereference:" <<reinterpret_cast<int*>(temp) << endl;

return 0;

}

Рис. 20.2. Использование операции reinterpret_cast

Операция const_cast

Операция const_cast имеет ту же форму, что и предыдущая:

соnst_сonst <целевой_тип> (аргумент)

164

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

#include <iostream> #include <string.h> using namespace std;

int main()

{

const char *ip;

ip = new char[20];

strcpy(const_cast<char*>(ip), "New const string,"); cout << ip << endl;

strcpy(const_cast<char*>ip, "Test"); cout << ip << endl;

delete [] ip; return 0;

}

Рис. 21.3. Использование операции const_cast

Операция static_cast

Операция статического приведения типа static_саst <целевой тип> (аргумент)

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

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

Если некоторый указатель может быть приведен к типу Т*, то объект этого типа может быть приведен к типу Т&.

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

165

#include <iostream> using namespace std;

class A {public: int i; A(){i=10;}virtual ~A(){}} ; class B: public A {

public: int i;

B(int ii): i(ii) {} B(A&): i(11) {

cout << "Derived conversion constructor... ";}};

int main(){

B b(22), *pb = &b;

A &ra = static_cast<A&>(b); // Ссылка на b как базовый объект.

A *pa = static_cast<A*>(&b); // Указатель на b как базовый

//объект.

cout << "Derived object: " << b.i << endl; cout << "Downcasting pointer to pointer: " <<

static_cast<B*>(pa)->i << endl; // Приведение указателей. cout << "Downcasting referense to referense: "<< static_cast<B&>(ra).i<< endl; // Приведение к ссылке. cout << "Downcasting reference to object: ";

cout << static_cast<B>(ra).i<< endl; // Приведение к объекту. return 0;}

Рис. 20.4. Использование операции static_cast

Операция dynamic_cast

Операция динамического приведения типа dynamic_сast <целевой_тип> (аргумент)

не имеет аналогов среди операций, выполняемых с применением «классической» нотации приведения. Операция и проверка ее корректности при известных условиях происходит во время выполнения программы (см. рис. 20.5.).

Целевой тип операции должен быть типом указателя, ссылки или void*. Если целевой тип — тип указателя, то аргументом должен быть указатель на объект класса; если целевой тип — ссылка, то аргумент должен также быть соответствующей ссылкой. Если целевым типом является void*, то аргумент также должен быть указателем, а результатом операции будет указатель, с помощью которого можно обратиться к любому элементу «самого производного» класса иерархии, который сам не может быть базовым ни для какого другого класса.

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

166

#include <iostream> using namespace std;

class A {

public: int i; A(){i=10;}virtual ~A(){}} ;

class B: public A

{

public: int i;

B(int ii): i(ii) {} B(A&): i(11)

{

cout << "Derived conversion constructor... ";

}

};

int main()

{

B b(22), *pb = &b;

A a, *ppa=&a;

A &ra = dynamic_cast<A&>(b); //Ссылка на b как базовый объект

A *pa = dynamic_cast<A*>(&b); //Указатель на b как базовый объект cout << "Derived object: " << b.i << endl;

// Приведение указателей

cout << "Downcasting pointer to pointer: " << dynamic_cast<B*>(pa)->i << endl;

// Приведение к ссылке

cout << "Downcasting referense to referense: "<< dynamic_cast<B&>(ra).i<< endl;

// Приведение к объекту

cout << static_cast<B*>(ppa)->i<< endl; return 0;

}

Рис. 20.5. Использование операции dynamic_cast

При попытке произвести некорректное преобразование операция возвращает нуль, если целевой_тип — указатель. Если ссылка, операция выбрасывает исключение типа bad_cast.

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

167

Тема 21

ПРОСТРАНСТВА ИМЕН

Пространство имен определяет некоторую декларативную область [4]. Они позволяют локализовать имена идентификаторов, чтобы избежать конфликтных ситуаций с ними. Это особенно важно при разработке многофайловых проектов группой разработчиков. Каждый программист может создать свое собственное пространство имен, тогда он может быть уверен в уникальности имен идентификаторов. Фактически пространства имен определяют область видимости.

Для задания пространства имен используется следующая инструкция: namespace name

{

//объявления

}

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

Пространство имен можно объявить только в области действия файл, то есть нельзя объявить локальное пространство имен внутри функции или класса. Вложенность пространств имен допускается.

Для использования пространства имен нужно его сделать действующим. Для этого существует два способа:

1.использование директивы using;

2.использование объявления using.

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

В действительности между объявлением и директивой есть еще одно существенное отличие. Если некоторое имя определено и в пространстве имен и в области объявлений, то при попытке применить объявление using, чтобы поместить имя из пространства имен в область объявлений, оба имени вступят в конфликт, компилятор сообщит об ошибке. Если же имя из пространства имен в область объявлений перенести с помощью директивы using, то локальная версия имени перекроет версию из пространства имен, но ошибки не возникнет.

Задача. Написать программу, которая хранит информацию о наградах собак, и ищет самую титулованную собаку.

168

//заголовочный файл namesp.h

#include <iostream>

//создание пространства имен собак namespace dogs

{

class Dog

{

public:

Dog(char* n= "Sharik", char* p = "Dvornyaga"); Dog(const Dog& ref);

~Dog();

friend std::ostream& operator<<(std::ostream& c, const Dog& ref);

friend std::istream& operator>>(std::istream& c, Dog& ref);

private:

char* name; char* poroda;

};

}

//создание пространства имен наград namespace awards

{

//директива, активизирующая пространство имен dogs using namespace dogs;

class Award

{

public:

Award();

Award(const Award& ref); ~Award();

friend std::ostream& operator<<(std::ostream& c, const Award& ref);

friend std::istream& operator>>(std::istream& c,

Award& ref);

private:

Dog dog; char* award;

};

const Award& bestAward(const Award *arr, int n);

}

Рис. 21.1. Создание пространств имен dogs и awards

169

// файл namesp.cpp

#include "namesp.h" #include <string.h> #include <stdlib.h>

#include <time.h>

namespace dogs

{

using std::ostream; Dog::Dog(char *n, char *p) {

if(n)

{

name = new char [strlen(n)+1]; strcpy(name, n);

}

else

name = 0; if(p)

{

poroda = new char [strlen(p)+1]; strcpy(poroda, p);

}

else

poroda = 0;

}

Dog::Dog(const Dog & ref) { if(ref.name)

{

name = new char [strlen(ref.name)+1]; strcpy(name, ref.name);

}

else

name = 0; if(ref.poroda)

{

poroda = new char [strlen(ref.poroda)+1]; strcpy(poroda, ref.poroda);

}

else

poroda = 0;

}

Dog::~Dog()

{

if(name)

delete [] name; if(poroda)

delete [] poroda;

}

170

Рис. 21.2. Начало описания пространства имен dogs

ostream& operator<<(ostream& c, const Dog& ref)

{

if(ref.name)

c<<ref.name<<std::endl;

if(ref.poroda)

c<<ref.poroda<<std::endl; return c;

}

std::istream& operator>>(std::istream& c, Dog& ref)

{

std::cout<<"Enter the name of the dog"<<std::endl; char str[255];

c.getline(str, 255);

ref.name = new char[strlen(str)+1]; strcpy(ref.name, str); std::cout.flush();

std::cout<<"Enter the poroda of the dog"<<std::endl; c.getline(str, 255);

ref.poroda = new char[strlen(str)+1]; strcpy(ref.poroda, str);

return c;

}

}

Рис. 21.3. Окончание описания пространства имен dogs

namespace awards

{

Award::Award():dog(0,0)

{

award =0;

}

Award::Award(const Award& ref):dog(ref.dog)

{

if(ref.award)

{

award = new char[strlen(ref.award)+1]; strcpy(award, ref.award);

}

else

award =0;

}

Award::~Award()

{

if(award)

delete [] award;

}

Рис. 21.4. Начало описания пространства имен awards

171

ostream& operator<<(ostream& c, const Award& ref)

{

c<<ref.dog<<std::endl;

if(ref.award)

c<<ref.award<<std::endl; return c;

}

std::istream& operator>>(std::istream& c, Award& ref)

{

c>>ref.dog;

std::cout<<"Enter award"<<std::endl; char str[255];

c.getline(str, 255);

ref.award = new char[strlen(str)+1]; strcpy(ref.award, str);

return c;

}

const Award& bestAward(const Award *arr, int n)

{

srand(time(0)); return arr[rand()%n];

}

}

Рис. 21.5. Окончание описания пространства имен awards

// файл test.cpp

#include "namesp.h"

int main()

{

using awards::Award;

using awards::operator <<; using awards::operator >>; using awards::Dog;

Award arr[3]; for(int i=0; i<3;i++)

{

std::cin>>arr[i];

}

Award best = awards::bestAward(arr, 3); std::cout<<best<<std::endl;

Dog my; std::cin>>my;

std::cout<<my<<std::endl; return 0;

}

Рис. 21.6. Использование пространств имен dogs и awards

172