Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Перегрузка конструкторов и членов функций

 

Конструкторы и функции члены класса могут перегружаться, как обычные функции. В следующем примере продемонстрирован пример класса для хранения разнотипных значений (целых, вещественных и строковых). В качестве такого хранилища в классе в качестве члена данных используется анонимное объединение (union), позволяющее использовать для размещения разнотипных данных один и тот же участок памяти. Класс нашего примера содержит два закрытых члена данных (TypeVal  - определяет тип хранимого значения и анонимное объединение для хранения самих значений), 4 перегруженных конструктора (обеспечивают инициализацию экземпляра класса значениями разных типов), 3 перегруженных варианта функции члена Set (с их помощью при работе с экземпляром в него можно записывать значения разных типов) и 3 функции для извлечения из экземпляра трех разных по типу значений.

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

 

const int SLen = 11; // Размер массива символов

const char Err [ ] = "\ n*** Нет значения ***\ n"; // Сообщение об ошибке

Class t_Variant

{

char TypeVal; // Тип значения (0 - нет значения; 1 - целое; 2 - вещественное; 3 - строка)

union {       // Анонимное объединение для хранения значений разных типов

            int IVal;                  // Целое значение

            double DVal;          // Вещественное значение

            char SVal [ SLen ]; // Строка символов

};

public:

// Перегруженные конструкторы

t_Variant () { TypeVal = 0; }; // Нет значения

t_Variant (int V) { Set (V); }; // Инициализация целым

t_ Variant (double V) { Set (V); }; // Инициализация вещественным

t_ Variant (char V [ ]) { Set (V); }; // Инициализация строкой

// Перегруженные функции члены

void Set (int V) // Присвоение целого значения    

{ TypeVal = 1; IVal = V; }

void Set (double V) // Присвоение вещественного значения    

{ TypeVal = 2; DVal = V; }

void Set (char V[ ]) // Присвоение строки    

{ TypeVal = 3; strncpy_s(SVal, SLen, V, SLen – 1); }

// Остальные функции члены

int ToInt () // Получение целого значения

{ if (TypeVal == 1) return IVal; else {cout << Err; return 0;} }

double ToDouble () // Получение вещественного значения

{ if (TypeVal == 1 || TypeVal == 2) return DVal; else {cout  <<  Err;  return 0;} }

char * ToString () // Получение строки

{ if (TypeVal == 3) return SVal; else {cout  <<  Err;  return "";} }

};

 

Посмотрим, как можно использовать этот класс.

Определим несколько экземпляров этого класса:

t_ Variant V1;

При создании экземпляра V1 будет использован вариант конструктора без параметров. В результате экземпляр V1 не содержит никаких данных, поэтому попытка получить из него какое-то значение приведет к выводу на экран сообщения об ошибке. Например:

cout << V1. ToDouble ();

На экране мы увидим сначала сообщение об ошибке *** Нет значения ***, а затем число 0, которое функция ToDouble возвращает при отсутствии в экземпляре конкретного значения запрошенного типа.

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

V1. Set (3.14);

После этого: cout << V1. ToDouble (); выведет на экран значение 3.14.

Этот же экземпляр далее в программе можно использовать для хранения и других типов значений:

V1. Set (“Слово ”);

cout << V1. ToString ();

На экране увидим: Слово.

При определении экземпляров этого класса их можно инициализировать конкретными значениями разных типов:

t_Variant V2 = 1001;       // или так:  t_Variant V2 (1001);

t_ Variant V3 = “Текст”; // или так: t_ Variant V3 (“Текст”);

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

 

Замечание к примеру. В перегруженном варианте функции void Set (char V[ ]) использована функция strncpy_ s, которая обеспечивает безопасное копирование одной строки символов в другой массив символов (разновидность функции strcpy). Прототип этой функции имеет 4 параметра и выглядит так:

 

            void strncpy_s (char * Dest, int DestLen, char * Src, int TrancLen)

 

Функция копирует строку символов Src в массив символов Dest.

DestLen – размер массива Dest.

TrancLen – количество символов, которое должно быть скопировано из Src в Dest.

Иными словами, если длина строки Src превышает размер DestLen массива Dest, то из строки Src в Dest надо скопировать TrancLen символов.

Дружественные функции

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

