- •Void Putdec(int d);
- •Int Numberdec();
- •Int dec;
- •Void Init (int m,int s);
- •1. В базовом и производном классе нет конструкторов.
- •2. В базовом классе создан конструктор без параметров, в производном конструктор отсутствует.
- •3. В базовом классе конструктор отсутстивует, в производном создан без параметров
- •4. В базовом и производном классах есть конструкторы без параметров.
- •5. В базовом есть конструктор без параметров, в производном с параметрами.
- •6. Если в базовом классе есть конструктор с параметрами, в производном классе должен быть с параметрами и базовый конструктор должен быть явно вызван.
- •Icalculate.Cs
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: