Наследование. Производные классы в C++, Java
Выбери формат для чтения
Загружаем конспект в формате pdf
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Наследование. Производные классы в C++, Java.
1) Производный класс, наследование полей и методов и добавление новых полей
и методов на С++
Имеем класс 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.
1
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 вызов наследуемого метода
}
2) Модификатор доступа 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();
2
protected:
int min;
int sec;
};
// вместо private:
К полям и методам из protected могут обращаться как методы Record, так и методы всех
наследуемых классов, (Sprint).
class A
{
......
protected:
int x;
....
};
......
class B : public A
{
....
}
class C : public B
{
....
};
Идентификатор x может появиться в любом методе классов A, B, C.
Объект базового класса, объявленный в производном не имеет доступа к private и
protected полям в C++.
class Record
{
public:
void Init (int m,int s);
int Numbersec();
protected:
int min;
private:
int sec;
};
int Record::Numbersec()
{
return min*60+sec;
}
void Record::Init(int m,int s)
{
3
min=m;
sec=s;
}
class Sprint : public Record // Sprint-наследник Record
{
public:
// новые методы Sprint
void Putdec(int d);
int Numberdec();
private:
// новое поле Sprint
int dec;
};
int Sprint::Numberdec()
{
int k;
k=Numbersec();
return k*10+dec;
}
void Sprint::Putdec(int d)
{
dec=d;
Record c;
c.min=5; // нет доступа к protected
c.sec=10; // нет доступа к prvate
}
В Java такой доступ есть
Java
class Record
{
protected int min,sec;
public Record(int m,int s)
{
min=m;
sec=s;
}
}
class Sprint extends Record
{
int dec;
4
public void Putdec(int d)
{
Record v=new Record(3,6);
v.min=8; // доступ к protected объекта базового класса из производного класса
dec=d;
}
public Sprint(int m,int s,int d)
{
super(m,s); // вызов конструктора базового класса
dec=d;
}
}
Инкапсуляция: public, private, protected разграничивают доступ к полям и методам.
3) Модификатор в заголовке наследуемого класса
class Sprint : public Record // 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
Пример закрытого наследования.
class Record
{
public:
void Init (int m,int s);
int Numbersec();
protected:
int min;
5
int
};
sec;
class Sprint : private Record
{
.....
int main(int argc, char* argv[])
{
int m,n;
Record x;
x.Init(1,8); // правильно
Sprint z;
z.Init(1,8); // min=1 sec=8 ошибка, Init теперь private
z.Putdec(4); // dec=4
правильно Putdec определена в Sprint как public
}
В Java используется только открытое наследование
4) Множественное наследование
В 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:
6
int Category; // разряд спортсмена
............
}
В классе Sportsman наследуются поля min, sec, Fam,Vozrast.
Пусть в классе Record имеется поле weight – вес, который поднял штангист и
такое же поле в классе Person – собственный вес человека.
class Record
{
public:
int weight;
.......
}
class Person
{
public:
char Fam[30];
int Vozrast;
int weight;
......
}
class Sportsman : public Person , public Record
{
public:
void Putweight(int x);
private:
int Category; // разряд спортсмена
............
}
void Sport5sman::Putweight(int x)
{
weight=m; // какое weight из двух ?
......
}
Решение – указывать явно класс. Person::weight=x;
Недостаток в данном операторе содержится информация что Sportsman производный от
Person.
В Java частично множественное наследование реализуется в интерфейсах.
7
5) Наследование на языке 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);
8
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);
}
}
6) Перегрузка методов и явное указание класса вызываемого метода
Возможна перегрузка метода из базового класса в производном классе.
Пример – переопределение Init в Sprint
class Record
{
public:
void Init (int m,int s);
int Numbersec();
protected:
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
{
public:
void Init(int m,int s, int d);
int Numberdec();
private:
int dec;
};
// перегрузка
void Sprint::Init(int m,int s, int d)
{
min=m;
sec=s;
9
dec=d;
}
int Sprint::Numberdec()
{
int k;
k=Numbersec();
return k*10+dec;
}
int main(int argc, char* argv[])
{
int m,n;
Record x;
x.Init(4,36); // min=4 sec=36
Sprint z;
z.Init(1,8,6); // min=1 sec=8 dec=6
}
Init(int m,int s,int d) в Sprint перекрывает наследуемый метод Init(int m,int s) в Record, не
работает:
int main(int argc, char* argv[])
{
Sprint z;
z.Init(1,8); // неправильное число аргументов
.........
На Java перекрытия нет, работают обе функции, наследуемая и определенная в классе.
Для вызова метода другого класса в C++ явно указывается принадлежность этому классу.
z.Record::Init(5,8); // min=5 sec=8 dec не определено
Вариант Init класса Sprint с вызовом Init базового класса.
void Sprint::Init(int m,int s, int d)
{
Record::Init(m,s); // вызов метода из другого класса
dec=d;
}
Возможна перегрузка метода в производном классе с такими же параметрами как в
базовом классе.
Добавлен метод:
void Sprint::Init(int m,int s)
{
min=m;
sec=s;
10
dec=1;
}
int main(int argc, char* argv[])
{
Sprint z;
z.Init(1,8,6); // min=1 sec=8 dec=6 вызов собственного метода
z.Init(1,8); // вызов второго собственного метода
Перегрузка методов производного класса в java
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
{
private int dec;
public void Putdec(int d)
{
dec=d;
}
public int Numberdec()
{
int k;
k=min*60+sec;
return k*10+dec;
}
public void Init(int m,int s,int d)
{
min=m;
sec=s;
dec=d;
}
}
11
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,5);
p=b.Numberdec();
System.out.printf("%d\n ", p);
}
}
В java перегруженный метод Init(int m,int s,int d) в Sprint не перекрывает наследуемый
метод Init(int m,int s) из Record
Sprint b=new Sprint();
b.Init(1, 10,5);
Sprint c=new Sprint();
c.Init(2, 15); // вызов наследуемой функции из Record с двумя аргументами
Вариант Init класса Sprint с вызовом наследуемого метода из Record
public void Init(int m,int s,int d)
{
super.Init(m,s); // вызов метода базового класса (аналог Record::Init(m,s) на C++
dec=d;
}
Если в Sprint нет перегруженного метода из Record Init(int m,int s) с двумя аргументами в
Java можно записать Init(m,s); без super. Понятно, где брать Init с двумя аргументами.
7) Конструкторы в производных классах
Конструкторы не наследуются. Правила работы с конструкторами производного класса.
Если в базовом классе есть конструктор без параметров, созданный в классе или
сгенерированный по умолчанию, при создании объектов производного класса сначала
вызывается конструктор базового класса.
При вызове деструктора, наоборот, сначала вызывается деструктор производного
класса, потом базового.
Варианты для конструкторов. Примеры для C++, на Java одинаково.
1. В базовом и производном классе нет конструкторов.
12
Создаются пустые конструкторы в базовом и производном классе.
int main(int argc, char* argv[])
{
// вызов пустых конструкторов по умолчанию
Record x; // min, sec не определены
Sprint z; // min,sec,dec не определены
}
2. В базовом классе создан конструктор без параметров, в производном конструктор
отсутствует.
class Record
{
private:
int min,sec;
public:
Record();
.........
};
Record::Record()
{
min=2;
sec=1;
}
class Sprint : public Record
{
private:
int dec;
public:
........
};
int main(int argc, char* argv[])
{
Record x; // вызов конструктора без параметров min=2, sec=1
Sprint z; // вызов базового конструктора Record min=2, sec=1, dec не определен
}
3. В базовом классе конструктор отсутстивует, в производном создан без параметров
class Record
{
private:
int min,sec;
13
public:
.........
};
class Sprint : public Record
{
private:
int dec;
public:
Sprint();
........
};
Sprint::Sprint()
{
min=3;
dec=2;
}
int main(int argc, char* argv[])
{
Record x; // пустой min, sec не определены
Sprint z; // min=3 dec=2 , sec – не определен
}
4. В базовом и производном классах есть конструкторы без параметров.
class Record
{
private:
int min,sec;
public:
Record();
.........
};
Record::Record()
{
min=5;
sec=4;
}
class Sprint : public Record
{
private:
int dec;
public:
Sprint();
14
........
};
Sprint::Sprint()
{
min=3;
dec=2;
}
int main(int argc, char* argv[])
{
Record x; // min=5 sec=4
Sprint z; // min=3 dec=2 sec=4 – вызов базового конструктора
}
5. В базовом есть конструктор без параметров, в производном с параметрами.
class Record
{
private:
int min,sec;
public:
Record();
.........
};
Record::Record()
{
min=1;
sec=2;
}
class Sprint : public Record
{
private:
int dec;
public:
Sprint(int d);
........
};
Sprint::Sprint(int d)
{
dec=d;
}
int main(int argc, char* argv[])
{
15
Record x; // min=1 sec=2
Sprint z(5); // min=1 sec=2 dec=5 // сначала вызывается конструктор базового класса
}
6. Если в базовом классе есть только конструктор с параметрами и в производном
создается конструктор с параметрами, то базовый конструктор должен быть явно
вызван.
class Record
{
private:
int min,sec;
public:
Record(int m,int s);
.........
};
Record::Record(int m,int s)
{
min=m;
sec=s;
}
class Sprint : public Record
{
private:
int dec;
public:
Sprint(int m,int s,int d);
........
};
Sprint::Sprint(int m,int s,int d) // ошибка, нет базового конструктора
{
min=m;
sec=s;
dec=d;
}
Sprint::Sprint(int m,int s, int d) :Record(m,s) // вызов базового конструктора в заголовке !
{
dec=d;
}
int main(int argc, char* argv[])
{
Record x(1,4); // min=1 sec=4
Sprint z(1,2,5); // min=1 sec=2 dec=5
16
}
Java:
package lab6_2;
class Record
{
protected int min,sec;
public Record(int m,int s)
{
min=m;
sec=s;
}
}
class Sprint extends Record
{
int dec;
public Sprint(int m,int s,int d)
{
super(m,s); // вызов конструктора базового класса
dec=d;
}
}
public class lab6
{
public static void main (String args[] )
{
Record a=new Record(7,8);
Sprint b=new Sprint(9,10,11);
}
}
8) Перегрузка оператора присваивания для производного класса на C++
Объектам базового класса можно присваивать объекты как базового так и производных
классов, при этом происходит копирование всех полей. Объектам производного класса
можно присвоить только объекты производного класса (нет значений дополнительных
полей)
int main(int argc, char* argv[])
{
int m,n;
Record x,y;
17
x.Init(4,36); // min=4 sec=36
y=x;
// y min=4 sec=36
Sprint z,w;
z.Init(1,8,6); // min=1 sec=8 dec=6
w=z;
// w min=1 sec=8 dec=6
x=z;
// x min=1 sec=8
w=y; // Ошибка не определено y.dec
}
На C++ возможна перегрузка оператора присваивания, аналогично +.
class Sprint : public Record
{
public:
void operator =(Record b); // void !
private:
int dec;
};
void Sprint::operator =(Record b) // перегрузка в классе Sprint
{
this->min=b.Getmin();
this->sec=b.Getsec();
// this->min=b.min; // ошибка !
// min – protected, но b объект базового класса, созданный
// в производном не имеет доступа ни к private ни к protected
this->dec=0;
// задание значений недостающему полю
}
int main(int argc, char* argv[])
{
Record x;
x.Init(3,8);
Sprint z;
z=x;
// min=3 sec=8 dec=0
}
9) Библиотека ввода-вывода на C++
Подключение библиотеки:
#include
cin – встроенный объект библиотеки ввода (входной поток связан с клавиатурой аналог scanf)
cout – встроенный объект библиотеки вывода связан с экраном (printf)
Перегруженный операторы << , >> для символьных величин – это логические сдвиги.
18
<< - вставка в выходной поток - ввод с консоли
>> - извлечение из входного потока - вывод на экран
#include "stdafx.h"
#include
#include
using namespace std;
//--------------------------------------------------------------------------int _tmain(int argc, _TCHAR* argv[])
{
char a,b,c;
a='1'; // 0011 0001 '1'
b=a << 2; // 1100 0100 'Д' логический сдвиг влево слева char
c=a >>1; // 0001 1000 логический сдвиг вправо слева char
// перегрузка << для вывода
cout << "abc\n";
int d,e;
d=45;
e=67;
cout <> ,<< для классов
#include "stdafx.h"
#include
#include
using namespace std;
class Record
{
public:
friend void operator << (ostream &o,Record r);
friend Record operator >> (istream &i,Record &r);
protected:
int min;
int sec;
};
void operator << (ostream &o,Record r)
{
cout << r.min << " " << r.sec;
}
Record operator >> (istream &o,Record &r)
{
cin >> r.min >> r.sec;
return r;
}
class Sprint : public Record
{
public:
friend void operator << (ostream &o,Sprint r);
friend Sprint operator >> (istream &i,Sprint &r);
private:
int dec;
};
void operator << (ostream &o,Sprint r)
{
21
cout << r.min << " " << r.sec<<" " <> (istream &o,Sprint &r)
{
cin >> r.min >> r.sec>>r.dec;
return r;
}
int _tmain(int argc, _TCHAR* argv[])
{
Record a;
cin>>a;
cout << a;
Sprint b;
cout << '\n';
cin>>b;
cout << b;
return 0;
}
Файловый ввод-вывод
#include "stdafx.h"
#include
#include
using namespace std;
class Record
{
public:
void Load(string Name);
void Save(string Name);
protected:
int min;
int sec;
};
void Record::Load(string Name)
{
ifstream fin;
// файловый ввод
fin.open(Name.c_str()); // перевод в массив char
fin >> min>>sec;
fin.close();
}
22
void Record::Save(string Name)
{
ofstream fout;
// файловый вывод
fout.open(Name.c_str());
fout << min<<" "<> min>>sec>>dec;
fin.close();
}
void Sprint::Save(string Name)
{
Record::Save(Name); // вызов метода базового класса запись min,sec b закрытие файла
ofstream fout;
fout.open(Name.c_str(),ios::app); // ios::app – режим дополнения дозапись dec
fout <<" " << dec;
fout.close();
}
int _tmain(int argc, _TCHAR* argv[])
{
Record a;
a.Load("rec.txt");
a.Save("rec1.txt");
Sprint b;
b.Load("spr.txt");
b.Save("spr1.txt");
return 0;
}
rec.txt
2 45
23
spr.txt
2 57 8
На Java можно вывести объекты целиком, переводя их в string, перегрузив встроенный метод
toString, имеющийся для всех объектов.
Пример вывода объекта в Java
class Record
{
protected int min,sec;
public Record(int m,int s)
{
min=m;
sec=s;
}
public String toString()
{
return min+" "+sec;
}
}
public static void main (String args[] )
{
Record a=new Record(7,8);
System.out.printf("%s",a.toString());
}
24