Tải bản đầy đủ (.pdf) (37 trang)

Lớp và đối tượng

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (280.03 KB, 37 trang )


53



CHƯƠNG 3
LỚP VÀ ðỐI TƯỢNG
Lớp (class) là khái niệm trung tâm trong lập trình hướng ñối tượng, nó là sự mở
rộng của khái niệm cấu trúc (struct) của C. Ngoài các thành phần dữ liệu (giống như
cấu trúc), lớp còn chứa các thành phần hàm, còn gọi là phương thức (method) hay
hàm thành viên (member function). Cũng giống như cấu trúc, lớp có thể ñược xem
như một kiểu dữ liệu. Vì vậy lớp còn ñược gọi là kiểu ñối tượng và do ñó có thể dùng
ñể khai báo các biến, mảng ñối tượng (như thể dùng kiểu int ñể khai báo các biến
mảng nguyên). Như vậy từ một lớp có thể tạo ra (bằng cách khai báo) nhiều ñối
tượng (object) khác nhau. Mỗi ñối tượng có vùng nhớ riêng của mình.
Chương này sẽ trình bầy cách ñịnh nghĩa lớp, cách xây dựng các phương thức,
giải thích về phạm vi truy nhập, sử dụng các thành phần của lớp, cách khai báo biến,
mảng cấu trúc, lời gọi tới các phương thức, …
§
1. ðỊNH NGHĨA LỚP
1.1. ðịnh nghĩa lớp
Lớp ñược ñịnh nghĩa theo mẫu:
class tên_lớp
{
// Khai báo các thành phần dữ liệu (thuộc tính)
// Khai báo các phương thức
} ;
// ðịnh nghĩa (xây dựng) các phương thức
Chú ý:
Thuộc tính của lớp có thể là các biến, mảng, con trỏ có kiểu chuẩn (int, float, char,
char*, long,...) hoặc kiểu ngoài chuẩn ñã ñịnh nghĩa trước (cấu trúc, hợp, lớp, ...) .


Thuộc tính của lớp không thể có kiểu của chính lớp ñó, nhưng có thể là kiểu con trỏ
lớp này, ví dụ:
class A
{
A x ; // Không cho phép, vì x có kiểu lớp A
A *p ; // Cho phép , vì p là con trỏ kiểu lớp A
...
};
1.2. Các thành phần của lớp
Khi báo các thành phần của lớp (thuộc tính và phương thức) có thể dùng các từ
khoá private và public ñể quy ñịnh phạm vi sử dụng của các thành phần. Nếu không

54

quy ñịnh cụ thể (không dùng các từ khoá private hay public) thì C
++
mặc ñịnh hiểu ñó
là private.
Các thành phần private (riêng) chỉ ñược sử dụng bên trong lớp (trong thân của các
phương thức của lớp). Các hàm không phải là phương thức của lớp không ñược phép
sử dụng các thành phần này. Các thành phần public (chung) ñược phép sử dụng ở cả
bên trong và bên ngoài lớp.
Các thành phần dữ liệu thường khai báo là private (nhưng không bắt buộc) ñể bảo
ñảm tính giấu kín, bảo vệ an toàn dữ liệu của lớp, không cho phép các hàm bên ngoài
xâm nhập vào dữ liệu của lớp. Các phương thức thường khai báo là public ñể chúng
có thể ñược gọi tới (sử dụng) từ các hàm khác trong chương trình.
Các phương thức có thể ñược xây dựng bên ngoài hoặc bên trong ñịnh nghĩa lớp.
Thông thường, các phương thức ngắn ñược viết bên trong ñịnh nghĩa lớp, còn các
phương thức dài thì viết bên ngoài ñịnh nghĩa lớp.
Trong thân phương thức của một lớp (giả sử lớp A) có thể sử dụng:

