Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Книга C++.doc
Скачиваний:
24
Добавлен:
10.11.2019
Размер:
2.48 Mб
Скачать

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

Итак продолжаем подвиги на стезе обучения !!! Первым пунктом нашей программы будут Перегруженные конструкторы. К этому моменту вы уже знаете этих 2 слова по отдельности и иногда можете попугать знакомых ими.

Экскурс в историю

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

Пример

//Предположим у вас возникла необходимость написать программу для ввода

//чисел разного типа. Перед вводом числа будет выдаваться подсказка о

//вводимом типе. Это эффектно реализуется с помощью перегрузки !!!

#include <iostream.h>

//У нас есть перегруженная функция под названием EnterDigit, которая

//принимает 2 параметра . Существует 2 определения этой функции:

//В первой версии , принимаемые аргументы - это char * и int & .

//В второй версии char * и double & .

//В зависимости от типа переданных значений вызывается соответствующая

//версия .

void EnterDigit (char *str , int &i){

cout<<str;

cin>>i;

}

void EnterDigit (char *str , double &i){

cout<<str;

cin>>i;

}

void main(){

int i;

double d;

//Вызов версии void EnterDigit (char *str , int &i)

// так как второй параметр имеет тип int

EnterDigit("Enter an integer :",i);

//Вызов версии void EnterDigit (char *str , int &i)

// так как второй параметр имеет тип double

EnterDigit("Enter a double :",d);

//Вывод результатов на экран

cout<<i<<" "<<d;

}

Послесловие к примеру

Итак мы рассмотрели пример на перегрузку функций . Я надеюсь вы вспомнили,что такое перегруженная функция и с чем её едят.

Маленькое замечание

Хотя можно использовать одно и то же имя для перегрузки функций, выполняющих совершенно разные действия, делать этого не стоит. Так как смысл перегрузки - выполнение схожих действий.

Конец экскурса

О конструкторах вы читали в предыдущем урок.Конструктор - тоже функция отсюда следует вывод !!! Его можно перегрузить . При этом на его перегрузку будут распостраняться все те же правила, которые используются при обычной перегрузке функций .Возникает вопрос , а зачем мне собственно говоря перегружать конструктор ? Для того чтобы при создании обьекта класса вы могли предусмотреть различные способы инициализации .

Пример

//Напишем класс таймер с перегруженным конструктором.

#include <iostream.h>

#include <stdlib.h>

// В библиотеке stdlib.h находится функция

// atoi (её прототип int atoi( const char *string ) ) ,

// которая конвертирует строку в число. Строка задаётся в качестве

// параметра , возвращаемое значение и будет полученным числом

// Ниже у нас определено 3 версии перегруженного конструктора, которые

// охватывают все возможные случаи инициализации .

class timer {

int seconds;

public:

// Первый перегруженный конструктор предназначен для инициализации

// обьекта с помощью строки

timer(char *t){

seconds=atoi(t);

cout<<" char * "<< "\n";

}

// Второй перегруженный конструктор предназначен для инициализации

// обьекта с помощью целого числа

timer(int t){

seconds=t;

cout<<" int "<< "\n";

}

// Третий перегруженный конструктор предназначен для инициализации

// обьекта с помощью 2 целых чисел ( минут и секунд )

timer(int min,int sec){

seconds=min*60+sec;

cout<<" int , int "<< "\n";

}

// Функция для показа количества секунд

void ShowTime(){

cout<<"Seconds : "<<seconds<<"\n";

}

};

void main(){

// Обьявления переменных

//Строковый массив для хранения секунд

char s[20];

//Переменная целого типа для хранения секунд

int sec;

//Переменная целого типа для хранения минут

int min;

// Ввод секунд в строковую переменную

cout<< "Put seconds : "<<" \n ";

cin>> s ;

// Ввод секунд в переменную целого типа

cout<< "Put seconds one more time : "<<" \n ";

cin>> sec ;

// Ввод минут в переменную целого типа

cout<< "Put minutes : "<<" \n ";

cin>> min ;

// Создание 3-х обьектов класса таймер

timer a(s), b(sec), c( min, sec);

// Показ значений секунд

a.ShowTime();

b.ShowTime();

c.ShowTime();

}

Вывод

Использование перегруженных конструкторов необходимо тогда , когда нужно предусмотреть все возможные формы инициализации при создании обьекта .

this

С каждым занятием мы все глубже и глубже проникаемся идеями объектно-ориентированого программирования. Мы уже знаем как описать класс, как использовать модификаторы доступа. Сегодня, мы рассмотрим еще один важный вопрос, который касается объектно-ориентированого программирования - указатель this. Заметим, что это один из сложнейших вопросов, который рассматривается в ООП. Поэтому отниситесь к данной теме со всей серьезностью.

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

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

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

Рассмотрим пример использования this. Ниже приведена тестовая программа, где создается класс Test в котором: объявлены два поля данных (с1 и с2); есть конструктор Test(char b) в котором происходит инициализация полей данных; функция-член Print(), которая позволяет вывести на экран содержимое полей данных (т.е. с1 и с2); функция-член Increment использует неявный указатель this, чтобы возвратить приращенные значения c1 и c2; а функция-член Where_Am_I возвращает адрес заданного объекта.

#include <iostream.h>

/*Демонстрация использования указателя this*/

class Test{

public:

//конструктор

Test(char b)

{

c1=b;

c2=c1+1;

}

//функция увеличения

Test Increment()

{

c1++;

c2++;

return (*this);//вернуть объект

}

//функция возвращает адрес памяти где расположен наш объект

Test* Where_Am_I()

{

return this;//вернуть указатель на себя

}

//функция печати

void Print()

{

cout<<c1<<c2<<'\t';

}

private:

char c1,c2;

};

void main()

{

Test a('A');//создадим экземпляр класса Test

a.Print();//выведем на экран значение полей данных

//узнаем где мы (т.е.выведем адреса памяти, где расположен наш объект)

cout<<"is at"<<a.Where_Am_I()<<endl;

Test b('B');//создадим экземпляр класса Test

b.Print();//выведем на экран значение полей данных

//узнаем где мы (т.е.выведем адреса памяти, где расположен наш объект)

cout<<"is at"<<b.Where_Am_I()<<endl;

//вызываем функцию-член Increment()

//так как функция возвращает экземпляр класса, то можем обратиться

//к функции-члену Print

b.Increment().Print();

}

Результат работы программы следующий:

AB is at0x0065FDF4

BC is at0x0065FDF0

CD Press any key to continue

Теперь снова вернемся к вопросу о перегрузке операторов. Обратите внимание, что при перегрузке унарного оператора список параметров операции-функции может быть пустым, так как параметр передается неявным образом в указателе this. При перегрузке бинарного оператора, список параметров операции-функции задает один операнд справа от оператора, так как операнд слева передается неявным образом, снова таки, в указателе this. Чтобы разобраться во всех тонкостях использования this, давайте рассмотрим пример строкового класса с перегруженными операторами.