C++ và Lập trình hướng đối tượng - Chương 5: Dẫn xuất và thừa kế - Phạm Văn Ất

Chương trình dưới đây minh hoạ cách xây dựng hàm tạo sao chép cho lớp D có 2 lớp cơ sở là C và B (C là lớp cơ sở trực tiếp, còn B là cơ sở của C) . Ngoài ra D còn có một thuộc tính là đối tượng của lớp A. Chương trình này dựa trên chương trình trong mục 7.3 với 2 thay đổi:

+ Xây dựng thêm hàm tạo sao chép cho lớp D.

+ Thay đổi một số câu lệnh trong hàm main để sử dùng hàm tạo sao chép.

Để thấy rõ vai trò của hàm tạo sao chép chúng ta hãy so sánh kết quả nhận được trong 2 trường hợp: Có hàm tạo sao chép và bỏ đi hàm này.

 

doc42 trang | Chuyên mục: C/C++ | Chia sẻ: tuando | Lượt xem: 500 | Lượt tải: 0download
Tóm tắt nội dung C++ và Lập trình hướng đối tượng - Chương 5: Dẫn xuất và thừa kế - Phạm Văn Ất, để xem tài liệu hoàn chỉnh bạn click vào nút "TẢI VỀ" ở trên
ình chúng ta sẽ định nghĩa lớp GV2 dẫn xuất từ lớp GV, sau đó trong lớp BM sẽ thay GV bằng GV2. Có 2 chỗ cần bổ sung và một chỗ cần sửa đổi như sau:
1. Bổ sung trong lớp GV phương thức:
GV* getGV()
{
return this;
}
Phương thức này dùng để xây dựng toán tử gán cho lớp GV2.
2. Trong lớp BM thay GV bằng GV2. Điều này có thể đạt được bằng sửa chữa trực tiếp hoặc bằng một câu lệnh #define :
#define GV GV2
3. Định nghĩa thêm 2 lớp: LV (Luận văn) và GV2 (Lớp GV2 dẫn xuất từ lớp GV) như sau:
class LV // Luan van
{
private:
char tenlv[30]; // Ten luan van
char tensv[25]; // Ten sinh vien
299	300	
int nambv; // Nam bao ve luan van
public:
LV() ; // Hàm tạo
const LV& operator=(const LV& l) ; // Gán
void nhap() ; // Nhập
void xuat() ;
} ;
class GV2 : public GV
{
private:
int solv; // Số luận văn đã hướng dẫn
LV *lv; // Danh sách luận văn
public:
GV2(); // Hàm tạo
~GV2() ; // Hàm huỷ
GV2& operator=(GV2& g); // Gán
void nhap(); // Nhập
void xuat(); // Xuất
} ;
Chương trình nâng cấp như sau:
//CT5-12B
// Nang cap chuong trinh
// CT nang cap
#include 
#include 
#include 
#include 
class MON_HOC
{
private:
char tenmh[20];
int sotiet;
public:
MON_HOC()
{
tenmh[0]=sotiet=0;
}
const MON_HOC& operator=(const MON_HOC& m)
{
strcpy(this->tenmh,m.tenmh);
this->sotiet = m.sotiet;
return m;
}
void nhap()
{
cout << "\nTen mon hoc:";
fflush(stdin); gets(tenmh);
cout << "So tiet: " ;
cin >> sotiet;
}
void xuat()
{
cout << "\nTen mon hoc:" << tenmh
<< " so tiet: " << sotiet;
}
} ;
// Bo sung phuong thuc getGV cho lop GV
// dung de xay dung toan tu gan cho lop GV2
301	302	
class GV
{
private:
char ht[25]; // Ho ten
int ns; // Nam sinh
int sm; // So mon hoc co the day
MON_HOC *mh ; //Danh sach cac mon hoc
public:
GV()
{
ht[0]= ns= sm= 0 ;
mh = NULL;
}
~GV()
{
ht[0]= ns= sm= 0 ;
if (mh) delete mh;
}
// Bo sung phuong thuc getGV
GV* getGV()
{
return this;
}
int getsm()
{
return sm;
}
const GV& operator=(const GV& g);
void nhap();
void xuat();
} ;
const GV& GV::operator=(const GV& g)
{
strcpy(this->ht,g.ht);
this->ns=g.ns;
int n = g.sm;
this->sm = n;
if (this->mh) delete this->mh;
if (n)
{
this->mh = new MON_HOC[n+1];
for (int i=1; i<=n; ++i)
this->mh[i] = g.mh[i];
}
return g;
}
void GV::nhap()
{
cout << "\nHo ten: " ;
fflush(stdin); gets(ht);
cout << "Nam sinh: " ;
cin >> ns;
cout << "So mon co the giang day: " ;
cin >> sm;
if (this->mh) delete this->mh;
if (sm)
{
this->mh = new MON_HOC[sm+1];
303	304	
for (int i=1; i<=sm; ++i)
this->mh[i].nhap();
}
}
void GV::xuat()
{
cout << "\nHo ten: " << ht ;
cout << "\nNam sinh: " << ns ;
cout << "\nSo mon co the giang day: " << sm;
if (sm)
{
cout << "\n Do la: ";
for (int i=1; i<=sm; ++i)
this->mh[i].xuat();
}
}
// Bo sung cac lop LV va GV2
class LV // Luan van
{
private:
char tenlv[30]; // Ten luan van
char tensv[25]; // Ten sinh vien
int nambv; // Nam bao ve luan van
public:
LV()
{
tenlv[0]=tensv[0] = nambv = 0;
}
const LV& operator=(const LV& l)
{
strcpy(this->tenlv,l.tenlv);
strcpy(this->tensv,l.tensv);
this->nambv = l.nambv ;
return l;
}
void nhap()
{
cout << "\nTen luan van:";
fflush(stdin); gets(tenlv);
cout << "Ten sinh vien:";
fflush(stdin); gets(tensv);
cout << "Nam bao ve: " ;
cin >> nambv ;
}
void xuat()
{
cout << "\nTen lan van:" << tenlv
<< " Sinh vien: " << tensv
<< " Nam bao ve: " << nambv;
}
} ;
class GV2 : public GV
{
private:
int solv;
LV *lv;
305	306	
public:
GV2():GV()
{
solv = 0 ;
lv = NULL;
}
~GV2()
{
if (solv) delete lv;
}
GV2& operator=(GV2& g);
void nhap();
void xuat();
} ;
GV2& GV2::operator=(GV2& g)
{
GV *g1, *g2;
g1 = this->getGV();
g2 = g.getGV();
*g1 = *g2;
int n = g.solv;
this->solv = n;
if (this->lv) delete this->lv;
if (n)
{
this->lv = new LV[n+1];
for (int i=1; i<=n; ++i)
this->lv[i] = g.lv[i];
}
return g;
}
void GV2::nhap()
{
GV::nhap();
cout << "So luan van da huong dan: " ;
cin >> solv;
if (this->lv) delete this->lv;
if (solv)
{
this->lv = new LV[solv+1];
for (int i=1; i<=solv; ++i)
this->lv[i].nhap();
}
}
void GV2::xuat()
{
GV::xuat();
cout << "\nSo luan van da huong dan: " << solv;
if (solv)
{
cout << "\n Do la: ";
for (int i=1; i<=solv; ++i)
this->lv[i].xuat();
}
}
// Sua lop BM: thay GV bang GV2
307	308	
#define GV GV2
class BM // Bo mon
{
private:
char tenbm[20];
int n; // So giao vien
GV *gv; // Danh sach giao vien
public:
BM()
{
tenbm[0] = n = 0;
gv = NULL;
}
void nhap();
void xuat();
void sapxep();
} ;
void BM::nhap()
{
cout << "\n\nTen bo mon: " ;
fflush(stdin); gets(tenbm);
cout << "So giao vien: ";
cin >> n;
if (gv) delete gv;
if (n)
{
gv = new GV[n+1];
for (int i=1; i<=n; ++i)
gv[i].nhap();
}
}
void BM::xuat()
{
cout << "\nBo mon: " << tenbm;
cout << "\nSo giao vien: " << n;
if (n)
{
cout << "\n Danh sach giao vien cua bo mon:";
for (int i=1; i<=n; ++i)
gv[i].xuat();
}
}
void BM::sapxep()
{
GV tg;
int i,j;
if (n)
for (i=1;i<n;++i)
for (j=i+1;j<=n;++j)
if (gv[i].getsm()<gv[j].getsm())
{
tg=gv[i]; gv[i]=gv[j]; gv[j]=tg;
}
}
#undef GV
void main()
{
BM b;
b.nhap();
309	310	
b.sapxep();
b.xuat();
getch();
}
§ 11. Từ khái quát đến cụ thể
Tính thừa kế cũng thường dùng để thiết kế các bài toán theo hướng từ khái quát đến cụ thể, từ chung đến riêng. Đầu tiên đưa ra các lớp để mô tả những đối tượng chung, sau đó dẫn xuất tới các đối tượng ngày một cụ thể hơn.
Một trường hợp khác cũng thường gặp là: Quản lý nhiều thực thể có những phần dữ liệu chung. Khi đó ta có thể xây dựng một lớp cơ sở gồm các phần dữ liệu chung. Mỗi thực thể sẽ được mô tả bằng một lớp dẫn xuất từ lớp cơ sở này.
Sau đây là một số ví dụ minh hoạ:
Ví dụ 1 (minh hoạ tư tưởng đi từ khái quát đến cụ thể) : Giả sử cần quản lý sinh viên của một trường đại học. Khi đó ta có thể bắt đầu từ lớp SINH_VIEN (Sinh viên). Sau đó dùng nó làm cơ sở để dẫn xuất tới các lớp mô tả các đối tượng sinh viên cụ thể hơn, ví dụ: SV Tin, SV Toán, SV Luật, SV Du lịch, ...
Các bài toán kiểu như vậy rất thường gặp trong thực tế.
Ví dụ 2 (minh hoạ phần chung của nhiều thực thể). Giả sử cần xây dựng phần mềm để thực hiện các phép tính về ma trân vuông và véc tơ cấp n. Ta có nhận xét là n chung cho cả véc tơ và ma trận. Hơn nữa nó còn chung cho tất cả các ma trận và véc tơ cùng xét trong bài toán. Vì vậy có thể định nghĩa một lớp cơ sở chỉ có một thuộc tính tĩnh (static) n. Các lớp ma trận, véc tơ dẫn xuất từ lớp này và sử dụng chung cùng một giá trị n.
Dưới đây là chương trình thực hiện các phép toán ma trận, véc tơ. Chương trình được tổ chức thành 3 lớp:
Lớp CAP (Cấp ma trận, véc tơ) gồm một thành phần tĩnh n và phương thức nhập n.
Lớp VT (Véc tơ) có một thuộc tính là mảng một chiều (chứa các phần tử của véc tơ) và các phương thức nhập, xuất.
Lớp MT (Ma trận) có một thuộc tính là mảng 2 chiều (chứa các phần tử của ma trận) , các phương thức nhập, xuất và nhân. Lớp MT là bạn của lớp VT.
Chương trình sẽ nhập một ma trận, nhập một véc tơ và tính tích của chúng.
//CT5-13
// ma tran vec to
// Dùng thuộc tính static
#include 
#include 
#include 
#include 
class CAP;
class MT;
class VT;
class CAP
{
private:
static int n;
public:
void nhap()
{
int ch;
if (n==0)
{
cout > n;
}
311	312	
else
{
cout <<"\n Hien n = " << n;
cout << "\n Co thay doi n? - C/K";
ch=toupper(getch());
if (ch=='C')
{
cout > n;
}
}
}
int getN()
{
return n;
}
} ;
int CAP::n=0;
class MT : public CAP
{
private:
double a[20][20];
public:
void nhap();
void xuat();
VT operator*(VT x);
};
class VT : public CAP
{
private:
double x[20];
public:
friend class MT;
void nhap();
void xuat();
};
void MT::nhap()
{
int n,i,j;
n = this->getN();
if (n==0)
{
this->CAP::nhap();
n = this->getN();
}
for (i=1; i<=n; ++i)
for (j=1; j<=n; ++j)
{
cout << " PT hang " << i << " cot " << j << " = ";
cin >> a[i][j];
}
}
void MT::xuat()
{
int n,i,j;
n = this->getN();
if (n)
for (int i=1; i<=n; ++i)
{
313	314	
cout << "\n" ;
for (int j=1; j<=n; ++j)
cout << a[i][j] << " ";
}
}
VT MT::operator*(VT x)
{
VT y;
int n,i,j;
n = this->getN();
for (i=1; i<=n; ++i)
{
y.x[i]=0;
for (j=1; j<=n; ++j)
y.x[i] += a[i][j]*x.x[j];
}
return y;
}
void VT::nhap()
{
int n,i;
n = this->getN();
if (n==0)
{
this->CAP::nhap();
n = this->getN();
}
for (i=1; i<=n; ++i)
{
cout << " PT thu " << i << " = ";
cin >> x[i];
}
}
void VT::xuat()
{
int n,i;
n = this->getN();
if (n)
{
cout << "\n";
for (int i=1; i<=n; ++i)
{
cout << x[i] << " ";
}
}
}
void main()
{
MT a; VT x,y;
clrscr();
cout<<"\nNhap ma tran A:";
a.nhap();
cout<<"\n\nNhap Vec to X:\n";
x.nhap();
y = a*x;
cout<<"\n\nMa tran A";
a.xuat();
315	316	
cout<<"\n\nVec to X";
x.xuat();
cout<<"\n\nVec to Y=AX";
y.xuat();
getch();
}
§ 12. Toàn thể và bộ phận
Thông thường khi xem xét, giải quyết một bài toán, ta thường chia nó thành các bài toán nhỏ hơn. Nói cách khác: Một bài toán lớn bao gồm nhiều bài toán bộ phận. Khi đó ta có thể định nghĩa các lớp cho các bài toán bộ phận. Lớp cho bài toán chung được dẫn xuất từ các lớp nói trên.
Xét một thí dụ đơn giản là bài toán quản lý thư viện. Nó gồm 2 bài toán bộ phận là quản lý sách và quản lý đọc giả. Chúng ta sẽ xây dựng lớp SACH và lớp DOC_GIA. Sau đó dùng các lớp này làm cơ sở để xây dựng lớp THU_VIEN.

File đính kèm:

  • docc_va_lap_trinh_huong_doi_tuong_chuong_5_dan_xuat_va_thua_ke.doc
Tài liệu liên quan