+ Các thuộc tính của lớp A
+ Các phương thức của lớp A
+ Các hàm tự lập trong chương trình. Vì phạm vi sử dụng của hàm là toàn chương
trình.
Giá trị trả về của phương thức có thể có kiểu bất kỳ (chuẩn và ngoài chuẩn)
Ví dụ sau sẽ minh hoạ các ñiều nói trên. Chúng ta sẽ ñịnh nghĩa lớp ñể mô tả và
xử lý các ñiểm trên màn hình ñồ hoạ. Lớp ñược ñặt tên là DIEM.
+ Các thuộc tính của lớp gồm:
int x ; // hoành ñộ (cột)
int y ; // tung ñộ (hàng)
int m ; // mầu
+ Các phương thức:
Nhập dữ liệu một ñiểm
Hiển thị một ñiểm
Ẩn một ñiểm
Lớp ñiểm ñược xây dựng như sau:
class DIEM
{
int x, y, m ;
public:
void nhapsl() ;
void hien() ;
void an()
{

55

putpixel(x, y, getbkcolor());
}
} ;

void DIEM::nhap()
{
cout << “\nNhập hoành ñộ (cột) và tung ñộ (hàng) của ñiểm: “
cin >> x >> y ;
cout << “\nNhập mã mầu của ñiểm: “
cin >> m ;
}
void DIEM::hien()
{
int mau_ht ;
mau_ht = getcolor();
putpixel(x, y, m);
setcolor(mau_ht);
}
Qua ví dụ trên có thể rút ra một số ñiều cần nhớ sau:
+ Trong cả ba phương thức (dù viết trong hay viết ngoài ñịnh nghĩa lớp) ñều ñược
phép truy nhập ñến các thuộc tính x, y và m của lớp.
+ Các phương thức viết bên trong ñịnh nghĩa lớp (như phương thức an) ñược viết
như một hàm thông thường.
+ Khi xây dựng các phương thức bên ngoài lớp (như các phương thức nhap, hien)
cần dùng thêm tên lớp và toán tử phạm vi :: ñặt ngay trước tên phương thức ñể quy
ñịnh rõ ñây là phương thức của lớp nào.
§
2. BIẾN, MẢNG ðỐI TƯỢNG
2.1. Khai báo biến, mảng ñối tượng
Một lớp (sau khi ñịnh nghĩa) có thể xem như một kiểu ñối tượng và có thể dùng ñể
khai báo các biến, mảng ñối tượng. Cách khai báo biến, mảng ñối tượng cũng giống
như khai báo biến, mảng các kiểu khác (như int, float, cấu trúc, hợp, ...), theo mẫu
sau:
Tên_lớp danh sách ñối ;

Tên_lớp danh sách mảng ;
Ví dụ sử dụng lớp DIEM ở
§
1, có thể khai báo các biến, mảng DIEM như sau:

56

DIEM d1, d2, d3 ; // Khai báo 3 biến ñối tượng d1, d2, d3
DIEM d[20] ; // Khai báo mảng ñối tượng d gồm 20 phần tử
Mỗi ñối tượng sau khi khai báo sẽ ñược cấp phát một vùng nhớ riêng ñể chứa các
thuộc tính của chúng. Chú ý rằng sẽ không có vùng nhớ riêng ñể chứa các phương
thức cho mỗi ñối tượng. Các phương thức sẽ ñược sử dụng chung cho tất cả các ñối
tượng cùng lớp. Như vậy về bộ nhớ ñược cấp phát thì ñối tượng giống cấu trúc.
Trong trường hợp này:
sizeof(d1) = sizeof(d2) = sizeof(d3) = 3*sizeof(int) = 6
sizeof(d) = 20*6 = 120
2.2. Thuộc tính của ñối tượng
Trong ví dụ trên, mỗi ñối tượng d1, d2, d3 và mỗi phần tử d[i] ñều có ba
thuộc tính là x, y, m. Mỗi thuộc tính ñều thuộc về một ñối tượng, vì vậy không
thể viết tên thuộc một cách riêng rẽ mà bao giờ cũng phải có tên ñối tượng ñi
kèm, giống như cách viết trong cấu trúc của C. Nói cách khác, cách viết thuộc
tính của ñối tượng như sau:
Tên_ñối_tượng.Tên_thuộc_tính
Với các ñối tượng d1, d2, d3 và mảng d, có thể viết như sau:
d1.x // Thuộc tính x của ñối tượng d1
d2.x // Thuộc tính x của ñối tượng d2
d3.y // Thuộc tính y của ñối tượng d3
d[2].m // Thuộc tính m của phần tử d[2]
d1.x = 100 ; // Gán 100 cho d1.x
d2.y = d1.x; // Gán d1.x cho d2.y

