Перегрузка операций приведения типа

Преобразования типов бывают сужающими и расширяющими. Сужающие трансформируют величину в тип, который не может содержать всех значений данного типа. Расширяющие преобразования трансформируют величину в тип, который может содержать как минимум все значения исходного типа. Расширяющий тип преобразований безопасен в программировании всегда, сужающий – нет. Преобразование типов может быть как явным, так и неявным. Несмотря на отсутствие в С++ механизмов уточнения подтипов, имеется мощная поддержка перегрузки функций. При проверке соответствия учитывается преобразование типов, (переопределение), как встроенное в язык, так и определенное программистом. Принцип, лежащий в основе применяемых преобразований, состоит в том, что следует отдавать предпочтение преобразованиям наименее опасным с точки зрения возможных ошибок, наименее «сомнительным». Определенные пользователем преобразования рассматриваются только в том случае, если без них вызов разрешить нельзя.

Пример 1

void g (int);

double d = 1.234;

char *str = "Строка";

G(d); // допустимо, т.к. существует неявное преобразование double к int

G(str); // ошибка

 

Пример 2

monstr::operator int() {return health;}

monstr Vasia;

cout<<int(Vasia);


 

10.7. Правила выбора реализации перегружаемых функций в C++

Далее приведены правила выбора реализации перегружаемых функций в C++ (тип, указанный слева от стрелки, преобразуется к типу, указанному справа).

- преобразования, выполняющиеся по умолчанию

Array -> pointer

T -> const T

- стандартные преобразования

int -> double

double -> int

производный тип -> базовый

- расширяющие разрядности преобразования

int -> double

short -> int

float -> double

Формат перегрузки операции приведения типа:

operator <имя нового типа> ();

Пример 1

char c = 256; // из-за потери 8 старших битов c ='\0';

double d = 0.999999999999999999;

long n = d; // n=0;

long m = 32768; // двоичное представление числа 32768 содержит единственную единицу - в 15 разряде

int k = m; // k = -32768, т.к. 15-й разряд для int является знаковым

unsigned u = m; // m = 32768

Пример 2

void g (int);

double d = 1.234;

char *str = "Строка";


G(d); // допустимо, т.к. существует неявное преобразование double к int

G(str); // ошибка

Пример 3

monstr::operator int() {return health;}

monstr Vasia;

cout<<int(Vasia);

Переопределение операций ввода-вывода

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

Формат операций включения/исключения из потока:

ostream &operator << (ostream &out, <новый тип> <имя>)

{<тело функции оператора>};

istream &operator >> (istream &in, <новый тип> <<мя>)

{<тело функции оператора>};

Если операции включения/исключения используются для полей описания, как private или protected, то эти операции должны описываться как дружественные.

Пример 1

#include<iostream.h>

Class Tadress

{

private:

char country[16];

char city[16];

char street[20];

int housenum;

public:

Tadress(){}

~Tadress(){}

friend ostream &operator << (ostream &out, Tadress obj);

friend istream &operator >> (istream &in, Tadress &obj);

};

ostream &operator << (ostream &out, Tadress obj) /* тело функции переопределяет вставки в поток */

{

out << "Address: "<< endl;

out << "Country: "<< obj.country<< endl;

out << "City:"<< obj.city<< endl;

out << "House:"<< obj.housenum<< endl;

return out;

}

istream &operator >> (istream &in, Tadress &obj) /* тело функции переопределяет исключения из потока */

{

cout<<"введите адрес следующим образом:";

cout<<" <<страна>> <<город>> <<улица>> <<номер дома>>"<<endl;

in >> obj.country >> obj.city >> obj.street >> obj.housenum;

return in;

}

Void main()

{

clrscr();

Tadress a, b;

cin >> a >> b;

cout << a << b;

getch();

}


 


Перегрузка операций вызова функций

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

Пример 1

Class if_greater

{

Public

int operator() (int a, int b) const { return a>b; }

};

Объект данного функционального класса используется так, как если бы он был функцией.

Пример 2

if_greater x;

cout<< x(1, 5)<< endl; /* 0 */

cout<< if_greater() (5, 1)<< endl; /* 1 */

 

Поскольку в классе определена операция вызова функции с двумя аргументами, то выражение x(1, 5) является допустимым, или это можно записать так:

Пример 3

*operator()(1, 5)

А во втором операторе вызова функции if_greater используется для вызова конструктор по умолчанию класса if_greater.

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


 


Перегрузка операций индексирования

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

Пример 1

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

#include<iostream.h>

#include<stdlib.h>

Class Vect

{

public:

explicit Vect (int n=10);

Vect (const int a[], int n); /* инициализация массива */

~Vect(){delete[] p;}

int &operator[] (int i);

void print();

private:

int *p;

int size;

};

Vect::Vect(int n):size(n)

{

p=new int[size];

for (int i=0; i<size; i++)

p[i]=a[i];

}

/* перегрузка операции индексации */

int &Vect::operator[](int i)

{

if(i<0 || i>=size)

{

cout<<"Неверный индекс (i="<<i<<")"<<endl;

cout<<"Завершение программы"<<endl;

exit(0);

}

return p[i];

}

Void Vect::Print()

{

for(int i=0; i<size; i++)

cout<<p[i]<<" ";

cout<< endl;

}

Int main()

{

int arr[10]={1,2,3,4,5,6,7,8,9,10};

Vect a(arr, 10);

a Print();

cout<<a[5]<<endl;

cout<<a[12]<<endl;

return 0;

}

Результаты работы:

1 2 3 4 5 6 7 8 9 10

6

Неверный индекс (i=12)

Завершение программы

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

Операцию индексирования можно определять только как метод класса.


 



Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: