Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Progi / Lesson 6 / lec6.doc
Скачиваний:
30
Добавлен:
28.03.2015
Размер:
342.53 Кб
Скачать

1) Конструктор копирования на C++

Конструктор копирования создается всегда, если он не перегружен явно.

class Record

{

private:

int min,sec;

public:

Record();

};

Record::Record()

{

min=1;

sec=2;

}

int _tmain(int argc, _TCHAR* argv[])

{

Record a;

Record b=a; // вызов конструктора копирования по умолчанию b: min=1 sec=2

// копируются поля a

Record c(a); // вызов конструктора копирования по умолчанию с: min=1 sec=2

return 0;

}

Перегрузка конструктора копирования.

#include "stdafx.h"

class Record

{

private:

int min,sec;

public:

Record(); // конструктор без параметров

Record (Record & r); // конструктор копирования

};

Record::Record()

{

min=1;

sec=2;

}

Record::Record(Record &r) // r - возвращаемый параметр - ссылка

{

min=r.min*2;

sec=r.sec+3;

}

int _tmain(int argc, _TCHAR* argv[])

{

Record a; // min=1 sec=2

Record b=a; // конструктор копирования, т.к. b создается : min=2 sec=5

Record c(a); // конструктор копирования min=2 sec=5

Record d=Record(a); // конструктор копирования min=2 sec=5

Record e; // конструктор без параметров min=1 sec=2

b=e; // присваивание, т.к. b был создан раньше min=1 sec=2

return 0;

}

Обычно параметр в конструкторе копирования объявляется const (не изменяемым)

...................

Record (const Record & r); // конструктор копирования с const

..........................

Record::Record(const Record &r)

{

min=r.min*2;

sec=r.sec+3;

}

Без const возможен конструктор, меняющий поля копируемого объекта.

Record::Record( Record &r)

{

min=r.min*2;

sec=r.sec+2;

r.min=0; // изменение поля копируемого

}

......

Record a;

Record b=a; // b: min=2 sec=5 a: min=0 !!

Перегрузка конструктора с дополнительным параметром, при этом конструктор копии по умолчанию создается!!

class Record

{

private:

int min,sec;

public:

Record();

Record (const Record & r,int m); // конструктор копирования

// с дополнительным параметром

};

Record::Record()

{

min=1;

sec=2;

}

Record::Record( const Record &r,int m)

{

min=m;

sec=r.sec+2;

}

int _tmain(int argc, _TCHAR* argv[])

{

Record a;

Record c=Record(a,7); // вызов конструктора копирования

// с дополнительным параметром min=7 sec=4

Record b=a; // вызов конструктора копирования по умолчанию

// конструктор создается, т.к. нет перегрузки

// Record (const Record & r);

// b: min=1 sec=2 поля копируются

return 0;

}

"мелкое" (shallow) и "глубокое" (deep) копирование

В классе Person есть год рождения и фамилия в виде указателя.

#include "stdafx.h"

#include <string.h>

class Person

{

private:

char *Fam;

int God;

public:

Person(char *f,int g); // конструктор с параметрами

Person(const Person &p); // копирующий конструктор

void Putfam(char *f); // задание новой фамилии

};

Person::Person(char *f,int g)

{

Fam=new char[20]; // выделение памяти для фамилии

strcpy(Fam,f);

God=g;

}

Person::Person(const Person &p)

{

Fam=p.Fam; // присваивание указателя!! мелкое копирование

God=p.God;

}

void Person::Putfam(char *f)

{

strcpy(Fam,f);

}

int _tmain(int argc, _TCHAR* argv[])

{

Person a("Иванова",1980);

Person b=a; // конструктор копирования b: Иванова 1980

a.Putfam("Петрова"); // b: Fam= Петрова !!!

// Fam у a и b указывает на одну и ту же область

return 0;

}

Исправление. Глубокое копирование.

Person::Person(const Person &p)

{

Fam=new char(strlen(p.Fam)+1); // выделение памяти из кучи для нового объекта

strcpy(Fam,p.Fam); // копирование в выделенную память

God=p.God;

}

.......

int _tmain(int argc, _TCHAR* argv[])

{

Person a("Иванов",1980);

Person b=a;

a.Putfam("Петров"); // b: осталась Ивановой

return 0;

}

2) Перегрузка оператора присваивания на C++

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

class Record

{

private:

int min,sec;

public:

Record(int m,int s);

void Putmin(int m);

};

Record::Record(int m,int s)

{

min=m;

sec=s;

}

void Record::Putmin(int m)

{

min=m;

}

int _tmain(int argc, _TCHAR* argv[])

{

Record a = new Record(2,4);

Record b=new Record(3,5);

a=b;

b.Putmin(5); // b: min=5 sec=5 a: min=3 sec=5 - прежнее значение после присваивания

Record *c = new Record(2,4);

Record *d=new Record(3,5);

c=d; // c и d указывают на одну и ту же область памяти

d->Putmin(5); // d: min=5 sec=5 c: min=5 sec=5 изменение в одной области памяти

return 0;

}

Можно выполнить присваивание содержимое объектов

*c=*d; // объекты указывают на разные области

d->Putmin(5); // d: min=5 sec=5 c: min=3 sec=5

Если у класса имеются указатели, стандартный метод перегрузки оператора присваивания имеет вид.

#include "stdafx.h"

#include <string.h>

class Person

{

private:

int God;

char *Fam;

public:

Person();

Person(int g,char *f);

Person(Person &p);

void Putfam(char *f);

Person & operator = (Person &p);

};

Person::Person() // конструктор без параметров

{

God=0;

Fam=NULL;

}

Person::Person(int g,char *f) // конструктор с параметрами

{

int k;

God=g;

k=strlen(f)+1;

Fam=new char[k]; // выделение памяти столько, сколько у аргумента

strcpy(Fam,f);

}

Person::Person(Person &p) // конструктор глубокого копирования

{

int k;

God=p.God;

k=strlen(p.Fam)+1;

Fam=new char[k]; // выделение памяти для копии

strcpy(Fam,p.Fam);

}

void Person::Putfam(char *f) // задание фамилии объекту

{

strcpy(Fam,f);

}

Person & Person::operator = (Person &p) // перегрузка присваивания (глубокое)

{

int k;

if(Fam)

{

delete Fam; // очистка паяти

}

Fam=NULL;

God=p.God;

k=strlen(p.Fam)+1;

Fam=new char[k]; // выделение памяти для ногвой копии

strcpy(Fam,p.Fam);

return *this;

}

int _tmain(int argc, _TCHAR* argv[])

{

Person a;

Person b(1981,"Сидоров");

Person c=b; // объект c создается, вызов конструктора копирования

b.Putfam("Кузнецов"); // c по прежнему Сидоров - глубокое копирование

c=b; // c был уже создан, вызов перегруженного присваивания c.Fam теперь Кузнецов

b.Putfam("Иванов"); // c по прежнему Кузнецов

return 0;

}

3) Производный класс, наследование полей и методов и добавление новых полей и методов на С++

Имеем класс Record (min,sec), введем новый классSprint, учитывающий десятые доли секунды спортсмена (int dec) . В классе добавим метод Putdec , устанавливающий количество десятых долей и Numberdec, вычисляющий общее число десятых долей секунды у объектов (например, для сравнения результатов).

class Record

{

public:

void Init (int m,int s); // задание значений полей

int Numbersec();

private:

int min;

int sec;

};

int Record::Numbersec()

{

return min*60+sec;

}

void Record::Init(int m,int s)

{

min=m;

sec=s;

}

// наследуемый класс

class Sprint : public Record // Sprint-наследник Record

{

public:

// новые методы Sprint

Void Putdec(int d);

Int Numberdec();

private:

// новое поле Sprint

Int dec;

};

В класссе Sprint имеются также public методы Numbersec, Init и private поля min, sec.

void Sprint::Putdec(int d)

{

dec=d;

}

int Sprint::Numberdec()

{

int k;

k=Numbersec();

return k*10+dec;

}

int main(int argc, char* argv[])

{

int m,n;

Record x,y;

x.Init(4,36); // min=4 sec=36

y.Init(2,4); // min=2 sec=4

Sprint z;

z.Init(1,8); // min=1 sec=8

z.Putdec(4); // dec=4

m=z.Numberdec(); // m=684

n=z.Nubersec(); // n=68 вызов наследуемого метода

}

4) Модификатор доступа protected

Заменим вызов Numbersec() из Numberdec прямым вычислением:

int Sprint::Numberdec()

{

int k;

k=min*60+sec; // вместо k=Numbersec();

return k*10+dec;

}

Ошибка! min, sec is not accessible

min , sec определены как private в Record и не могут вызываться из методов другого класса Sprint, даже после наследования.

Исправление:

class Record

{

public:

void Init (int m,int s);

int Numbersec();

protected: // вместо private:

intmin;

intsec;

};

К полям и методам из protected могут обращаться как методы Record, так и методы всех наследуемых классов, (Sprint).

class A

{

......

protected:

int x;

....

};

......

class B : public A

{

....

}

class C : public B

{

....

};

Идентификатор xможет появиться в любом методе классов A, B, C.

Инкапсуляция: public, private, protected разграничивают доступ к полям и методам.

5) Модификатор в заголовке наследуемого класса

classSprint:publicRecord// public -открытое наследование

publicозначает, что наследуемые поля изRecordостаются без изменения:

Record public -> Sprint public

Record protected -> Sprint protected

Record private -> Sprint private

Другие варианты:

class Sprint : protected Record // защищенное наследование

Наследуемые поля и методы перестают быть public

Record public -> Sprint protected