2.3. Sử dụng các phương thức
Cũng giống như hàm, một phương thức ñược sử dụng thông qua lời gọi. Tuy
nhiên trong lời gọi phương thức bao giờ cũng phải có tên ñối tượng ñể chỉ rõ
phương thức thực hiện trên các thuộc tính của ñối tượng nào. Ví dụ lời gọi:
d1.nhapsl();
sẽ thực hiện nhập số liệu vào các thành phần d1.x, d1.y và d1.m
Câu lệnh:
d[3].nhapsl() ;
sẽ thực hiện nhập số liệu vào các thành phần d[3].x, d[3].y và d[3].m
Chúng ta sẽ minh hoạ các ñiều nói trên bằng một chương trình ñơn giản sử
dụng lớp DIEM ñể nhập ba ñiểm, hiện rồi ẩn các ñiểm vừa nhập.
#include <conio.h>
#include <iostream.h>
#include <graphics.h>

57

class DIEM
{
int x, y, m ;
public:
void nhapsl();
void an()
{
putpixel(x,y,getbkcolor());
}
void hien();
};
void DIEM::nhapsl()
{

cout << "\nNhap hoanh do (cot) va tung do (hang) cua diem: " ;
cin >> x >> y ;
cout << " \nNhap ma mau cua diem: " ;
cin >> m ;
}
void DIEM::hien()
{
int mau_ht;
mau_ht = getcolor() ;
putpixel(x,y,m);
setcolor(mau_ht);
}
void kd_do_hoa() // Khởi ñộng ñồ họa
{
int mh, mode ;
mh=mode=0;
initgraph(&mh, &mode, "");
}
void main()
{
DIEM d1, d2, d3 ;
d1.nhapsl();
d2.nhapsl();

58

d3.nhapsl();
kd_do_hoa();
setbkcolor(BLACK);
d1.hien();

d2.hien();
d3.hien();
getch();
d1.an();
d2.an();
d3.an();
getch();
closegraph();
}
§
3. CON TRỎ ðỐI TƯỢNG
Con trỏ ñối tượng dùng ñể chứa ñịa chỉ của biến, mảng ñối tượng. Nó ñược khai
báo như sau:
Tên_lớp *Tên_con_trỏ ;
Ví dụ dùng lớp DIEM có thể khai báo:
DIEM *p1 , *p2, *p3 ; // Khai báo 3 con trỏ p1, p2, p3
DIEM d1, d2 ; // Khai báo 2 ñối tượng d1, d2
DIEM d[20] ; // Khai báo mảng ñối tượng
và có thể thực hiện các câu lệnh:
p1 = &d2 ; // p1 chứa ñịa chỉ của d2 , hay p1 trỏ tới d2
p2 = d ; // p2 trỏ tới ñầu mảng d
p3 = new DIEM // Tạo một ñối tượng và chứa ñịa chỉ của nó vào p3
ðể sử dụng thuộc tính của ñối tượng thông qua con trỏ, ta viết như sau:
Tên_con_trỏ->Tên_thuộc_tính
Chú ý: Nếu con trỏ chứa ñịa chỉ ñầu của mảng, có thể dùng con trỏ như tên mảng.
Như vậy sau khi thực hiện các câu lệnh trên thì:
p1->x và d2.x là như nhau
p2[i].y và d[i].y là như nhau
Quy tắc sử dụng thuộc tính:
ðể sử dụng một thuộc tính của ñối tượng ta phải dùng phép . hoặc phép -> . Trong

chương trình, không cho phép viết tên thuộc tính một cách ñơn ñộc mà phải ñi kèm
tên ñối tượng hoặc tên con trỏ theo các mẫu sau:

59