Дружественными некоторому классу (классам) называют функции, не принадлежащие этому классу (классам), но имеющие доступ к закрытым членам этого класса (классов).

При определении функций «друзей» используется ключевое слово friend.

Используя предыдущий пример класса, определим для него некоторую функцию «друга» char * ConvToStr (t_ Variant V, char * Dest), предназначенную для преобразования значения, хранящегося в экземпляре V в текстовую строку Dest:

 

Class t_ Variant

{

char TypeVal;

union

{      

            int IVal;                 

            double DVal;         

            char SVal [ SLen ];

};

public:

// Открытые функции члены:

            …..

// Функция друг:

friend char * ConvToStr (t_Variant V, char * Dest);

};

 

Прототип функции «друга» предваряется ключевым словом friend и помещается в открытую секцию описания класса. Реализация этой функции:

 

char * ConvToStr (t_Variant V, char * Dest)

{

switch (V. TypeVal) // В зависимости от типа хранящихся данных

{

            // При отсутствии данных результат преобразования – сообщение об ошибке:

            case 0: strcpy_s (Dest, 100, Err);

                                   break;

            // Преобразуем целое значение V. IVal в строку Dest:

            case 1: _itoa_s (V.IVal, Dest, 15, 10);

                              break;

            // Преобразуем вещественное значение V. DVal в строку Dest:

            case 2: _gcvt_s (Dest, 20, V.DVal, 10);

                                   break;

            // Копируем строку V. SVal в строку Dest:

            case 3: strcpy_s (Dest, 100, V.SVal);

                                   break;

}

// Возвращаем строку Dest:

return Dest;

}

 

Обратим внимание на следующее: функция ConvToStr не принадлежит классу t_ Variant (в ее заголовке отсутствует префикс t_ Variant::), но, несмотря на это, внутри функции мы можем обращаться к закрытым членам данных этого класса (TypeVal, IVal, DVal и SVal).

 

Функции могут быть друзьями нескольких различных классов, например:

 

// Опережающее описание второго класса:

class Class_2;

 

// Описание первого класса:

class Class_1 {

// Закрытые члены:

int a;

….

public:

// Открытые члены:

….

// Функция «друг»:

friend void F (Class_1 C1, Class_2 C2);

};

 

// Описание второго класса:

class Class_2 {

// Закрытые члены:

int a;

….

public:

// Открытые члены:

….

// Функция «друг»:

friend void F (Class_1 C1, Class_2 C2);

};

 

Реализация функции «друга»:

 

Void F (Class_1 C1, Class_2 C2)

{

if (C1. a  != C2. a)

{

            // Что-то делаем

            ……

}

}

 

В этом примере функция F является другом и класса Class_1 и класса Class_2, поэтому в ее реализации мы можем обращаться к закрытым членам и того и другого класса.

Обратите внимание на использование в этом примере опережающего описания класса Class_2. Если его не сделать, то компилятор не будет «знать», что представляет собой второй параметр функции «друга», и это приведет к ошибке при компиляции программы.

 

Функция «друг» одного класса может быть функцией членом другого класса. Этот же пример мы можем реализовать так:

 

// Опережающее описание второго класса:

class Class_2;

 

// Описание первого класса:

class Class_1 {

// Закрытые члены:

int a;

….

public:

// Открытые члены:

….

// Функция член этого класса:

void F (Class_2 C2);

};

 

// Описание второго класса:

class Class_2 {

// Закрытые члены:

int a;

….

public:

// Открытые члены:

….

// Функция «друг»:

friend void F (Class_2 C2);

};

 

Реализация функции члена класса Class_1:

 

Class_1:: void F (Class_2 C2)

{

if (a  != C2. a)

{

            // Что-то делаем

            ……

}

}

 

Здесь функция F является функцией членом класса Class_1. Поэтому:

· нет необходимости передавать в нее параметр, соответствующий экземпляру класса Class_1, так как в ее реализации доступ ко всем членам класса Class_1 обеспечивается автоматически;

· поскольку функции F является членом класса Class_1, ее реализация должна сопровождаться оператором разрешения области видимости (префикс Class_1::);

· при обращении к членам класса Class_1 в реализации функции F нет необходимости пользоваться оператором «точка» (if (a  != C2. a) здесь a – член класса Class_1, а C2. a – это обращение к члену a экземпляра C2 класса Class_2).

 

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

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...