Professional Documents
Culture Documents
class <tên_lớp>
{
[ phạm vi truy xuất : ]
<khai báo các thành phần dữ liệu (thuộc tính)>
[ phạm vi truy xuất : ]
<khai báo các hàm thành phần (phương thức)>
};
Trong đó: Tên lớp: Tên lớp do người lập trình tự đặt theo quy cách đặt tên trong C+
+ (Tên trong C++ bắt đầu bằng dấu gạch thấp hoặc ký tự, không chứa dấu cách, không
chứa ký tự đặc biệt, không trùng với từ khóa trong C++).
2.2 Khai báo các thành phần của lớp
2.2.1 Phạm vi truy xuất
Khi xây dựng một class, chắc chắn bạn sẽ phải xác định phạm vi truy cập cho các
thuộc tính và phương thức trong class đó. Mục đích của việc này nhằm quy định các
thành phần nào có thể được truy cập, thay đổi từ bên ngoài, thành phần nào là riêng tư.
Có thể hiểu phạm vi truy xuất này cũng giống như biến toàn cục và biến cục bộ.
Biến toàn cục có thể được truy cập từ tất cả các hàm sau khai báo nó, còn biến cục bộ chỉ
có thể được truy cập nội bộ trong hàm.
Trong C++, phạm vi truy cập được xác định qua 3 từ khóa: public, private và
protected.
- public: Các thành phần mang thuộc tính này đều có thể được truy cập từ bất kỳ
hàm nào, dù ở trong hay ngoài lớp.
- private: Các thành phần mang thuộc tính này chỉ có thể được truy cập bên trong
phạm vi lớp. Vì trong C++ cho phép định nghĩa phương thức ngoài khai báo lớp
nên phạm vi lớp được hiểu là bên trong khai báo lớp và bên trong các định nghĩa
thuộc lớp.
- protected: Các thành phần mang thuộc tính này chỉ có thể được truy cập bên trong
phạm vi lớp và các lớp con kế thừa nó. Như vậy, nếu một lớp không có lớp con kế
thừa nó thì phạm vi protected cũng giống như private.
Một ngoại lệ chỉ có trong C++ đó là định nghĩa friend. Một hàm hoặc lớp friend
có thể truy cập vào các thành phần private và protected của lớp với hàm đó (hàm friend)
hoặc với các đối tượng khác (lớp friend) với điều kiện phải được khai báo trước trong
lớp.
Một số lưu ý về phạm vi truy xuất trong C++:
- Phạm vi truy xuất trong C++ được xác định trong qua các nhãn trong khai báo lớp.
Nhãn bao gồm từ khóa và dấu hai chấm.
- Mỗi nhãn có phạm vi ảnh hưởng từ lúc khai báo đến khi gặp nhãn khác hoặc hết
khai báo lớp.
- Nếu không chỉ rõ nhãn đầu tiên thì ngầm định nó có phạm vi truy cập là private.
2.2.2 Các thành phần dữ liệu (thuộc tính) của lớp
Các thành phần dữ liệu (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, double,…) 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
…
};
Khai báo thành phần dữ liệu: Tương tự như khai báo biến , cú pháp:
<kiểu dữ liệu > <tên_thành_phần>;
Lưu ý:
- Trong các khai báo của các thuộc tính, có thể sử dụng từ khóa static nhưng không
được sử dụng các từ khóa auto, register, extern trong khai báo các thuộc tính.
- Thành phần dữ liệu không được khởi tạo giá trị ban đầu.
Các thành phần dữ liệu của lớp thường (không bắt buộc) khai báo là private để
đảm bao 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.
2.2.3 Các thành phần hàm (phương thức của lớp)
Các phương thức của lớp: Có thể được được định ngay nghĩa bên trong lớp hoặc có
thể định nghĩa ngoài định nghĩa lớp. Khi định nghĩa bên ngoài lớp phải khai báo phương
thức đó trong lớp.
Các phương thức thường được khai báo là public để chúng có thể được gọi tới từ
các hàm khác trong chương trình. Các phương thức trong lớp có thể được định nghĩa theo
2 cách như sau:
- Cách 1: Định nghĩa phương thức ngay trong lớp. Khi định nghĩa phương thức ngay
trong lớp chúng ta định nghĩa giống như hàm thông thường.
- Cách 2: Khai báo phương thức trong lớp và định nghĩa phương thức ngoài lớp. Khai
báo phương thức trong lớp vẫn khai báo theo cú pháp hàm thông thường.
<kiểu_trả_về> <tên_phương_thức> ([đối_số]) ;
Riêng việc định nghĩa phương thức ngoài lớp phải tuân theo cú pháp sau:
<kiểu_trả_về> tên_lớp :: <tên_phương_thức> ([đối_số])
{
// <thân phương thức>
}
Và dù phương thức được định nghĩa trong lớp hay ngoài lớp thì đều có khả năng
truy xuất đến các thành phần khác trong lớp là như nhau.
Trong thân phương thức của 1 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ự do trong chương trình, vì phạm vi của các hàm tự do 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ỳ, gồm cả kiểu chuẩn và kiểu
ngoài chuẩn.
Khai báo lớp hình chữ nhật gồm có 2 thành phần dữ liệu là chiều dài d và chiều rộng r,
các phương thức trong lớp gồm nhập, in, tính chu vi, tính diện tích của hình chữ nhật.
class HCN
{
private:
float d,r;
public:
void nhap()
{
cout<<“nhap chieu dai: ”; cin>>d;
cout<<“nhap chieu rong: ”; cin>>r;
}
void inthongtin()
{
cout<<”(”<<d<<”,”<<r<<”)”;
}
float tinhchuvi()
{
return 2*(d+r);
}
float tinhdientich()
{
return d*r ;
}
};
Có thể khai báo các phương thức trong lớp và định nghĩa các phương thức ngoài lớp
như sau:
class HCN
{
private:
float d,r;
public:
void nhap() ;
void inthongtin(HCN h);
float tinhchuvi();
float tinhdientich();
};
void HCN :: nhap()
{
cout<<“nhap chieu dai: ”; cin>>d;
cout<<“nhap chieu rong: ”; cin>>r;
}
void HCN :: inthongtin()
{
cout<<”(”<<d<<”,”<<r<<”)”;
}
int main()
{
HCN H[5];
int i;
float Maxcv;
//Nhap chieu dai và chieu rong cua 5 hinh chu nhat
for(i=0;i<5;i++)
{
cout<<"Nhap thong tin hinh chu nhat thu "<<
i+1<<":"<<endl;
H[i].nhap();
}
// Tìm chu vi lon nhat
Maxcv = H[0].tinhchuvi();
for(i=1;i<5;i++)
{
if (Maxcv < H[i].tinhchuvi())
Maxcv = H[i].tinhchuvi();
}
cout<<"Gia tri lon nhat cua chu vi là: "<<Maxcv;
return 0;
}
2.3 5 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. Cú pháp khai
báo như sau:
Tên_lớp *con_trỏ ;
Chẳng hạn với lớp HCN có thể khai báo:
HCN *p1, *p2, *p3; // khai báo 3 con trỏ p1, p2, p3
HCN h1, h2; // khai báo 2 đối tượng h1, h2
HCN H[20]; // khai báo mảng đối tượng H
Khi đó có thể thực hiện các câu lệnh sau:
p1 = &h1;// p1 chứa địa chỉ của h1, hay p1 trỏ tới h1
p2 = H;
hoặc
p2 = &H[0]; /*p2 trỏ tới đầu mảng H hoặc p2 trỏ tới
phần tử đầu tiên của mảng H, 2 lệnh này tương
đương nhau*/
p3 = new HCN; /*Tạo 1 đố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
Tên_mảng_đối_tượng[chỉ_số] -> Tên_thuộc_tính
Lưu ý: 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 -> d và h1.d là như nhau
p2[i] -> r và H[i] . rlà như nhau
Chương trình dưới đây minh họa cho việc sử dụng con trỏ trong chương trình.
Ví dụ 2.3:
#include<iostream.h>
#include<conio.h>
class HCN
{
private:
float d,r;
public:
void nhap() ;
void inthongtin();
};
void HCN :: nhap()
{
cout<<"nhap chieu dai: "; cin>>d;
cout<<"nhap chieu rong: "; cin>>r;
}
void HCN :: inthongtin()
{
cout<<"("<<d<<","<<r<<")";
}
int main()
{
HCN *p;
int i,n;
p = new HCN[n+1];
cout<<"nhap n: ";
cin>>n;
cout<<"Nhap thong tin cho cac hinh chu
nhat:"<<endl;
for(i=0;i<n;i++)
{
cout<<"nhap kich thuoc hcn thu "<<i+1<<endl;
p[i].nhap();
cout<<endl;
}
cout<<"In thong tin hinh chu nhat: ";
for(i=0;i<n;i++)
{
cout<<"thong tin hcn thu "<<i+1;
p[i].inthongtin();
cout<<endl;
}
return 0;
}
2.3.6 Phép gán các đối tượng
Có thể thực hiện phép gán giữa 2 đối tượng cùng kiểu. Ví dụ a, b là 2 đối tượng
cùng kiểu lớp phân số PS, lớp PS có 2 thành phần dữ liệu là tử số ts và mẫu số ms. Khi
đó phép gán đối tượng b cho đối tượng a thực chất là gán các thành phần dữ liệu của đối
tượng b cho đối tượng a.
PS a, b;
a(1,3);
b = a;
Ví dụ 2.8 [2]
#include<iostream.h>
#include<conio.h>
class HCN
{
private:
float d,r;
public:
HCN ()
{
d = r = 0;
}
HCN (float dx, float rx);
void inthongtin();
};
HCN ::HCN(float dx, float rx)
{
d = dx;
r = rx;
}
void HCN :: inthongtin()
{
cout<<"("<<d<<","<<r<<")";
}
int main()
{
HCN h1(30,20);
HCN H[50];
cout<<"In thong tin hinh chu nhat: ";
h1.inthongtin();
cout<<"In thong tin 10 hinh chu nhat: ";
for (int i=0; i<10;i++)
{
H[i].inthongtin();
cout<<endl;
}
return 0;
}
Còn một giải pháp khác không cần định nghĩa thêm hàm khởi tạo không tham số.
Khi đó cần khai báo giá trị ngầm định cho các tham số của hàm thiết lập hai tham số như
sau:
Ví dụ 2.9 [2]
#include<iostream.h>
#include<conio.h>
class HCN
{
private:
float d,r;
public:
HCN (float dx=0, float rx=0);
void inthongtin();
};
HCN ::HCN(float dx=0, float rx=0)
{
d = dx;
r = rx;
}
void HCN :: inthongtin()
{
cout<<"("<<d<<","<<r<<")";
}
int main()
{
HCN h1(30,20);
HCN H[50];
//Đúng vì các đối tượng được tạo nhờ hàm thiết lập 2 tham
// số có giá trị ngầm định là 0 và 0
cout<<"In thong tin hinh chu nhat: ";
h1.inthongtin();
cout<<"In thong tin 10 hinh chu nhat: ";
for (int i=0; i<10;i++)
{
H[i].inthongtin();
cout<<endl;
}
return 0;
}
2.5.3 Hàm khởi tạo sao chép (copy constructor)
Khi lớp không có các thuộc tính kiểu con trỏ hoặc tham chiếu, thì dùng hàm khởi
tạo mặc định là đủ. Khi lớp có các thuộc tính kiểu con trỏ hoặc tham chiếu thì phải sử
dụng hàm khởi tạo sao chép.
Hàm khởi tạo sao chép sử dụng một đối kiểu tham chiếu đối tượng để khởi gán
cho đối tượng mới. Hàm khởi tạo sao chép được viết theo mẫu:
<tên_lớp> (const <tên_lớp> &<dt>)
{
/* các câu lệnh dùng các thuộc tính của đối
tượng đối tượng để khởi gán cho các thuộc tính của
đối tượng mới*/
}
Chương trình sau minh họa việc xây dựng và sử dụng các hàm khởi tạo. Chương trình
thực hiện trên lớp đa thức DT [2].
Ví dụ 2.10:
#include<iostream.h>
#include<conio.h>
#include<math.h>
class DT
{
private:
int n; //bac da thuc
double *a; //tro toi vung nho chua cac he so
da thuc a0, a1,...
public:
DT()
{
this->n=0;
this->a=NULL;
}
DT(int n1)
{
this->n=n1;
this->a=new double[n1+1];
}
DT (const DT &d)
{
this->n=d.n;
this->a=new double[d.n + 1];
for(int i=0;i<=d.n;i++)
this->a[i]=d.a[i];
}
2.8.2.3 Tất cả các hàm thành phần của một lớp là bạn của một lớp khác
Để tất cả các hàm thành phần của lớp B là bạn của lớp A, trong khai báo của lớp A
phải có chỉ thị:
class A
{ .. .
friend class B;
.. .
}
Khi đó còn gọi lớp B là bạn của lớp A (lớp bạn)
Lưu ý: Để biên dịch khai báo của lớp A, phải đặt khai báo
class B; trước khai báo lớp A.
Trong chương trình sau, lớp ma trận mt và lớp vec tơ vt là bạn bè của nhau. Khi đó tất cả
các phương thức của lớp này đều là bạn của lớp kia và ngược lại [1].
Ví dụ 2.14
#include <conio.h>
#include <iostream.h>
class mt;
class vt;
class mt
{
private:
double a[10][10];
int n;
public:
friend class vt;
mt()
{
n=0;
}
void nhap();
void in();
vt tich(const vt &y);
mt tich(const mt &b) ;
};
class vt
{
private:
double x[10];
int n;
public:
friend class mt;
vt()
{
n=0;
}
void nhap();
void in();
vt tich(const mt &b);
double tich(const vt &y) ;
};
void mt::nhap()
{
cout << "\n cap ma tran: " ;
cin >> n;
for (int i=1; i<=n; ++i)
for (int j=1; j<=n; ++j)
{
cout << "\nphan tu hang " << i << " cot " << j << " =
" ;
cin >> a[i][j];
}
}
void mt::in()
{
for (int i=1; i<=n; ++i)
{
cout << "\n" ;
for (int j=1; j<=n; ++j)
cout << a[i][j] << " " ;
}
}
void vt::nhap()
{
cout << "\n cap vec to: " ;
cin >> n;
for (int i=1; i<=n; ++i)
{
cout << "\nphan tu thu " << i << " = " ;
cin >> x[i];
}
}
void vt::in()
{
for (int i=1; i<=n; ++i)
cout << x[i] << " " ;
}
vt mt::tich(const vt &y)
{
vt z;
int i,j;
for (i=1; i<=n; ++i)
{
z.x[i] = 0.0 ;
for (j=1; j<=n; ++j)
z.x[i] += a[i][j]*y.x[j];
}
z.n = n;
return z;
}
mt mt::tich(const mt &b)
{
mt c;
int i,j,k;
for (i=1; i<=n; ++i)
for (j=1; j<=n; ++j)
{
c.a[i][j] = 0.0 ;
for (k=1; k<=n; ++k)
c.a[i][j] += a[i][k]*b.a[k][j];
}
c.n = n;
return c;
}
vt vt::tich(const mt &b)
{
vt z;
int i,j;
for (j=1; j<=n; ++j)
{
z.x[j] = 0.0 ;
for (i=1; i<=n; ++i)
z.x[j] += b.a[i][j]*x[i];
}
z.n = n;
return z;
}
double vt::tich(const vt &y)
{
double tg=0.0;
for (int i=1; i<=n; ++i)
tg += x[i]*y.x[i];
return tg;
}
int main()
{
mt a,b,c;
vt x,y;
cout << "\nma tran a";
a.nhap();
cout << "\nma tran b";
b.nhap();
cout << "\nma tran c";
c.nhap();
cout << "\nvec to x";
x.nhap();
cout << "\nvec to y";
y.nhap();
mt d= a.tich(b);
vt u = d.tich(y);
vt v = x.tich(c);
double s = v.tich(u);
cout << "\n\nvec to v\n";
v.in();
cout << "\n\nma tran d";
d.in();
cout << "\n\nvec to y\n";
y.in();
cout << "\n\ns= vdy = " << s;
return 0;
}
2.8.3 Hàm bạn của nhiều lớp
Một hàm tự do có thể là bạn của nhiều lớp nếu trong chính các lớp đó phải đưa ra
khai báo hàm bạn của mình:
friend <kiểu_trả_về> Tên_hàm (Danh_sách_đối_số);
Việc định nghĩa hàm bạn bên ngoài lớp giống như một hàm thông thường
Lưu ý:
- Trong hàm bạn không còn tham số ngầm định this như trong hàm thành phần.
- Việc định nghĩa hàm bạn bên ngoài lớp giống như một hàm thông thường
- Khi khai báo các hàm phải đảm bảo các lớp được sử dụng trong hàm đã được khai
báo trước đó (không cần định nghĩa trước).
Chương trình dưới đây thực hiện phép nhân 1 ma trận vuông cấp 3x3 với 1 véc tơ
3 chiều. Trong chương trình xây dựng hàm tính tích là một hàm tự do và là bạn của 2 lớp
ma trận và lớp vec tơ [1].
Ví dụ 2.15
#include <iostream.h>
int n;
class vector; //khai báo trước vì lớp matran có sử dụng lớp này
class matran
{
private:
int mt[3][3];
public:
matran(double t[3][3])
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
mt[i][j] = t[i][j];
}
}
}
friend vector nhan(matran a,vector b);
};
class vector
{
private:
int vt[3];
public:
vector(double v1=0,double v2=0,double v3=0)
{
vt[0] = v1;
vt[1] = v2;
vt[2] = v3;
}
void invt()
{
for (int i = 0; i < 3; i++)
cout << vt[i]<< " ";
cout << endl;
}
friend vector nhan(matran a,vector b);
};
vector nhan(matran a,vector b)
{
int i,j;
double sum;
vector kq;
for (int i = 0; i < 3; i++)
{
sum = 0;
for (int j = 0; j < 3; j++)
sum = sum + a.mt[i][j] * b.vt[j];
kq.vt[i]=sum;
}
return kq;
}
int main()
{
vector V(1,2,3);
vector KQ;
double dlm[3][3]={1,2,3,4,5,6,7,8,9};
matran M=dlm;
KQ = nhan(M,V);
cout<<"Ket qua phep nhan la: ";
KQ.invt();
return 0;
}
TÓM TẮT NỘI DUNG
Chương 2 gồm các nội dung hướng dẫn chi tiết cách xây dựng lớp trong ngôn ngữ
lập trình C++. Một lớp gồm các thành phần dữ liệu (gọi là thuộc tính) và các hàm thành
phần (gọi là phương thức). Trong chương 2 hướng dẫn chi tiết cách khai báo và sử dụng
các thuộc tính và các phương thức cơ bản trong lớp, trong chương trình. Mỗi nội dung
đều có các ví dụ minh họa cụ thể.