Tên_ñối_tượng.Tên_thuộc_tính
Tên_con_trỏ->Tên_thuộc_tính
Tên_mảng_ñối_tượng[chỉ_số].Tên_thuộc_tính
Tên_con_trỏ[chỉ_số].Tên_thuộc_tính
Chương trình dưới ñây cũng sử dụng lớp DIEM ñể nhập một dẫy ñiểm, hiển thị và
ẩn các ñiểm vừa nhập.
Chương trình dùng một con trỏ kiểu DIEM và dùng toán tử new ñể tạo ra một dẫy
ñối tượng.
#include <conio.h>
#include <iostream.h>
#include <graphics.h>
class DIEM
{
int x, y, m ;
public:
void nhapsl();
void an()
{
putpixel(x,y,getbkcolor());
}
void hien();
};
void DIEM::nhapsl()
{
cout <<"\nNhap hoanh do (cot) va tung do (hang) cua diem:" ;

cin >> x >> y ;
cout << " \nNhap ma mau cua diem: " ;
cin >> m ;
}
void DIEM::hien()
{
int mau_ht;
mau_ht = getcolor() ;
putpixel(x,y,m);
setcolor(mau_ht);
}

60

void kd_do_hoa()
{
int mh, mode ;
mh=mode=0;
initgraph(&mh, &mode, "");
}
void main()
{
DIEM *p;
int i, n;
cout << "So diem: " ;
cin >> n;
p = new DIEM[n+1];
for (i=1; i<=n; ++i)
p[i].nhapsl();
kd_do_hoa();

for (i=1; i<=n; ++i)
p[i].hien();
for (i=1; i<=n; ++i)
p[i].an();
getch();
closegraph();
}
§
4. ðỐI CỦA PHƯƠNG THỨC, CON TRỎ THIS
4.1. Con trỏ this là ñối thứ nhất của phương thức
Chúng ta hãy xem lại phương thức nhapsl của lớp DIEM
void DIEM::nhapsl()
{
cout <<"\nNhap hoanh do (cot) va tung do (hang) cua diem:" ;
cin >> x >> y ;
cout << " \nNhap ma mau cua diem: " ;
cin >> m ;
}
Rõ ràng trong phương thức này chúng ta sử dụng tên các thuộc tính x, y và m một
cách ñơn ñộc. ðiều này có vẻ như mâu thuẫn với quy tắc sử dụng thuộc tính nêu

61

trong mục trước. Song sự thể là: C
++
sử dụng con trỏ ñặc biệt this trong các
phương thức. Các thuộc tính viết trong phương thức ñược hiểu là thuộc một ñối
tượng do con trỏ this trỏ tới. Phương thức nhapsl có thể viết một cách tường
minh như sau:
void DIEM::nhapsl()

{
cout << "\nNhap hoanh do (cot) va tung do (hang) cua diem:" ;
cin >> this->x >> this->y ;
cout << " \nNhap ma mau cua diem: " ;
cin >> this->m ;
}
Từ góc ñộ hàm số có thể kết luận rằng: Phương thức của lớp bao giờ cũng có ít
nhất một ñối là con trỏ this và nó luôn luôn là ñối ñầu tiên của phương thức.
4.2. Tham số ứng với ñối con trỏ this
Xét một lời gọi tới phương thức nhapsl:
DIEM d1;
d1.nhapsl() ;
Trong trường hợp này tham số truyền cho con trỏ this chính là ñịa chỉ của d1:
this = &d1
Do ñó:
this->x chính là d1.x
this->y chính là d1.y
this->m chính là d1.m
*this chính là d1
Như vậy câu lệnh:
d1.nhapsl() ;
sẽ nhập dữ liệu cho các thuộc tính của ñối tượng d1. Từ ñó có thể rút ra kết luận:
Tham số truyền cho ñối con trỏ this chính là ñịa chỉ của ñối tượng ñi kèm với phương
thức trong lời gọi phương thức.
4.3. Các ñối khác của phương thức
Ngoài ñối ñặc biệt this (ñối này không xuất hiện một cách tường minh), phương
thức còn có các ñối khác ñược khai báo như trong các hàm. ðối của phương thức có
thể có kiểu bất kỳ (chuẩn và ngoài chuẩn).
Ví dụ ñể xây dựng phương thức vẽ ñường thẳng qua 2 ñiểm ta cần ñưa vào 3 ñối:
Hai ñối là 2 biến kiểu DIEM, ñối thứ ba kiểu nguyên xác ñịnh mã mầu. Vì ñã có ñối

