Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по С++ глава 5.doc
Скачиваний:
3
Добавлен:
05.11.2018
Размер:
166.91 Кб
Скачать

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

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

class CCurrency

{

// ...

public:

// ...

CCurrency (double DolAndCen)

{

Dollars = long (DolAndCen);

Cents = int ({DolAndCen - Dollars) * 100.0 + 0.5);

}

// ...

};

Заметьте: конструктор округляет число центов, сохраняемое в параметре DolAndCen, до ближайшего целого числа. Кроме того, обратите внимание: конструктор выполняет явное преобразование типов данных, используя разрешенный в языке C++ альтернативный синтаксис, отличающийся от традиционной записи операции приведения типов. Например, в конструкторе используется выражение long (DolAndCen), а не традиционная запись (long) DolAndCen. Безусловно, в C++ допустимо использовать приведение типов, хотя новый синтаксис может облегчить чтение некоторых выражений.

Такой конструктор преобразования позволяет инициализировать объекты класса CCurrency следующим образом.

CCcurrency Bucksl(29.63);

CCurrency Bucks2 = 43.247; // будет округлено до 43.25

CCurrency Bucks3 (2.0e9); // почти максимально допустимое

// число долларов CCurrency *Bucks = new CCurrency (534.85);

Приведенное ранее определение класса CCurrency содержит следующий конструктор:

CCurrency (long Dol, int Cen)

{

SetAmount (Dol, Cen);

}

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

CCurrency (long Dol, int Cen = 0)

{

SetAmount (Dol, Cen);

}

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

// установка значений: Dollars = 25 и Cents = О

CCurrency Dough = 25L;

CCurrency *PDough = new CCurrency (25L);

Символ L добавляется в конец каждой целочисленной константы, задавая ей тип long. Если данный символ не указан, константа будет рассматриваться как константа типа int. В этой ситуации компилятор не определит, преобразовывать ли тип int в double, чтобы вызвать конструктор для double, либо преобразовывать в long с тем, чтобы вызвать конструктор для long. Такая ситуация называется неоднозначным вызовом перегруженной функции и порождает ошибку компиляции, Преобразования типа int в double и long являются стандартными преобразованиями. Переменные типов int и long рассматриваются как различные типы, требующие преобразования.

Рассмотрим пример с добавлением конструктора преобразования в CMessage:

class CMessage

{

// ...

public: // .. .

CMessage (const char *String)

{

Buffer = new char [strlen (String)+1];

scrcpy (Buffer, String);

}

// ...

};

Теперь объект можно инициализировать одной строкой.

CMessage Note = "do it now";

CMessage *PNote = new CMessage ("remember!");

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

Помимо использования конструктора преобразования при явном создании объекта класса и инициализации его единственным значением другого типа, компилятор также вызывает соответствующий конструктор преобразования для преобразования переменной-члена какого-либо другого типа в объект класса. Другими словами, конструктор преобразования указывает компилятору, как преобразовывать объекты или переменные различных типов в объект данного класса. Например, два конструктора преобразования класса CCurrency позволят присвоить значение типа double или long существующему объекту этого класса.

CCurrency Bucks;

Bucks = 29.95;

Bucks = 35L;

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

В следующем примере предположим, что функция имеет параметр типа CCurrency.

void Insert (CCurrency Dinero);

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

Важное преимущество, которое дает создание конструкторов преобразования, состоит в применении перегруженных операторов, определенных для класса, и в том, что пропадает необходимость в написании отдельной функции-оператора для каждой ожидаемой комбинации операндов. Например, CCurrency в настоящий момент имеет три функции operator+:

class CCurrency

{

// ...

public:

// ...

CCurrency operator+ (const CCurrency sCurr)

{

return CCurrency (Dollars + Curr.Dollars, Cents + Curr.Cents);

}

CCurrency operator+ (long Dol)

{

return CCurrency (Dollars + Dol, Cents);

}

friend CCurrency operator+ (long Dol, const CCurrency &Curr);

// . . .

};

Теперь, благодаря конструктору, преобразующему значения типа long в объекты класса CCurrency, можно исключить две функции operator+ и переписать функцию, которая не является членом класса.

class CCurrency

{

// ...

public:

// ...

friend CCurrency operator+ (const CCurrency &Curr1,

const CCurrency &Curr2);

// ...

};

CCurrency operator+ (const CCurrency &Curr1,

const CCurrency &Curr2)

{

return CCurrency (Currl.Dollars + Curr2.Dollars,

Currl.Cents + Curr2.Cents);

}

Эта единственная функция-оператор может выполнять следующие операции сложения.

CCurrency Bucks1 (39, 95);

CCurrency Bucks2 (149, 85);

CCurrency Bucks3;

Bucks3 = Bucks1 + Bucks2;

Bucks3 = Bucks1 + 10L;

Bucks3 = 15L + Bucks1;

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

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

CCurrency Bucks1 (39, 95);

CCurrency Bucks2 (149, 85);

CCurrency Bucks3;

Bucks3 = Bucksl + 29.51;

Bucks3 = 87.64 + Bucks1;

В листингах 5.1 и 5.2 приведены окончательные версии классов CCurrency и CMessage, в том числе новые конструкторы копирования и преобразования.

Листинг 5.1

// CCurr.h: файл заголовков класса CCurrency

#include <string.h>

#include <iostream.h>

class CCurrency

{

private:

long Dollars;

int Cents;

public:

CCurrency () // конструктор по умолчанию

{

Dollars = Cents = 0;

}

CCurrency (long Dol, int Cen =0) // конструктор преобразования

{

SetAmount (Dol, Cen) ;

}

CCurrency (double DolAndCen) // конструктор преобразования

{

Dollars = long (DolAndCen);

Cents = int ((DolAndCen - Dollars) * 100.0 + 0.5);

}

void GetAmount (long *PDol, int *PCen)

{

*PDol = Dollars;

*PCen = Cents;

}

void PrintAmount ()

{

cout.fill ('0');

cout.width (1);

cout << '$' << Dollars << '.';

cout.width (2);

cout << Cents << '\n';

}

void SetAmount (long Dol, int Cen)

{

Dollars - Dol + Cen / 100;

Cents = Cen % 100;

}

friend CCurrency operator+ (const CCurrency &Curr1,

const CCurrency &Curr2);

};

CCurrency operator+ (const CCurrency &Curr1, const CCurrency &Curr2)

{

return CCurrency (Corrl.Dollars + Curr2.Dollars,

Currl.Cents + Curr2.Cents);

}

Листинг 5.2

// CMess.h: файл заголовков класса CMessage

#include <string.h>

#include <iostream.h>

class CMessage

{

private:

char *Buffer;

public:

CMessage () // конструктор по умолчанию

{

Buffer = new char ('\0');

}

CMessage (const CMessage &Message) // конструктор копирования

{

Buffer = new char [strlen (Message.Buffer) + 1];

strcpy (Buffer, Message.Buffer);

}

CMessage (const char *String) // конструктор преобразования

{

Buffer = new char [strlen (String) + 1];

strcpy (Buffer, String);

}

~CMessage ()

{

delete [] Buffer;

}

void Display ()

{

cout << Buffer << '\n';

}

void Set (char *String)

{

delete [] Buffer;

Buffer = new char (strlen (String) + 1];

strcpy (Buffer, String);

}

CMessage & operator= (const CMessage &Message)

{

if (&Message = this)

return *this;

delete [] Buffer;

Buffer = new char [strlen (Message.Buffer) + 1];

strcpy (Buffer, Message.Buffer);

return *this;

}

};

Примечание

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