Record protected -> Sprint protected

Record private -> Sprint private

class Sprint : private Record // закрытое наследование

Все наследуемые поля и методы становятся private

Record public -> Sprint private

Record protected -> Sprint private

Record private -> Sprint private

int main(int argc, char* argv[])

{

int m,n;

Sprint z;

z.Init(1,8); // min=1 sec=8 // ошибка, Init теперь private

z.Putdec(4); // dec=4 // правильно Putdec определена в Sprint как public

m=z.Numberdec(); // m=684 // правильно

n=z.Nubersec(); // n=68 // ошибка

}

В современных ООП языках используется только открытое наследование

6) Множественное наследование

В C++ класс может быть наследником нескольких классов.

class Record

{

public:

..........

protected:

int min,sec;

...........

}

class Person

{

public:

..........

protected:

char Fam[30];

int Vozrast;

.............

}

class Sportsman : public Person , public Record

{

public:

..........

private:

int Category;

............

}

В классе Sportsman наследуются поля min, sec, Fam,Vozrast.

Пусть в классе Personимеется полеdoublemin.

class Person

{

public:

..........

protected:

char Fam[30];

int Vozrast;

double min;

.............

}

void Sportsman::Putmin(int m)

{

min=m;// какое min из двух ??

......

}

Решение – указывать явно класс. Person::min=m;

В современных ООП языках множественное наследование не используется

7) Наследование на языке java

Наследуемый класс – подкласс (subclass), класс от которого наследуется – суперкласс (superclass).

Смысл protected такой же.

Вариант программы с дополнительным классом для задания main.

class Record // это суперкласс

{

protected int min,sec;

public void Init(int m,int s)

{

min=m;

sec=s;

}

public int Numbersec()

{

return min*60+sec;

}

}

class Sprint extends Record // extends наследует (расширяет) – это подкласс

{

private int dec;

public void Putdec(int d)

{

dec=d;

}

public int Numberdec()

{

int k;

k=min*60+sec;

return k*10+dec;

}

}

public class WorkSport // класс для main public файл WorkSport.java

{

public static void main (String args[] )

{

int p;

Record a=new Record(); // объект Record

a.Init(2, 40);

p=a.Numbersec();

System.out.printf("%d\n ", p);

Sprint b=new Sprint(); // объект Sprint

b.Init(1, 10); // вызов наследуемого метода;

// b.min=5; - ошибка вызов protected вне наследуемого класса

b.Putdec(6);

p=b.Numberdec();

System.out.printf("%d\n ", p);

}

}

На консоли:

160

706

8) Понятие пакета в java

Пакет позволяет разнести классы по отдельным файлам и обеспечить связь между полями и методами различных классов. Пакет – отдельный каталог, в котором размещается несколько файлов – public классов.

Пример создания пакета

package sports;

public class Record

{

protected int min,sec;

public void Init(int m,int s)

{

min=m;

sec=s;

}

public int Numbersec()

{

return min*60+sec;

}

}

package sports;

public class Sprint extends Record

{

private int dec;

public void Putdec(int d)

{

dec=d;

}

public int Numberdec()

{

int k;

k=min*60+sec;

return k*10+dec;

}

}

package sports;

public class worksport

{

public static void main (String args[] )

{

int p;

Record a=new Record();

a.Init(2, 40);

p=a.Numbersec();

System.out.printf("%d\n ", p);

Sprint b=new Sprint();

b.Init(1, 10);

b.Putdec(6);

p=b.Numberdec();

System.out.printf("%d\n ", p);

}

}

Размещение в директории sports 3 файла java:

lab5\\src\\sports\\

record.java sprint.java worksport.java

9) Наследование в языке C#. Модификатор protected.

Класс Record - базовый класс

Класс Sprint - производный класс

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

classRecord

{

protectedintmin,sec;// для доступа объектам производных классов

public void Init(int m, int s)

{

min = m;

sec = s;

}

public int Numbersec()

{

return min * 60 + sec;

}

}

class Sprint : Record // наследование

// C++ - public: Record Java - extends Record

{

private int dec;

public void Putdec(int d)

{

dec=d;

}

public int Numberdec()

{

int k;

k = 60 * min + sec; // protected!

return k*10+dec;

}

}

namespace ConsoleApplication1

{

class Program

{

static void Main(string[] args)

{

Record a=new Record();

Sprint b=new Sprint();

int m,k;

a.Init(5, 6);

b.Init(5, 6);// наследуемый метод

b.Putdec(8);

m=b.Numberdec();

k = b.Numbersec(); // вызов наследуемого метода

}

}

}

Множественное наследование в C# отсутствует

10) Перегрузка методов и явное указание класса вызываемого метода

на c++, Java, c#

Возможна перегрузка метода из базового класса в производном классе.

Пример – переопределение Init в Sprint

class Record

{

public:

Соседние файлы в папке Lesson 6