ngầm ñịnh this là ñối thứ nhất, nên chỉ cần khai báo thêm 2 ñối. Phương thức có thể
viết như sau:

62

void DIEM::doan_thang(DIEM d2, int mau)
{
int mau_ht;
mau_ht = getcolor();
setcolor(mau);
line(this->x, this->y, d2.x, d2.y);
setcolor(mau_ht);
}
Chương trình sau minh hoạ các phương thức có nhiều ñối. Ta vẫn dùng lớp DIEM
nhưng có một số thay ñổi:
+ Bỏ thuộc tính m (mầu)
+ Bỏ các phương thức hien và an
+ ðưa vào bốn phương thức mới:
ve_ doan_thang (Vẽ ñoạn thẳng qua 2 ñiểm)
ve_tam_giac (Vẽ tam giác qua 3 ñiểm)
do_dai (Tính ñộ dài của ñoạn thẳng qua 2 ñiểm)
chu_vi (Tính chu vi tam giác qua 3 ñiểm)
Chương trình còn minh hoạ:
+ Việc phương thức này sử dụng phương thức khác (phương thức ve_tam_giac sử
dụng phương thức ve_doan_thang, phương thức chu_vi sử dụng phương thức do_dai)
+ Sử dụng con trỏ this trong thân các phương thức ve_tam_giac và chu_vi
Nội dung chương trình là nhập ba ñiểm, vẽ tam giác có ñỉnh là ba ñiểm vừa nhập
sau ñó tính chu vi tam giác.
#include <conio.h>
#include <iostream.h>

#include <graphics.h>
#include <math.h>
#include <stdio.h>
class DIEM
{
int x, y ;
public:
void nhapsl();
void ve_doan_thang(DIEM d2, int mau) ;
void ve_tam_giac(DIEM d2, DIEM d3,int mau) ;
double do_dai(DIEM d2)
{

63

DIEM d1 = *this ;
return sqrt( pow(d1.x - d2.x,2) + pow(d1.y - d2.y,2) ) ;
}
double chu_vi(DIEM d2, DIEM d3);
};
void DIEM::nhapsl()
{
cout <<" \nNhap hoanh do (cot) va tung do (hang) cua diem:" ;
cin >> x >> y ;
}
void kd_do_hoa() {
int mh, mode ;
mh=mode=0;
initgraph(&mh, &mode, "");
}

void DIEM::ve_doan_thang(DIEM d2, int mau)
{
setcolor(mau);
line(this->x,this->y,d2.x,d2.y);
}
void DIEM::ve_tam_giac(DIEM d2, DIEM d3,int mau)
{
(*this).ve_doan_thang(d2,mau);
d2.ve_doan_thang(d3,mau);
d3.ve_doan_thang(*this,mau);
}
double DIEM::chu_vi(DIEM d2, DIEM d3)
{
double s;
s= (*this).do_dai(d2) + d2.do_dai(d3) + d3.do_dai(*this) ;
return s;
}
void main()
{
DIEM d1, d2, d3;
d1.nhapsl();

64

d2.nhapsl();
d3.nhapsl();
kd_do_hoa();
d1.ve_tam_giac(d2,d3,15);
double s = d1.chu_vi(d2,d3);
printf("Chu vi = %0.2f", s);

getch();
closegraph();
}

