Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
63
Добавлен:
16.02.2016
Размер:
455 Кб
Скачать

Перегрузка конструкторов

Чаще всего перегрузка применяется при создании перегру­женных конструкторов (перегружать деструктор нельзя!). Целью этой перегрузки является желание предоставить пользователю как можно больше вариантов создания представителей класса. На самом деле мы уже неоднократно встречались с перегрузкой кон­структоров, хотя и не говорили об этом. Если класс предоставля­ет конструктор с параметрами и конструктор по умолчанию, мы уже имеем дело с перегрузкой конструкторов. Как мы уже знаем, конструктор по умолчанию необходим при выделении динамиче­ской памяти массиву объектов (ибо динамический массив объек­тов не может быть инициализирован).

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

void func(AnyClass& rOb);

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

AnyClass ob(t);

как

AnyClass ob = AnyClass(t);

если в классе определен конструктор

AnyClass(T t) ;

В рассматриваемом случае это означает, что вызов функции с аргументом t типа Т, то есть

func(t);

трактуется как

func(AnyClass(t));

При этом осуществляется неявное преобразование типа дан­ных. Неявное потому, что исходный код не вызывает явно конст­руктор для преобразования. С этой целью компилятор каждый раз будет создавать временный объект класса AnyClass, кото­рому и будет передавать параметр, переданный в функцию.

Следующий пример это демонстрирует.

class AnyClass

{

int x;

public:

//Конструктор по умолчанию

AnyClass () ;

//Конструктор преобразования типа int

AnyClass(int);

//Конструктор преобразования типа long

AnyClass(long);

//Конструктор преобразования типа double

AnyClass(double);

};

AnyClass::AnyClass() {x = 0;}

AnyClass::AnyClass(int _x) {x = _x;}

AnyClass::AnyClass(long _x) {x = int(_x);}

AnyClass::AnyClass(double _x) {x = _x;}

void func(AnyClass& rOb)

{

//Тело функции

}

main()

{

func(10); //Преобразование int в AnyClass

func(10L); //Преобразование long в AnyClass

//Преобразование double в AnyClass

func(10.0);

AnyClass ob;

func(ob); //Преобразование не нужно

return 0;

}

Этот же способ преобразования типа данных может быть ис­пользован и для преобразования из одного класса в другой. При­веденный ниже пример демонстрирует конструктор преобразова­ния из одного класса в другой.

class AnyClass

{

int x;

public:

AnyClass(){x =0;}

int GetX(){return x;}

};

class OtherClass

{

int z ;

public:

//Конструктор по умолчанию

OtherClass(){z =0;}

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

OtherClass(AnyClass& cl) ;

};

//Определение конструктора преобразования

OtherClass::OtherClass(AnyClass& cl)

{

z = cl.GetX() ;

}

void func(OtherClass& Ob)

{

//Тело функции

}

main ()

{

AnyClass ob;

//Преобразование AnyClass в Othlass

func(ob);

return 0;

}

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

Определенные пользователем преобразования типа применя­ются компилятором в следующих ситуациях:

• при инициализации объектов;

• при вызове функций;

• при возврате функцией значений.

Разумеется, их можно применять и для явного преобразования типа. Например:

main ()

{

AnyClass ob;

//Явное преобразование типа

OtherClass newOb = (OtherClass) ob;

}

Рассмотрим теперь пример, который демонстрирует пользу перегрузки другого конструктора - конструктора копирования. Пусть дан класс, который предназначен для использования в про­грамме прямоугольных областей:

class Rect

{

public:

Rect() ;

Rect(int, int);

Rect(int, int, int, int);

Rect(const Rect&);

Rect(const Rect&, int =0, int =0);

private:

int x, y, w, h;

};

Rect:: Rect()

{

x=y = w = h = 0;

}

Rect:: Rect(int _x, int _y)

{

x = _x;

y = _y;

w = h = 100;

}

Rect:: Rect(int _x, int _y, int _w, int _h)

{

x = _x;

у = _y;

w = _w;

h = _h;

}

Rect:: Rect(const Rect& rc)

{

x = rc.x;

у = rc.у;

w = rc.w;

h = rc.h;

}

Rect:: Rect(const Rect& rc, int _x, int _y)

{

x = _x;

у = _у;

w = rc.w;

h = rc.h;

}

main()

{

Rect rc(5,10,10,200);

Rect newrc(rc, 15, 40);

return 0;

}

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

Rect(const Rect&, int =0, int =0);

Приведенный код перегруженного конструктора копирования копирует некоторые из данных-членов объекта, указанного ссылкой, но какие-то данные-члены нового объекта инициализирует по-другому. Данный конструктор можно квалифицировать как конструктор копирования благодаря тому, что первый его параметр представляет собой (константную) ссылку на объект класса, а остальные - это аргументы по умолчанию. Таким обра­зом, вполне возможно создавать конструкторы копирования, со­держащие дополнительные параметры. Это предоставляет чрез­вычайную гибкость при создании разнообразных конструкторов копирования.

Соседние файлы в папке ЯзыкС++Глушаков