Розміри породжених об’єктів
Припустімо, що об’єкти, які зберігаються в пам’яті, мають різні розміри. Чому і коли таке може бути? Типовою причиною є створення породжених класів.
Згадаємо програму 17.6, у якій реалізувалося успадковування. Там у нас був клас employee, що виступав як базовий для класів manager, scientist та laborer. Об’єкти всіх цих трьох породжених класів мають різні розміри, оскільки містять різні об’єми даних.
Нам хотілося б записати дані зі списку, що містить всі три типи породжених об’єктів, використовуючи простий цикл і метод write() класу ofstream. Але нам потрібно знати розміри об’єктів, щоб передати їх в якості другого аргументу цієї функції.
Нехай існує масив вказівників arrap[], що зберігає посилання на об’єкти типу employee. Вказівники можуть посилатися таким чином і на всі три породжені класи. При використанні віртуальних функцій можна використовувати вирази типу:
arrap->putdata();
Тоді під час роботи програми буде поставлена на виконання версія putdata(), що відповідає об’єкту, на який посилається вказівник, а не та, що відповідає базовому класу. Але чи можна використовувати sizeof() для обчислення розміру аргументу за посиланням? Тобто, чи можна написати такий вираз
ouf.write((char*)arrap[j],sizeof(*arrap[j]);
На жаль, ні, оскільки sizeof() не є віртуальною функцією. Вона не знає, що потрібно звертатися до типу об’єкту, на який посилається вказівник, а не на тип самого вказівника. Ця функція завжди буде повертати розмір об’єкту базового класу.
Використання функції typeid()
Як же нам знайти розмір об’єкту, якщо ми маємо лише вказівник на нього? Одним з розв’язків є використання функції typeid(). Її можна використовувати для визначення класу об’єкту, підставляючи ім’я цього класу в якості аргументу sizeof().
Наш наступний приклад показує, як це все працює. Встановивши розмір об’єкту, ми можемо використати його у функції write() для запису в файл.
До програми 17.6 додамо нескладний інтерфейс користувача. Деякі специфічні методи зроблені віртуальними, щоб можна було скористатися масивом вказівників на об’єкти. В програму також включені деякі прийоми виявлення помилок, описані раніше.
Далі приведено текст програми 26.13.
#include<fstream>
#include<iostream>
#include<typeinfo> //для typeid()
#include<conio>
using namespace std;
#include<process.h> //для exit()
const int LEN=32; //максимальна довжина прізвища
const int MAXEN=100;//максимальне число працівників
enum employee_type {tmanager,tscientist,tlaborer};
/////////////
class employee
{private:
char name[LEN];
unsigned long number;
static int n;
static employee* arrap[]; //масив вказівників на клас робітника
public:
virtual void getdata()
{//cin.ignore(10,'/n');
cout<<"Vvedit pip: ";cin>>name;
cout<<"Vvedit nomer: ";cin>>number;
}
virtual void putdata()
{cout<<"\n PIP: "<<name;
cout<<"\n Nomer: "<<number;
}
virtual employee_type get_type();//одержати тип
static void add(); //додати працівника
static void display();//вивести дані про всіх
static void read();//читання з файлу
static void write(); //запис у файл
};
//статичні змінні
int employee::n;
employee* employee::arrap[MAXEN];
////////////////
class manager:public employee
{ private:
char title[LEN];
double dues;
public:
void getdata()
{employee::getdata();
cout<<"Vvedit tytul: ";cin>>title;
cout<<"Vvedit podatok: ";cin>>dues;
}
void putdata()
{employee::putdata();
cout<<"\n Tytul: "<<title;
cout<<"\n Podatok: "<<dues;
}
};
/////////////
class scientist:public employee
{private:
int pubs;
public:
void getdata()
{//реалізувати метод для цього класу
}
void putdata()
{//реалізувати метод для цього класу
}
};
//////////
class laborer:public employee
{
};
///////////
//додати працівника до списку
void employee::add()
{char ch;
//реалізувати меню вводу різних типів працівників
arrap[n++]->getdata();
}
void employee::display()
{//реалізувати меню відображення різних типів працівників
arrap[j]->putdata();
cout<<endl;
}
}
/////////
//повернення типу обєкту
employee_type employee::get_type()
{
if (typeid(*this)==typeid(manager))
return tmanager;
else if(typeid(*this)==typeid(scientist))
return tscientist;
else if(typeid(*this)==typeid(laborer))
return tlaborer;
else
{cerr<<"\n Nepravylnyj typ robitnyka ";exit(1);}
return tmanager;
}
//==============
//записати всі обєкти з памяті в файл
void employee::write()
{int size,j;
cout<<"Ide zapys "<<n<<" robitnykiv \n";
ofstream ouf;
employee_type etype;
ouf.open("EMPLOY.DAT",ios::trunc|ios::binary);
if(!ouf)
{cout<<"\nNemozlyvo vidkryty file\n";getch();return;}
for(j=0;j<n;j++)
{etype=arrap[j]->get_type();
ouf.write( (char*)&etype,sizeof(etype));
switch(etype)
{case tmanager:size=sizeof(manager);break;
case tscientist:size=sizeof(scientist);break;
case tlaborer: size=sizeof(laborer);break;
}
ouf.write((char*)arrap[j],size);
if(!ouf)
{cout<<"\nZapys u file nemozlyvyj\n";return;}
}
}
//----------
//Читання всіх даних з файлу в память
void employee::read()
{int size;
employee_type etype;
ifstream inf;
inf.open("EMPLOY.DAT",ios::binary);
if(!inf)
{cout <<"\nNemozlyvo vidkryty file\n";getch();return;}
n=0; //в памяті працівників нема
while(true)
{inf.read((char*)&etype,sizeof(etype));
if(inf.eof()) //вихід з циклу по eof
break;
if(!inf)
{cout <<"\nNemozlyvo chytaty type\n";getch();return;}
switch(etype)
{case tmanager:
arrap[n]=new manager;
size=sizeof(manager);
break;
case tscientist:
arrap[n]=new scientist;
size=sizeof(scientist);
break;
case tlaborer:
arrap[n]=new laborer;
size=sizeof(laborer);
break;
defaults:cout<<"\nNevidomyj typ u faili\n";getch();return;
}
inf.read((char*)arrap[n],size);
if(!inf)
{cout<<"\nChytanna z failu nemozlyve\n";getch();return;}
n++;
}//end while
cout<<"Ide chytanna "<<n<<" pracivnykiv\n";
}
/////////////
void main()
{char ch;
while(true)
{
cout<<"a - Dodavanna vidomostej"
"\nd - Vyvesty vidomosti pro vsih"
"\nw - Zapysaty vsi dani v fail"
"\nr - Prochytaty vsi dani z failu"
"\nx - Vyhid"
"\n Vash vybir:";
cin>>ch;
//реалізувати реакцію на ввід
} ; //end while
}
Програма 26.13
Дата добавления: 2015-08-26; просмотров: 604;