4.4. Một số nhận xét về ñối của phương thức và lời gọi phương thức
Quan sát nguyên mẫu phương thức:
void ve_doan_thang(DIEM d2, int mau) ;
Nhận thấy phương thức có ba ñối:
ðối thứ nhât là một ñối tượng DIEM do this trỏ tới
ðối thứ hai là ñối tượng DIEM d2
ðối thứ ba là biến nguyên (mau)
Nội dung phương thức là vẽ một ñoạn thẳng ñi qua các ñiểm *this và d2 theo mã
mầu mau. Xem thân của phương thức sẽ thấy ñược nội dung này:
void DIEM::ve_doan_thang(DIEM d2, int mau)
{
setcolor(mau);
line(this->x, this->y, d2.x, d2.y);
}
Tuy nhiên trong trường hợp này, vai trò của this không cao lắm, vì nó ñược ñưa
vào chỉ cốt làm rõ ñối thứ nhất. Trong thân phương thức có thể bỏ từ khoá this vẫn
ñược.
Vai trò của this trở nên quan trọng trong phương thức ve_tam_giac:
void ve_tam_giac(DIEM d2, DIEM d3,int mau) ;
Phương thức này có bốn ñối là:
this trỏ tới một ñối tượng kiểu DIEM
d2 một ñối tượng kiểu DIEM
d3 một ñối tượng kiểu DIEM
mau một biến nguyên
Nội dung phương thức là vẽ 3 cạnh:
cạnh 1 ñi qua *this và d2

cạnh 2 ñi qua d2 và d3

65

cạnh 3 ñi qua d3 và *this
Các cạnh trên ñược vẽ nhờ sử dụng phương thức ve_doan_thang:
Vẽ cạnh 1 dùng lệnh: (*this).ve_doan_thang(d2,mau) ;
Vẽ cạnh 2 dùng lệnh: d2.ve_doan_thang(d3,mau);
Vẽ cạnh 3 dùng lệnh: d3.ve_doan_thang(*this,mau);
Trong trường này rõ ràng vai trò của this rất quan trọng. Nếu không dùng nó thì
công việc trở nên khó khăn, dài dòng và khó hiểu hơn. Chúng ta hãy so sánh hai
phương án:
+ Phương án dùng this trong phương thức ve_tam_giac:
void DIEM::ve_tam_giac(DIEM d2, DIEM d3,int mau)
{
(*this).ve_doan_thang(d2,mau);
d2.ve_doan_thang(d3,mau);
d3.ve_doan_thang(*this,mau);
}
+ Phương án không dùng this trong phương thức ve_tam_giac:
void DIEM::ve_tam_giac(DIEM d2, DIEM d3,int mau)
{
DIEM d1;
d1.x = x;
d1.y = y;
d1.ve_doan_thang(d2,mau);
d2.ve_doan_thang(d3,mau);
d3.ve_doan_thang(d1,mau);
}
4.5. Các ví dụ

Ví dụ 1 minh hoạ:
+ Thuộc tính (thành phần dữ liệu) của lớp có thể là ñối tượng của lớp khác ñã
ñịnh nghĩa bên trên.
+ Phương thức có giá trị trả về kiểu ñối tượng và con trỏ ñối tượng.
Nội dung chương trình là nhập một dẫy hình chữ nhật, sau ñó tìm hình chữ nhật có
diện tích lớn nhất và hình chữ nhật có chu vi lớn nhất.
Chương trình ñược tổ chức thành hai lớp:
+ Lớp HINH_CN gồm:
- Các thuộc tính: d và r (chiều dài và chiều rộng)
- Các phương thức
void nhapsl() ; // Nhập chiều dài, rộng

66

int dien_tich(); // Tính diện tích
int chu_vi() ; // Tính chu vi
+ Lớp DAY_HINH_CN gồm:
- Các thuộc tính:
int n ; //Số hình chữ nhật của dẫy
HINH_CN *h; //Con trỏ tới dẫy ñối tượng của lớp HINH_CN
- Các phương thức:
void nhapsl(); // Nhập một dẫy hình chữ nhật
HINH_CN hinh_dt_max() ; // Trả về hình chữ nhật có diện tích max
HINH_CN *hinh_cv_max() ; // Trả về con trỏ tới HCN có chu vi max
#include <conio.h>
#include <iostream.h>
class HINH_CN
{
int d, r; // chieu dai va chieu rong
public:

void nhapsl()
{
cout << " \nNhap chieu dai va chieu rong: " ;
cin >> d >> r ;
}
void in()
{
cout << "\nchieu dai = " << d ;
cout << " chieu rong= " << r;
}
int dien_tich()
{
return d*r;
}
int chu_vi()
{
return 2*(d+r);
}
} ;
class DAY_HINH_CN

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×