Конструктори приведення

Конструктор приведення класу — це конструктор з єдиним аргументом, тип якого відрізняється від типу класу. Такий конструктор звичайно ініціалізує новий об'єкт, використовуючи літерали, або дані змінних або об'єктів інших типів.

Наприклад, в клас CMoney можна додати конструктор приведення, який дасть можливість ініціалізувати об'єкт, використовуючи суму гривень і копійок, виражену одним дійсним числом з плаваючою крапкою:

 

class CMoney

{

// …

public:

// …

CMoney (double GrivnasAndCopecks)

{

Grivnas = long(GrivnasAndCopecks);

Copecks = int((GrivnasAndCopecks - Grivnas)* 100.0 + 0.5);

}

};

 

Конструктор округляє число копійок, що зберігається в аргументі GrivnasAndCopecks, до найближчого цілого числа. Крім того, конструктор виконує явне приведення типів даних, використовуючи дозволений в мові C++ альтернативний синтаксис, що відрізняється від традиційного запису операції приведення типів; у конструкторі використовується вираз long(GrivnasAndCopecks), а не традиційний запис (long)GrivnasAndCopecks.

Наведений вище конструктор приведення дозволяє ініціалізувати об'єкти класу CMoney таким чином:

 

CMoney Bucksl(29.63);

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

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

// число копійок

CMoney *Bucks = new CMoney(534.85);

 

Приведене раніше оголошення класу CMoney містило наступний конструктор:

 

CMoney (long Grn, int Cop)

{

SetAmount (Grn, Cop);

}

 

Цей конструктор можна переробити в конструктор приведення, додавши для другого аргументу значення за умовчанням:

 

CMoney (long Grn, int Cop = 0)

{

SetAmount(Grn, Cop);

}

 

Оскільки так написаний конструктор може тепер приймати один фактичний аргумент, його можна використовувати для ініціалізації об'єктів класу CMoney, задаючи тільки кількість гривень:

 

// обидві інструкції встановлюють значення: Grivnas = 25 і Copecks = 0

CMoney Dough = 25L;

CMoney *PDough = new CMoney (25L);

 

Символ L додається в кінець кожної цілочисельної константи, задаючи їй тип long. Якщо даний символ не вказаний, константа розглядатиметься як константа типу int. У цій ситуації компілятор не визначить, чи приводити значення типу int до типу double, щоб викликати конструктор для double, або приводити значення тип int до значення типу long з тим, щоб викликати конструктор для long.

Така ситуація називається неоднозначним викликом перевантаженої функції і породжує помилку компіляції. Приведення типу int до double або long є стандартними приведеннями. Хоча змінні типів int та long мають однаковий розмір в Visual C++, вони розглядаються як різні типи, які вимагають приведення.

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

 

class CMessage

{

// . . .

public:

// . . .

CMessage (const char *String)

{

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

strcpy (Buffer, String);

}

};

 

Тепер об'єкт повідомлення можна ініціалізувати одним рядком:

 

CMessage Note = "Зроби це зараз!";

CMessage *PNote = new CMessage ("Пам'ятай це завжди!");

 

Нижче приведена остаточна версія класу CMessage, включаючи нові конструктори копіювання і приведення.

 

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

#include <string.h>

#include <iostream>

using namespace std;

class CMessage

{

private:

char *Buffer;

public:

CMessage () // конструктор за умовчанням

{

Buffer = new char ('');

}

CMessage (const CMessage SMessage) // конструктор копіювання

{

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;

}

};

 

7.5 Динамічна ідентифікація типу

В мові C++ для підтримки об’єктно – орієнтованого програмування разом з приведеннями типів в стилі мови C++, розглянутими в підрозділі 2.7.2, є ще один допоміжний засіб – ідентифікація типу під час виконання (Run – Time Type Identification - RTTI).

Ідентифікація типу під час виконання здійснюється спеціальним оператором typeid(), який оголошений в заголовному файлі <typeinfo> в двох варіантах:

 

typeid(object)

typeid(type)

 

Обидва варіанти оператора під час виконання програми повертають посилання на об'єкт класу type_info, який також оголошений в заголовному файлі <typeinfo>. Об'єкт класу type_info, посилання на який повертається оператором , містить розгорнену інформацію про тип аргументу, заданого як об'єкт object в першому варіанті або як ім'я типу type – в другому варіанті оператора.

Клас type_info містить закриті члени і наступні відкриті члени класу:

 

const char* name();

bool operator==(const type_info &ob);

bool operator!=(const type_info &ob);

bool before(const type_info &ob);

 

Функція name() повертає вказівник на рядок, що містить ім'ям типу аргументу оператора.

Перевантажені оператори == та != дають можливість порівнювати типи об'єктів: викликаючого об'єкту і об'єкту, заданого аргументом.

Функція before(), як правило, використовується для внутрішніх цілей; вона повертає true, якщо викликаючий об'єкт передує об'єкту, заданому як аргумент.

Найбільшу користь можна отримати від оператора type_info(), застосовуючи його до вказівника на об'єкт базового класу. При такому використанні він автоматично повертає тип реального об'єкту, адресу якого містить в даний момент вказівник, наприклад:

 

#include <iostream>

#include <typeinfo>

using namespace std;

class A

{

public:

virtual bool f(){return false;}

};

class B: public A

{

public:

};

 

int main()

{

A *p;

A obA;

B obB;

p = &obA;

cout << “Вказівник p містить адресу об'єкту типу ”;

cout << typeid(*p).name() << ‘n’;

p = &obB;

cout << “Вказівник p містить адресу об'єкту типу ”;

cout << typeid(*p).name() << ‘n’;

return 0;

}

 

Після виконання даної програми на екрані з'являться два рядки:

 

Вказівник p містить адресу об'єкту типу class A

Вказівник p містить адресу об'єкту типу class B