Lớp Diem biểu diễn khái niệm điểm trong mặt phẳng
với hai thành phần toạ độ x và y. Khai báo lớp Diem
được đặt trong tập tin diem.h và chi tiết cài đặt các hàm
thành phần được đặt trong tập tin diem.cpp. Có thể xây
dựng một số ứng dụng của lớp điểm như trong tập tin
tdiem.cpp hoặc dongho.cpp với các ứng dụng tương ứng
là tdiem.exe và dongho.exe (hoặc dongho2.exe,
dongho3.exe).
Chương 2. L?p 120
Lớp DaGiac biểu diễn khái niệm đa giác trong mặt
phẳng có các thao tác tịnh tiến, vị tự, quay, vẽ Phần
khai báo được đặt trong tập tin dagiac.h, phần cài đặt
trong tập tin dagiac.cpp và ứng dụng tdg.cpp cho phép
tạo, vẽ, tịnh tiến và quay đa giác. Tập tin thực thi là
tdg.exe
120 trang |
Chia sẻ: nguyenlam99 | Lượt xem: 1369 | Lượt tải: 0
Bạn đang xem trước 20 trang tài liệu Lập trình hướng đối tượng - Chương 2: Đối tượng và lớp, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
Lập Trình Hướng Đối Tượng
45 LT+30TH
Chương 2 – Đối tượng và Lớp
1. Đối tượng và lớp
2. Cài đặt lớp trong C++
3. Thiết lập và huỷ bỏ đối tượng
4. Giao diện và chi tiết cài đặt
5. Các nguyên tắc xây dựng lớp
6. Một số ví dụ về lớp
2.1. Đối tượng và lớp
Ta định nghĩa một đối tượng là một "cái gì đó" có ý
nghĩa cho vấn đề ta quan tâm. Đối tượng phục vụ hai
mục đích: Giúp hiểu rõ thế giới thực và cung cấp cơ sở
cho việc cài đặt trong máy.
Mỗi đối tượng có một nét nhận dạng để phân biệt nó
với các đối tượng khác. Nét nhận dạng mang ý nghĩa
các đối tượng được phân biệt với nhau do sự tồn tại vốn
Chương 2. Lớp 3
có của chúng chứ không phải các tính chất mà chúng
có.
Đối tượng và lớp
Các đối tượng có các đặc tính tương tự nhau được gom
chung lại thành lớp đối tượng. Ví dụ Người là một lớp
đối tượng. Một lớp đối tượng được đặc trưng bằng các
thuộc tính, và các hoạt động (hành vi).
Một thuộc tính (attribute) là một giá trị dữ liệu cho mỗi
đối tượng trong lớp. Tên, Tuổi, Cân nặng là các thuộc
tính của Người.
Chương 2. Lớp 4
Một thao tác (operation) là một hàm hay một phép biến
đổi có thể áp dụng vào hay áp dụng bởi các đối tượng
trong lớp.
Đối tượng và lớp
Cùng một thao tác có thể được áp dụng cho nhiều lớp
đối tượng khác nhau, một thao tác như vậy được gọi là
có tính đa dạng (polymorphism).
Mỗi thao tác trên mỗi lớp đối tượng cụ thể tương ứng với
một cài đặt cụ thể khác nhau. Một cài đặt như vậy được
gọi là một phương thức (method).
Một đối tượng cụ thể thuộc một lớp được gọi là một thể
hiện (instance) của lớp đó. Joe Smith, 25 tuổi, nặng
Chương 2. Lớp 5
58kg, là một thể hiện của lớp người.
Sơ đồ đối tượng
Ta dùng sơ đồ đối tượng để mô tả các lớp đối tượng. Sơ
đồ đối tượng bao gồm sơ đồ lớp và sơ đồ thể hiện.
Sơ đồ lớp mô tả các lớp đối tượng trong hệ thống, một
lớp đối tượng được diễn tả bằng một hình chữ nhật có 3
phần: phần đầu chỉ tên lớp, phần thứ hai mô tả các
thuộc tính và phần thứ ba mô tả các thao tác của các
đối tượng trong lớp đó.
Chương 2. Lớp 6
Sơ đồ lớp và sơ đồ thể hiện
Sinh viên
Họ tên
Năm sinh
Mã số
Điểm TB
(Sinh viên)
Nguyễn Văn A
1984
0610234T
9.2
Tên lớp
Thuộc tính
Chương 2. Lớp 7
Đi học
Đi thi
Phân loại
Thao tác
Sơ đồ lớp Sơ đồ thể hiện
2.2. Các thành phần của lớp
Một kiểu dữ liệu là một biểu diễn cụ thể một khái niệm
trong thực tế. Ví dụ kiểu int là một biểu diễn cụ thể của
khái niệm số nguyên trong toán học.
Trong C++, các kiểu dữ liệu có sẵn (built-in data types)
:int, long, float, double, char... cho phép kiểm tra lúc
biên dịch và phát sinh mã chương trình tối ưu. Các kiểu
dữ liệu này cung cấp một giao diện tự nhiên độc lập với
Chương 2. Lớp 8
phần cài đặt.
2.2. Các thành phần của lớp
Lớp trong C++ là cài đặt của kiểu dữ liệu trừu tượng do
người sử dụng định nghĩa, cho phép kết hợp dữ liệu, các
phép toán, các hàm liên quan để tạo ra một đơn vị
chương trình duy nhất. Các lớp này có đầy đủ ưu điểm và
tiện lợi như các kiểu dữ liệu nội tại. Lớp tách rời phần
giao diện (chỉ liên quan với người sử dụng) và phần cài
đặt lớp.
Chương 2. Lớp 9
Lớp trong C++ được cài đặt sử dụng từ khoá struct và
class.
Ví dụ so sánh: Xây dựng kiểu dữ liệu stack.
1. Cách tiếp cận cổ điển:
// Stack1.cpp :
//Dung cau truc va ham toan cuc
#include
typedef int bool;
typedef int Item;
const bool false = 0, true = 1;
Chương 2. Lớp 10
struct Stack
{
Item *st, *top;
int size;
};
Ví dụ so sánh (tt)
void StackInit(Stack *ps, int sz)
{
ps->st = ps->top = new Item[ps->size=sz];
}
void StackCleanUp(Stack *ps)
{
delete [] ps->st;
Chương 2. Lớp 11
}
bool StackFull(Stack *ps)
{
return (ps->top - ps->st >= ps->size);
}
Ví dụ so sánh (tt)
bool StackEmpty(Stack *ps)
{
return (ps->top st);
}
bool StackPush(Stack *ps, Item x)
{
Chương 2. Lớp 12
if (StackFull(ps)) return false;
*ps->top++ = x;
return true;
}
Ví dụ so sánh (tt)
bool StackPop(Stack *ps, Item *px)
{
if (StackEmpty(ps)) return false;
*px = *--ps->top;
return true;
}
Chương 2. Lớp 13
Ví dụ so sánh (tt)
void XuatHe16(long n)
{
static char hTab[] = “0123456789ABCDEF”;
Stack s;
StackInit(&s,8);
int x;
do {
StackPush(&s, n%16);
Chương 2. Lớp 14
n /= 16;
} while(n);
while(StackPop(&s,&x))
cout << hTab[x];
StackCleanUp(&s);
}
Ví dụ so sánh (tt)
Nhận xét:
Giải quyết được vấn đề.
Khai báo cấu trúc dữ liệu nằm riêng, các hàm xử lý dữ
liệu nằm riêng ở một nơi khác. Do đó khó theo dõi
quản lý khi hệ thống lớn. Vì vậy khó bảo trì.
Mọi thao tác đều có tham số đầu tiên là con trỏ đến đối
tượng cần thao tác. Tư tưởng thể hiện ở đây là hàm hay
Chương 2. Lớp 15
thủ tục đóng vai trò trọng tâm. Đối tượng được gởi đến
cho hàm xử lý.
Trình tự sử dụng qua các bước: Khởi động, sử dụng thực
sự, dọn dẹp.
Ví dụ so sánh (tt)
2. Cách tiếp cận dùng hàm thành phần:
//...
struct Stack
{
Item *st, *top;
int size;
void Init(int sz) {st = top = new
Item[size=sz];}
Chương 2. Lớp 16
void CleanUp() {if (st) delete [] st;}
bool Full() const {return (top - st >=
size);}
bool Empty() const {return (top <= st);}
bool Push(Item x);
bool Pop(Item *px);
};
Ví dụ so sánh (tt)
bool Stack::Push(Item x)
{
if (Full()) return false;
*top++ = x;
return true;
}
Chương 2. Lớp 17
bool Stack::Pop(Item *px)
{
if (Empty()) return false;
*px = *--top;
return true;
}
Ví dụ so sánh (tt)
void XuatHe16(long n)
{
static char hTab[] = “0123456789ABCDEF”;
Stack s;
s.Init(8);
int x;
do {
Chương 2. Lớp 18
s.Push(n%16);
n /= 16;
} while(n);
while(s.Pop(&x))
cout << hTab[x];
s.CleanUp();
}
Ví dụ so sánh (tt)
Nhận xét:
Giải quyết được vấn đề.
Dữ liệu và các hàm xử lý dữ liệu được gom vào một chỗ
bên trong cấu trúc. Do đó dễ theo dõi quản lý, dễ bảo trì
nâng cấp.
Các thao tác đều bớt đi một tham số so với cách tiếp cận
cổ điển. Vì vậy việc lập trình gọn hơn. Tư tưởng thể hiện
Chương 2. Lớp 19
ở đây là đối tượng đóng vai trò trọng tâm. Đối tượng
thực hiện thao tác trên chính nó.
Trình tự sử dụng qua các bước: Khởi động, sử dụng thực
sự, dọn dẹp.
2.2.1 Các hàm thành phần.
Là hàm được khai báo trong lớp. Hàm thành phần có thể
được định nghĩa bên trong hoặc bên ngoài lớp.
Hàm thành phần có nghi thức giao tiếp giống với các
hàm bình thường khác: có tên, danh sách tham số, giá trị
trả về.
Gọi hàm thành phần bằng phép toán dấu chấm (.) hoặc
dấu mũi tên (->).
Chương 2. Lớp 20
Stack s, *ps = &s;
s.Init(10);
for (int i = 0; i < 20; i++)
ps->Push(i);
2.2.3 Lớp
Trong cách tiếp cận dùng struct và hàm thành phần,
người sử dụng có toàn quyền truy xuất, thay đổi các
thành phần dữ liệu của đối tượng thuộc cấu trúc. Ví dụ:
Stack s;
s.Init(10);
s.size = 100; // Nguy hiem
for (int i = 0; i < 20; i++)
Chương 2. Lớp 21
s.Push(i);
Vì vậy, ta không có sự an toàn dữ liệu. Lớp là một
phương tiện để khắc phục nhược điểm trên.
Lớp có được bằng cách thay từ khoá struct bằng từ khoá
class.
Lớp
Trong lớp mọi thành phần mặc nhiên đều là riêng tư
(private) nghĩa là thế giới bên ngoài không được phép
truy xuất. Do đó có sự an toàn dữ liệu.
class Stack
{
Item *st, *top;
int size;
Chương 2. Lớp 22
void Init(int sz) {st = top = new
Item[size=sz];}
void CleanUp() {if (st) delete [] st;}
bool Full() const {return (top - st >= size);}
bool Empty() const {return (top <= st);}
bool Push(Item x);
bool Pop(Item *px);
};
Lớp
Phát biểu:
s.size = 100; // Bao sai
Sẽ bị báo sai lúc biên dịch, nhờ đó tránh được những lỗi
lúc chạy chương trình (run-time error) rất khó tìm khi thực
hiện chương trình.
Chương 2. Lớp 23
2.2.4 Thuộc tính truy xuất
Tuy nhiên lớp như trên trở thành vô dụng vì các hàm
thành phần cũng trở thành private và không ai dùng
được. Điều đó được khắc phục nhờ thuộc tính truy xuất.
Thuộc tính truy xuất của một thành phần của lớp chỉ rõ
phần chương trình được phép truy xuất đến nó.
• Thuộc tính private
• Thuộc tính public
Chương 2. Lớp 24
Thuộc tính truy xuất
Các thành phần là nội bộ của lớp, bao gồm dữ liệu và
các hàm phục vụ nội bộ được đặt trong phần private.
Các hàm nhằm mục đích cho người sử dụng dùng được
đặt trong phần public.
Ví dụ sau minh hoạ thuộc tính truy xuất.
Chương 2. Lớp 25
// Stack.h
class Stack
{
Item *st, *top;
int size;
void Init(int sz) {st = top = new Item[size=sz];}
void CleanUp() {delete [] st;}
public:
Stack(int sz = 20) {Init(sz);}
Thuộc tính truy xuất
Chương 2. Lớp 26
~Stack() {delete [] st;}
bool Full() const {return (top - st >= size);}
bool Empty() const {return (top <= st);}
bool Push(Item x);
bool Pop(Item *px);
};
Ví dụ về lớp và thuộc tính truy xuất
// Stack.cpp
#include "stack.h"
bool Stack::Push(Item x)
{
if (Full()) return false;
*top++ = x;
return true;
}
Chương 2. Lớp 27
bool Stack::Pop(Item *px)
{
if (Empty()) return false;
*px = *--top;
return true;
}
Ví dụ về lớp và thuộc tính truy xuất
// he16.cpp
#include "stack.h"
void XuatHe16(long n)
{
static char hTab[] = “0123456789ABCDEF”;
Stack s(8); int x;
do {
Chương 2. Lớp 28
s.Push(n%16);
n /= 16;
} while(n);
while(s.Pop(&x))
cout << hTab[x];
}
Sử dụng phạm vi truy xuất
Phạm vi truy xuất được sử dụng đúng sẽ cho phép ta kết
luận: Nhìn vào lớp thấy được mọi thao tác trên lớp.
Người dùng bình thường có thể khai thác hết các chức
năng (public) của lớp.
Người dùng cao cấp có thể thay đổi chi tiết cài đặt, cải
tiến giải thuật các hàm thành phần.
Chương 2. Lớp 29
Tự tham chiếu
Là tham số ngầm định của hàm thành phần trỏ đến đối
tượng. Nhờ đó hàm thành phần biết được nó đang thao
tác trên đối tượng nào.
Khi một đối tượng gọi một thao tác, địa chỉ của đối tượng
được gởi đi một cách ngầm định với tên this, tên các
thành phần của đối tượng được hiểu là của đối tượng có
địa chỉ this này.
Chương 2. Lớp 30
bool Stack::Push(Item x)
{
if (Full()) // if (this->Full())
return false;
*top++ = x; // *this->top++ = x;
return true;
}
Phương thức thiết lập và hủy bỏ.
Phương thức thiết lập và huỷ bỏ được xây dựng nhằm
mục đích khắc phục lỗi quên khởi động đối tượng hoặc
khởi động dư. Việc quên khởi động đối tượng thường
gây ra những lỗi rất khó tìm.
Phương thức thiết lập là hàm thành phần đặc biệt được
tự động gọi đến mỗi khi một đối tượng thuộc lớp được
tạo ra. Người ta thường lợi dụng đặc tính trên để khởi
Chương 2. Lớp 31
động đối tượng.
Phương thức thiết lập có tên trùng với tên lớp để phân
biệt nó với các hàm thành phần khác.
Phương thức thiết lập và hủy bỏ.
Có thể có nhiều phiên bản khác nhau của phương thức
thiết lập
Phương thức huỷ bỏ là hàm thành phần đặc biệt được tự
động gọi đến mỗi khi một đối tượng bị huỷ đi. Người ta
thường lợi dụng đặc tính trên để dọn dẹp đối tượng.
Phương thức huỷ bỏ bắt đầu bằng dấu ngã (~) theo sau
bởi tên lớp để phân biệt nó với các hàm thành phần
Chương 2. Lớp 32
khác.
Chỉ có thể có tối đa một phương thức huy bo.
Phương thức thiết lập và hủy bỏ.
typedef int Item;
class Stack
{
Item *st, *top;
int size;
void Init(int sz);
void CleanUp() {delete [] st;}
public:
Stack(int sz = 20) {Init(sz);}
Chương 2. Lớp 33
~Stack() {delete [] st;}
bool Full() const {return (top - st >= size);}
bool Empty() const {return (top <= st);}
bool Push(Item x);
bool Pop(Item *px);
};
2.3 Các đặc tính khác của lớp
Hàm bạn (Friends).
Hàm thành phần hằng (const member functions).
Thành phần tĩnh (static members)
Con trỏ tới hàm thành phần.
Chương 2. Lớp 34
2.3.1 Hàm bạn (friends)
Nguyên tắc chung khi thao tác trên lớp là thông qua các
hàm thành phần. Tuy nhiên có những trường hợp ngoại
lệ, khi hàm phải thao tác trên hai lớp.
Hàm bạn của một lớp là hàm được khai báo ở bên
ngoài nhưng được phép truy xuất các thành phần riêng
tư của lớp.
Ta làm một hàm trở thành hàm bạn của lớp bằng cách
Chương 2. Lớp 35
đưa khai báo của hàm đó vào trong lớp, thêm từ khoá
friend ở đầu.
Ta dùng hàm bạn trong trường hợp hàm phải là hàm
toàn cục nhưng có liên quan mật thiết với lớp, hoặc là
hàm thành phần của một lớp khác.
Ví dụ: Nhân ma trận, không dùng hàm bạn
const int N = 4;
class Vector
{
double a[N];
public:
double Get(int i) const {return a[i];}
void Set(int i, double x) {a[i] = x;}
};
Chương 2. Lớp 36
Ví dụ: Nhân ma trận, không dùng hàm bạn
class Matrix
{
double a[N][N];
public:
double Get(int i, int j) const {return a[i][j];}
void Set(int i, int j, double x) {a[i][j] = x;}
//...
};
Chương 2. Lớp 37
Ví dụ: Nhân ma trận, không dùng hàm bạn
Vector Multiply(const Matrix &m, const
Vector &v)
{
Vector r;
for (int i = 0; i < N; i++)
{
r.Set(i, 0);
Chương 2. Lớp 38
for (int j = 0; j < N; j++)
r.Set(i, r.Get(i)+
m.Get(i,j)*v.Get(j));
}
return r;
}
const int N = 4;
class Matrix;
class Vector
{
double a[N];
public:
double Get(int i) const {return a[i];}
void Set(int i, double x) {a[i] = x;}
Ví dụ: Nhân ma trận, dùng hàm bạn
Chương 2. Lớp 39
friend Vector Multiply(const Matrix &m, const Vector
&v);
};
class Matrix
{
double a[N][N];
public:
double Get(int i, int j) const {return a[i][j];}
void Set(int i, int j, double x) {a[i][j] = x;}
friend Vector Multiply(const Matrix &m, const Vector
&v);
Ví dụ: Nhân ma trận, dùng hàm bạn
Chương 2. Lớp 40
};
Ví dụ minh hoạ: Sử dụng hàm bạn
Vector Multiply(const Matrix &m, const
Vector &v)
{
Vector r;
for (int i = 0; i < N; i++)
{
r.a[i] = 0;
Chương 2. Lớp 41
for (int j = 0; j < N; j++)
r.a[i] += m.a[i][j]*v.a[j];
}
return r;
}
2.3.2 Hàm thành phần hằng
Hàm thành phần hằng là hàm thành phần có thể áp
dụng được cho các đối tượng hằng.
Ta qui định một hàm thành phần là hằng bằng cách
thêm từ khoá const vào cuối khai báo của nó.
Ta khai báo hàm thành phần là hằng khi nó không thay
đổi các thành phần dữ liệu của đối tượng.
Chương 2. Lớp 42
Hàm thành phần hằng
inline char *strdup(const char *s)
{
return strcpy(new char[strlen(s) + 1], s);
}
class string
{
char *p;
Chương 2. Lớp 43
public:
string(char *s = "") {p = strdup(s);}
~string() {delete [] p;}
string(const string &s2) {p = strdup(s2.p);}
void Output() const {cout << p;}
void ToLower() {strlwr(p);}
};
Hàm thành phần hằng
void main()
{
const string Truong("DH BC TDT");
string s("ABCdef");
s.Output();
s.ToLower();
s.Output();
Chương 2. Lớp 44
Truong.Output();
Truong.ToLower(); // Bao loi
}
2.3.3 Thành phần tĩnh (static members)
Thành phần dữ liệu tĩnh là thành phần dữ liệu dùng
chung cho mọi đối tượng thuộc lớp.
Hàm thành phần tĩnh là hàm thành phần có thể hoạt
động không cần dữ liệu của đối tượng, nói cách khác,
nó không cần đối tượng.
Ta dùng hàm thành phần tĩnh thay vì hàm toàn cục vì
nó có liên quan mật thiết với lớp.
Chương 2. Lớp 45
Ta còn dùng hàm thành phần tĩnh để tạo đối tượng có
giá trị trả về cho biết việc tạo đối tượng có thành công
theo nghĩa luận lý hay không.
Ví dụ về thành phần tĩnh: CDate
typedef int bool;
const bool false = 0, true = 1;
class CDate
{
static int dayTab[][13];
int day, month, year;
public:
Chương 2. Lớp 46
static bool LeapYear(int y) {return y%400
== 0 || y%4==0 && y%100 != 0;}
static int DayOfMonth(int m, int y);
static bool ValidDate(int d, int m, int y);
void Input();
};
Ví dụ về thành phần tĩnh : CDate
int CDate::dayTab[][13] =
{
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
};
int CDate::DayOfMonth(int m, int y)
{
return dayTab[LeapYear(y)][m];
Chương 2. Lớp 47
}
bool betw(int x, int a, int b)
{
return x >= a && x <= b;
}
Ví dụ về thành phần tĩnh : CDate
bool CDate::ValidDate(int d, int m, int y)
{
return betw(m,1,12) &&
betw(d,1,DayOfMonth(m,y));
}
void CDate::Input()
{
int d,m,y;
Chương 2. Lớp 48
cin >> d >> m >> y;
while (!ValidDate(d,m,y))
{
cout << "Please enter a valid date: ";
cin >> d >> m >> y;
}
day = d; month = m; year = y;
}
Ví dụ về thành phần tĩnh: Stack
class Stack
{
Item *st, *top;
int size;
void Init(int sz);
void CleanUp() {if (st) delete [] st;}
public:
Stack(int sz = 20) {Init(sz);}
Chương 2. Lớp 49
~Stack() {CleanUp();}
static Stack *Create(int sz);
bool Full() const {return (top-st >= size);}
bool Empty() const {return (top <= st);}
bool Push(Item x);
bool Pop(Item *px);
};
Ví dụ về thành phần tĩnh: Stack
// Stack.cpp
#include
#include "stack.h"
bool Stack::Push(Item x)
{
if (Full()) return false;
*top++ = x;
return true;
Chương 2. Lớp 50
}
bool Stack::Pop(Item *px)
{
if (Empty()) return false;
*px = *--top;
return true;
}
Stack *Stack::Create(int sz)
{
Stack *ps = new Stack(sz);
if (!ps->st)
{
delete ps;
return NULL;
}
Ví dụ về thành phần tĩnh: Stack
Chương 2. Lớp 51
return ps;
}
void main()
{
Stack *ps = new Stack(50000); // khong biet tao
duoc stack khong
Stack *pr = Stack::Create(50000);
if (!pr)
{ cout << "Khong the tao stack"; return; }
else
Ví dụ về thành phần tĩnh: Stack
Chương 2. Lớp 52
pr->Push(5); // ...
}
2.4 Thiết lập và huỷ bỏ đối tượng
Ta cần kiểm soát khi nào phương thức thiết lập được
gọi, khi nào phương thức huỷ bỏ được gọi.
• Khi nào đối tượng thiết lập được gọi? Khi đối tượng
được tạo ra.
• Khi nào phương thức huỷ bỏ được gọi? Khi đối tượng bị
huỷ đi.
Thời gian từ khi đối tượng được tạo ra đến khi nó bị huỷ
Chương 2. Lớp 53
•
đi được gọi là thời gian sống.
Vậy vấn đề xác định khi nào phương thức thiết lập và
huỷ bỏ được gọi trở thành:
Khi nào đối tượng được tạo ra ?
Khi nào đối tượng bị huỷ đi ?
Thiết lập và huỷ bỏ đối tượng
Thời gian sống của đối tượng khác nhau tuỳ thuộc đối
tượng đó thuộc lớp lưu trữ (storage class) nào. Trong C++
có các lớp lưu trữ sau:
auto
global
static
free stored
Chương 2. Lớp 54
Ta sẽ lần lượt xét các loại sau:
Đối tượng tự động
Đối tượng toàn cục
Đối tượng tĩnh
Đối tượng được cấp phát động
Đối tượng thành phần
Đối tượng tự động
Đối tượng tự động (automatic objects) là đối tượng được
tự động sinh ra và tự động bị huỷ đi.
Đối tượng địa phương
• Là các biến được khai báo, định nghĩa bên trong một khối.
• Nó được tự động sinh ra khi chương trình thực hiện ngang dòng
lệnh chứa định nghĩa và bị huỷ đi sau khi chương trình hoàn tất
khối chứa định nghĩa đó.
Chương 2. Lớp 55
Khi khởi động một đối tượng bằng một đối tượng cùng
kiểu, cơ chế tạo đối tượng mặc nhiên là sao chép từng
bit, do đó đối tượng được khởi động sẽ chia sẻ tài
nguyên với đối tượng nguồn.
Đối tượng là biến địa phương
#include
#include
char *strdup(const char *s)
{
return strcpy(new char[strlen(s) + 1], s);
}
class string
Chương 2. Lớp 56
{
char *p;
public:
string(char *s = "") {p = strdup(s);}
~string() {cout << "delete "<< (void *)p <<
"\n"; delete [] p;}
void Output() const {cout << p;}
};
void main()
{
string a("Nguyen Van A");
string b = a; // String b(a)
a.Output(); cout << "\n";
b.Output(); cout << "\n";
}
Chương 2. Lớp 57
Xuất liệu khi thực hiện đoạn chương trình trên:
Nguyen Van A
Nguyen Van A
delete 0x0f06
delete 0x0f06
Đối tượng b có nội dung vật lý giống với a, nghĩa là dùng
chung phần tài nguyên (con trỏ đến chuỗi “Nguyen Van
A”). Khi kết thúc hàm main() hai đối tượng này bị huỷ,
Chương 2. Lớp 58
phần tài nguyên chung bị huỷ 2 lần (SAI).
Đối tượng là tham số truyền bằng giá trị
Đối tượng là tham số hàm, truyền bằng giá trị thì tham
số hình thức là bản sao của tham số thực sự, nên có nội
dung vật lý giống tham số thực sự do cơ chế sao chép
từng bit.
extern char *strdup(const char *s);
class String
{
char *p;
Chương 2. Lớp 59
public:
String(char *s = "") {p = strdup(s);}
~String() {cout << "delete "<< (void *)p <<
"\n"; delete [] p;}
void Output() const {cout << p;}
int Compare(String s) const;
};
Đối tượng là tham số truyền bằng giá trị
int String::Compare(String s) const
{
return strcmp(p,s.p);
}
void main()
{
Chương 2. Lớp 60
String a("Nguyen Van A");
String b("Le Van Beo");
int c = a.Compare(b);
cout 0 ? "a > b" : c == 0 ? "a =
b" : "a < b") << "\n";
}
Đối tượng là tham số truyền bằng giá trị
Khi thực hiện đoạn chương trình trên, ta được xuất liệu
(có thể thay đổi ở những lần thực hiện khác nhau hoặc
ở máy khác):
delete 0x0f34
a > b
delete 0x0f34
delete 0x0f22
Chương 2. Lớp 61
Đối tượng là giá trị trả về
extern char *strdup(const char *s);
class String
{
char *p;
public:
String(char *s = "") {p = strdup(s);}
~String() {cout << "delete "<< (void *)p << "\n";
delete [] p;}
Chương 2. Lớp 62
void Output() const {cout << p;}
int Compare(String s) const;
String UpCase() const;
};
Đối tượng là giá trị trả về
String String::UpCase() const
{
String r = *this;
strupr(r.p);
return r;
}
void main()
{
Chương 2. Lớp 63
clrscr();
String a("Nguyen Van A");
cout << "a = "; a.Output(); cout << "\n";
String A;
A = a.UpCase();
cout << "a = "; a.Output(); cout << "\n";
cout << "A = "; a.Output(); cout << "\n";
}
Đối tượng là giá trị trả về
Khi thực hiện đoạn chương trình trên, ta được xuât liệu
a = Nguyen Van A
delete 0x0f36
delete 0x0f36
a = 2¤2¤EN VAN A
A = 2¤2¤EN VAN A
delete 0x0f36
Chương 2. Lớp 64
delete 0x0f36
Null pointer assignment
Đối tượng giá trị trả về là bản sao của biểu thức trả về.
Do đó có sự chia sẻ tài nguyên (SAI).
Đối tượng là giá trị trả về
Các lỗi sai gây ra ở đoạn chương trình trên do sao chép
đối tượng (phát biểu String r = *this), đối tượng giá trị trả
về và phép gán (A = a.Upcase).
Ta có thể khắc phục lỗi gây ra do phép gán bằng cách
thay hai phát biểu khai báo A và gán bằng phát biểu
khởi động:
String A = a.UpCase();
Chương 2. Lớp 65
Đối tượng là giá trị trả về
void main()
{
clrscr();
String a("Nguyen Van A");
cout << "a = "; a.Output(); cout << "\n";
String A = a.UpCase();
cout << "a = "; a.Output(); cout << "\n";
cout << "A = "; a.Output(); cout << "\n";
}
Chương 2. Lớp 66
Đối tượng là giá trị trả về
Xuất liệu trong trường hợp này là
a = Nguyen Van A
delete 0x0d32
a = NGUYEN VAN A
A = NGUYEN VAN A
delete 0x0d32
delete 0x0d32
Chương 2. Lớp 67
Null pointer assignment
Phương thức thiết lập bản sao
Các lỗi sai nêu trên gây ra do sao chép đối tượng, ta có
thể khắc phục bằng cách “dạy” trình biên dịch sao
chép đối tượng một cách luận lý thay vì sao chép từng
bit theo nghĩa vật lý. Điều đó được thực hiện nhờ
phương thức thiết lập bản sao.
Phương thức thiết lập bản sao là phương thức thiết lập
có tham số là đối tượng cùng kiểu, dùng để sao chép đối
Chương 2. Lớp 68
tượng.
Phương thức thiết lập bản sao
Phương thức thiết lập bản sao thực hiện sao chép theo
nghĩa logic, thông thường là tạo nên tài nguyên mới (sao
chép sâu).
Phương thức thiết lập bản sao cũng có thể chia sẻ tài
nguyên cho các đối tượng (sao chép nông). Trong trường
hợp này, cần có cơ chế để kiểm soát sử huỷ bỏ đối
tượng.
Chương 2. Lớp 69
Phương thức thiết lập bản sao
extern char *strdup(const char *s);
class String
{
char *p;
public:
String(char *s = "") {p = strdup(s);}
String(const String &s) {p =
strdup(s.p);}
Chương 2. Lớp 70
~String() {cout << "delete "<< (void *)p
<< "\n"; delete [] p;}
void Output() const {cout << p;}
int Compare(String s) const;
String UpCase() const;
};
Phương thức thiết lập bản sao
int String::Compare(String s) const
{
return strcmp(p,s.p);
}
String String::UpCase() const
{
String r = *this;
Chương 2. Lớp 71
strupr(r.p);
return r;
}
Phương thức thiết lập bản sao
void main()
{
clrscr();
String a("Nguyen Van A");
String b("Le Van Beo");
String aa = a;
int c = a.Compare(b);
Chương 2. Lớp 72
cout 0 ? "a > b" : c == 0 ? "a =
b" : "a < b") << "\n";
cout << "a = "; a.Output(); cout << "\n";
String A = a.UpCase();
cout << "a = "; a.Output(); cout << "\n";
cout << "A = "; A.Output(); cout << "\n";
}
Phương thức thiết lập bản sao
Xuất liệu trong trường hợp trên như sau:
delete 0x0d84
a > b
a = Nguyen Van A
delete 0x0d84
a = Nguyen Van A
A = NGUYEN VAN A
Chương 2. Lớp 73
delete 0x0d96
delete 0x0d72
delete 0x0d62
delete 0x0d50
Mỗi đối tượng đều có tài nguyên riêng nên không xảy
trường hợp một vùng tài nguyên bị giải phóng nhiều lần.
Phương thức thiết lập bản sao
Tham số của phương thức thiết lập bản sao bắt buộc là
tham chiếu.
Phương thức thiết lập bản sao có thể được dùng để sao
chép nông, tài nguyên vẫn được chia sẻ nhưng có một
biến đếm để kiểm soát.
Lưu ý:
Nếu đối tượng không có tài nguyên riêng thì không cần
Chương 2. Lớp 74
phương thức thiết lập bản sao.
Khi truyền tham số là đối tượng thuộc lớp có phương
thức thiết lập bản sao, ta nên truyền bằng tham chiếu
và có thêm từ khoá const.
Sao chép nông và sao chép sâu
Dùng phương thức thiết lập bản sao như trên, trong đó
đối tượng mới có tài nguyên riêng là sao chép sâu.
Ta có thể sao chép nông bằng cách chia sẻ tài nguyên
và dùng một biến đếm để kiểm soát số thể hiện các đối
tượng có chia sẻ cùng tài nguyên.
Khi một đối tượng thay đổi nội dung, nó phải được tách
ra khỏi các đối tượng dùng chung tài nguyên, nói cách
Chương 2. Lớp 75
khác, nó phải có tài nguyên riêng (như sao chép sâu).
Ví dụ về sao chép nông
extern char *strdup(const char *s);
class StringRep
{
friend class String;
char *p;
int n;
StringRep(const char *s) {p = strdup(s); n = 1;}
~StringRep() {cout << "delete ” << (void *)p <<
Chương 2. Lớp 76
"\n"; delete [] p;}
};
Ví dụ về sao chép nông
class String
{
StringRep *rep;
public:
String(const char *s = "") {rep = new
StringRep(s);}
String(const String &s) {rep = s.rep;
rep->n++;}
Chương 2. Lớp 77
~String();
void Output() const {cout p;}
int Compare(String s) const;
String UpCase() const;
void ToUpper();
};
Ví dụ về sao chép nông
String::~String()
{
if (--rep->n <= 0)
delete rep;
}
int String::Compare(String s) const
{
return strcmp(rep->p,s.rep->p);
Chương 2. Lớp 78
}
String String::UpCase() const
{
String r(rep->p);
strupr(r.rep->p);
return r;
}
Ví dụ về sao chép nông
void main()
{
clrscr();
String a("Nguyen Van A");
String b("Le Van Beo");
String aa = a;
int c = a.Compare(b);
cout 0 ? "a > b" : c == 0 ? "a = b" : "a
Chương 2. Lớp 79
< b") << "\n";
cout << "a = "; a.Output(); cout << "\n";
String A = a.UpCase();
cout << "a = "; a.Output(); cout << "\n";
cout << "A = "; A.Output(); cout << "\n";
}
Ví dụ về sao chép nông
Xuất liệu khi thực hiện đoạn chương trình trên như sau:
a > b
a = Nguyen Van A
a = Nguyen Van A
A = NGUYEN VAN A
delete 0x0d8a
delete 0x0d72
Chương 2. Lớp 80
delete 0x0d58
Ví dụ về sao chép nông
delete 0x0d84
a > b
a = Nguyen Van A
delete 0x0d84
a > b
a = Nguyen Van A
a = Nguyen Van A
A = NGUYEN VAN A
So sánh với sao chép sâu: Bên trái, dùng sao chép sâu, bên
phải dùng sao chép nông.
Chương 2. Lớp 81
a = Nguyen Van A
A = NGUYEN VAN A
delete 0x0d96
delete 0x0d72
delete 0x0d62
delete 0x0d50
delete 0x0d8a
delete 0x0d72
delete 0x0d58
Đối tượng tĩnh
Đối tượng tĩnh có thể là đối tượng được định nghĩa toàn
cục, được định nghĩa có thêm từ khoá static (toàn cục
hoặc địa phương).
Đối tượng toàn cục hoặc tĩnh toàn cục được tạo ra khi
bắt đầu chương trình và bị huỷ đi khi kết thúc chương
trình.
Đối tượng tĩnh địa phương được tạo ra khi chương trình
Chương 2. Lớp 82
thực hiện đoạn mã chứa định nghĩa đối tượng lần đầu
tiên và bị huỷ đi khi kết thúc chương trình.
Đối tượng tĩnh
Trình tự thực hiện chương trình gồm:
• Gọi phương thức thiết lập cho các đối tượng toàn cục
• Thực hiện hàm main()
• Gọi phương thức huỷ bỏ cho các đối tượng toàn cục
Ví dụ sau minh hoạ ý nghĩa của đối tượng toàn cục
Chương 2. Lớp 83
Ví dụ về đối tượng toàn cục
Xét đoạn chương trình sau:
#include
void main()
{
cout << "Hello, world.\n";
}
Hãy sửa lại đoạn chương trình trên để có xuất liệu:
Chương 2. Lớp 84
Entering a C++ program saying...
Hello, world.
And then exitting
Yêu cầu không thay đổi hàm main() dưới bất kỳ hình
thức nào.
Ví dụ về đối tượng toàn cục
Đoạn chương trình được sửa lại như sau:
#include
void main()
{
cout << "Hello, world.\n";
}
class Dummy
Chương 2. Lớp 85
{
public:
Dummy() {cout << "Entering a C++ program
saying...\n";}
~Dummy() {cout << "And then exitting...";}
}d;
Đối tượng là thành phần của lớp
Đối tượng có thể là thành phần của đối tượng khác, khi
một đối tượng thuộc lớp “lớn” được tạo ra, các thành
phần của nó cũng được tạo ra. Phương thức thiết lập
(nếu có) sẽ được tự động gọi cho các đối tượng thành
phần.
Nếu đối tượng thành phần phải được cung cấp tham số
khi thiết lập thì đối tượng kết hợp (đối tượng lớn) phải
Chương 2. Lớp 86
có phương thức thiết lập để cung cấp tham số thiết lập
cho các đối tượng thành phần.
Đối tượng là thành phần của lớp
Cú pháp để khởi động đối tượng thành phần là dùng
dấu hai chấm (:) theo sau bởi tên thành phần và tham số
khởi động.
Khi đối tượng kết hợp bị huỷ đi thì các đối tượng thành
phần của nó cũng bị huỷ đi, nghĩa là phương thức huỷ
bỏ sẽ được gọi cho các đối tượng thành phần, sau khi
phương thức huỷ bỏ của đối tượng kết hợp được gọi.
Chương 2. Lớp 87
Đối tượng là thành phần của lớp
class Diem
{
double x,y;
public:
Diem(double xx, double yy) {x = xx; y = yy;}
// ...
};
class TamGiac
{
Chương 2. Lớp 88
Diem A,B,C;
public:
void Ve() const;
// ...
};
TamGiac t; // Bao sai
Đối tượng là thành phần của lớp
class String
{
char *p;
public:
String(char *s) {p = strdup(s);}
String(const String &s) {p = strdup(s.p);}
~String() {cout << "delete "<< (void *)p << "\n";
delete [] p;
//...
};
Chương 2. Lớp 89
class SinhVien
{
String MaSo;
String HoTen;
int NamSinh;
public:
};
SinhVien s; // Bao sai
Đối tượng thành phần - thiết lập
class Diem
{
double x,y;
public:
Diem(double xx, double yy) {x = xx; y = yy;}
// ...
};
Chương 2. Lớp 90
Khởi động đối tượng thành phần
class TamGiac
{
Diem A,B,C;
public:
TamGiac(double xA, double yA, double xB, double
yB, double xC, double yC):A(xA,yA),
B(xB,yB),C(xC,yC){}
void Ve() const;
Chương 2. Lớp 91
// ...
};
TamGiac t(100,100,200,400,300,300);
Đối tượng là thành phần của lớp
class String
{
char *p;
public:
String(char *s) {p = strdup(s);}
String(const String &s) {p = strdup(s.p);}
~String() {cout << "delete "<< (void *)p <<
"\n"; delete [] p;
Chương 2. Lớp 92
//...
};
Khởi động đối tượng thành phần
class SinhVien
{
String MaSo;
String HoTen;
int NamSinh;
public:
SinhVien(char *ht, char *ms, int ns):HoTen(ht),
MaSo(ms){NamSinh = ns;}
Chương 2. Lớp 93
//...
};
SinhVien s(“Nguyen Van Beo”, “19920005”, 1985);
//Ok
Khởi động đối tượng thành phần
Cú pháp dùng dấu hai chấm cũng được dùng cho đối
tượng thành phần thuộc kiểu cơ bản.
class Diem
{
double x,y;
public:
Diem(double xx, double yy):x(xx), y(yy){}
Chương 2. Lớp 94
// ...
};
Khởi động đối tượng thành phần
Cú pháp dùng dấu hai chấm cũng được dùng cho đối
tượng thành phần thuộc kiểu cơ bản.
class SinhVien
{
String MaSo, HoTen;
int NamSinh;
public:
Chương 2. Lớp 95
SinhVien(char *ht, char *ms, int ns):HoTen(ht),
MaSo(ms), NamSinh(ns){}
//...
};
Đối tượng thành phần - Huỷ bỏ
Khi đối tượng kết hợp bị huỷ bỏ, các đối tượng thành phần
của nó cũng bị huỷ bỏ.
class SinhVien
{
String MaSo, HoTen;
int NamSinh;
int SoMon;
double *Diem;
public:
Chương 2. Lớp 96
SinhVien(char *ht, char *ms, int ns, int sm, double *d);
~SinhVien() {delete [] Diem;}
//...
};
SinhVien::SinhVien(char *ht, char *ms, int ns, int sm,
double *d):HoTen(ht), MaSo(ms), NamSinh(ns), SoMon(sm)
{
memcpy(Diem = new double[SoMon], d, SoMon*sizeof(double));
}
Đối tượng là thành phần của mảng
Khi một mảng được tạo ra, các phần tử của nó cũng
được tạo ra, do đó phương thức thiết lập sẽ được gọi cho
từng phần tử một.
Vì không thể cung cấp tham số khởi động cho tất cả các
phần tử của mảng nên khi khai báo mảng, mỗi đối
tượng trong mảng phải có khả năng tự khởi động, nghĩa
là có thể được thiết lập không cần tham số.
Chương 2. Lớp 97
Đối tượng có khả năng tự khởi động trong các trường
hợp sau:
• Lớp không có phương thức thiết lập.
• Lớp có phương thức thiết lập không tham số.
• Lớp có phương thức thiết lập mà mọi tham số đều có giá trị
mặc nhiên.
Đối tượng là thành phần của mảng
class Diem
{
double x,y;
public:
Diem(double xx, double yy):x(xx), y(yy) {}
void Set(double xx, double yy) {x = xx, y = yy;}
// ...
};
class String
Chương 2. Lớp 98
{
char *p;
public:
String(char *s) {p = strdup(s);}
String(const String &s) {p = strdup(s.p);}
~String() {cout << "delete "<< (void *)p <<
"\n"; delete [] p;}
// ...
Đối tượng là thành phần của mảng
class SinhVien
{
String MaSo;
String HoTen;
int NamSinh;
public:
SinhVien(char *ht, char *ms, int ns):HoTen(ht),
MaSo(ms), NamSinh(ns){}
Chương 2. Lớp 99
//...
};
String as[3]; // Bao sai
Diem ad[5]; // Bao sai
SinhVien asv[7]; // Bao sai
PTTLập với tham số có giá trị mặc nhiên
class Diem
{
double x,y;
public:
Diem(double xx = 0, double yy = 0):x(xx), y(yy){}
void Set(double xx, double yy) {x = xx, y = yy;}
// ...
};
class String
Chương 2. Lớp 100
{
char *p;
public:
String(char *s = “”) {p = strdup(s);}
String(const String &s) {p = strdup(s.p);}
~String() {cout << "delete "<< (void *)p << "\n";
delete [] p;}
// ...
PTTlập với tham số có giá trị mặc nhiên
class SinhVien
{
String MaSo;
String HoTen;
int NamSinh;
public:
SinhVien(char *ht = “Nguyen Van A”, char *ms =
“19920014”, int ns =
1982):HoTen(ht), MaSo(ms),
Chương 2. Lớp 101
NamSinh(ns){}
//...
};
String as[3]; // Ok: Ca ba phan tu deu la chuoi rong
Diem ad[5]; // Ok: ca 5 diem deu la (0,0)
SinhVien asv[7]; // Ok: Het sai ca 7 sinh vien deu
co cung hoten, maso, namsinh
Dùng PTTlập không tham số
class Diem
{
double x,y;
public:
Diem(double xx, double yy):x(xx), y(yy){}
Diem():x(0), y(0){}
// ...
};
class String
Chương 2. Lớp 102
{
char *p;
public:
String(char *s) {p = strdup(s);}
String() {p = strdup(“”);}
~String() {cout << "delete "<< (void *)p << "\n";
delete [] p;}
// ...
};
Dùng ptthiết lập không tham số
class SinhVien
{
String MaSo;
String HoTen;
int NamSinh;
public:
SinhVien(char *ht, char *ms, int ns):HoTen(ht),
MaSo(ms), NamSinh(ns){}
Chương 2. Lớp 103
SinhVien():HoTen(“Nguyen Van A”),
MaSo(“19920014”), NamSinh(1982){}
//...
};
String as[3]; // Ca ba phan tu deu la chuoi rong
Diem ad[5]; // ca 5 diem deu la (0,0)
SinhVien asv[7];// Ca 7 sinh vien deu co cung
hoten, maso, namsinh
Đối tượng được cấp phát động
Đối tượng được cấp phát động là các đối tượng được tạo
ra bằng phép toán new và bị huỷ đi bằng phép toán
delete
Phép toán new cấp đối tượng trong vùng heap (hay vùng
free store) và gọi phương thức thiết lập cho đối tượng
được cấp.
Dùng new có thể cấp một đối tượng và dùng delete để
Chương 2. Lớp 104
huỷ một đối tượng.
Dùng new và delete cũng có thể cấp nhiều đối tượng và
huỷ nhiều đối tượng.
Đối tượng được cấp phát động
class String
{
char *p;
public:
String(char *s) {p = strdup(s);}
String(const String &s) {p = strdup(s.p);}
~String() {delete [] p;}
//...
Chương 2. Lớp 105
};
class Diem
{
double x,y;
public:
Diem(double xx, double yy):x(xx),y(yy){};
//...
};
//...
Cấp và huỷ một đối tượng
int *pi = new int;
int *pj = new int(15);
Diem *pd = new Diem(20,40);
String *pa = new String("Nguyen Van A");
//...
delete pa;
delete pd;
delete pj;
Chương 2. Lớp 106
delete pi;
Cấp và huỷ nhiều đối tượng
Trong trường hợp cấp nhiều đối tượng, ta không thể
cung cấp tham số cho từng phần tử được cấp:
int *pai = new int[10];
Diem *pad = new Diem[5]; // Bao sai
String *pas = new String[5]; // Bao sai
//...
Thông báo lỗi cho đoạn chương trình trên như sau:
Chương 2. Lớp 107
Cannot find default constructor to initialize array element of type
'Diem'
Cannot find default constructor to initialize array element of type
String’
Lỗi trên được khắc phục bằng cách cung cấp phương
thức thiết lập để đối tượng có khả năng tự khởi động.
Cấp và huỷ nhiều đối tượng
class String
{
char *p;
public:
String(char *s = "Alibaba") {p = strdup(s);}
String(const String &s) {p = strdup(s.p);}
~String() {delete [] p;}
//...
Chương 2. Lớp 108
};
class Diem
{
double x,y;
public:
Diem(double xx, double yy):x(xx),y(yy){};
Diem():x(0),y(0){};
//...
};
Cấp và huỷ nhiều đối tượng
Khi đó mọi phần tử được cấp đều được khởi động với
cùng giá trị
int *pai = new int[10];
Diem *pad = new Diem[5];
// ca 5 diem co cung toa do (0,0)
String *pas = new String[5];
// Ca 5 chuoi cung duoc khoi dong bang “Alibaba”
Việc huỷ nhiều đối tượng được thực hiện bằng cách dùng
Chương 2. Lớp 109
delete và có thêm dấu [] ở trước.
delete [] pas;
delete [] pad;
delete [] pai;
(?) Có thể thay ba phát biểu trên bằng một phát biểu duy
nhất sau được không ?
delete pas,pad,pai; // ??
2.4 Giao diện và chi tiết cài đặt
Lớp có hai phần tách rời, một là phần giao diện khai báo
trong phần public để người sử dụng “thấy” và sử dụng, và
hai là chi tiết cài đặt bao gồm dữ liệu khai báo trong
phần private của lớp và chi tiết mã hoá các hàm thành
phần, vô hình đối với người dùng.
Ta có thể thay đổi uyển chuyển chi tiết cài đặt, nghĩa là
có thể thay đổi tổ chức dữ liệu của lớp, cũng như có thể
Chương 2. Lớp 110
thay đổi chi tiết thực hiện các hàm thành phần (do sự
thay đổi tổ chức dữ liệu hoặc để cải tiến giải thuật).
Nhưng nếu bảo đảm không thay đổi phần giao diện thì
không ảnh hưởng đến người sử dụng, và do đó không làm
đổ vỡ kiến trúc của hệ thống.
Lớp ThoiDiem có thể được cài đặt với các thành phần
dữ liệu là giờ, phút, giây hoặc tổng số giây tính từ 0 giờ.
Lớp ThoiDiem – Cách 1
class ThoiDiem
{
int gio, phut, giay;
static bool HopLe(int g, int p, int gy);
public:
ThoiDiem(int g = 0, int p = 0, int gy = 0)
{Set(g,p,gy);}
void Set(int g, int p, int gy);
Chương 2. Lớp 111
int LayGio() const {return gio;}
int LayPhut() const {return phut;}
int LayGiay() const {return giay;}
void Nhap();
void Xuat() const;
void Tang();
void Giam();
};
Lớp ThoiDiem – Cách 2
class ThoiDiem
{
long tsgiay;
static bool HopLe(int g, int p, int gy);
public:
ThoiDiem(int g = 0, int p = 0, int gy = 0)
{Set(g,p,gy);}
void Set(int g, int p, int gy);
Chương 2. Lớp 112
int LayGio() const {return tsgiay / 3600;}
int LayPhut() const {return (tsgiay%3600)/60;}
int LayGiay() const {return tsgiay % 60;}
void Nhap();
void Xuat() const;
void Tang();
void Giam();
};
Giao diện và chi tiết cài đặt
Có thể xem chi tiết đầy đủ lớp thời điểm cài đặt bằng
giờ, phút, giây và cài đặt bằng tổng số giây trong tập tin
nguồn thgian.cpp và thgian2.cpp,
Tương tự lớp Stack có thể được cài đặt dưới dạng mảng
hoặc dưới dạng danh sách liên kết.
Chương 2. Lớp 113
2.5 Các nguyên tắc xây dựng lớp
Khi ta có thể nghĩ đến “nó” như một khái niệm riêng rẽ,
xây dựng lớp biểu diễn khái niệm đó. Ví dụ lớp
SinhVien.
Khi ta nghĩ đến “nó” như một thực thể riêng rẽ, tạo đối
tượng thuộc lớp. Ví dụ đối tượng Sinh viên “Nguyen Van
A” (và các thuộc tính khác như mã số, năm sinh).
Lớp là biểu diễn cụ thể của một khái niệm, vì vậy lớp
Chương 2. Lớp 114
luôn luôn là DANH TỪ.
Các thuộc tính của lớp là các thành phần dữ liệu, nên
chúng luôn luôn là DANH TỪ.
Các hàm thành phần là các thao tác chỉ rõ hoạt động
của lớp nên các hàm này là ĐỘNG TỪ.
Các nguyên tắc xây dựng lớp
// SAI
class TamGiac
// DUNG
class TamGiac
Các thuộc tính dữ liệu phải vừa đủ để mô tả khái niệm,
không dư, không thiếu.
Các thuộc tính có thể được suy diễn từ những thuộc tính
khác thì dùng hàm thành phần để thực hiện tính toán. Chu
vi, diện tích tam giác là thuộc tính suy diễn.
Chương 2. Lớp 115
{
Diem A,B,C;
double ChuVi,
DienTich;
public:
//...
};
{
Diem A,B,C;
public:
//...
double ChuVi()const;
double DienTich() const;
};
Các nguyên tắc xây dựng lớp
Cá biệt có thể có một số thuộc tính suy diễn đòi hỏi
nhiều tài nguyên hoặc thời gian để thực hiện tính toán, ta
có thể khai báo là dữ liệu thành phần. Ví dụ tuổi trung
bình của dân Việt Nam.
class QuocGia
{
long DanSo;
double DienTich;
Chương 2. Lớp 116
double TuoiTrungBinh;
//...
public:
double TinhTuoiTB() const;
//...
};
Các nguyên tắc xây dựng lớp
Chi tiết cài đặt, bao gồm dữ liệu và phần mã hoá các
hàm thành phần có thể thay đổi uyển chuyển nhưng
phần giao diện, nghĩa là phần khai báo các hàm thành
phần cần phải cố định để không ảnh hưởng đến người
sử dụng (xem phần 2.4). Tuy nhiên nên cố gắng cài đặt
dữ liệu một cách tự nhiên theo đúng khái niệm.
// NEN // KHONG NEN
Chương 2. Lớp 117
class PhanSo
{
int tu, mau;
public:
//...
};
class PhanSo
{
long tu_mau;
public:
//...
};
Các nguyên tắc xây dựng lớp
Dữ liệu thành phần nên được kết hợp thay vì phân rã
// NEN
class TamGiac
{
Diem A,B,C;
public:
//...
// KHONG NEN
class TamGiac
{
double xA, yA, xB, yB,
xC, yC;
public:
Chương 2. Lớp 118
};
class HinhTron
{
Diem Tam;
double BanKinh;
public:
//...
};
//...
};
class HinhTron
{
double tx, ty, BanKinh;
public:
//...
};
Các nguyên tắc xây dựng lớp
Trong mọi trường hợp, nên có phương thức thiết lập để
khởi động đối tượng.
Nên có phương thức thiết lập có khả năng tự khởi động
không cần tham số.
Nếu đối tượng có nhu cầu cấp phát tài nguyên thì phải
có phương thức thiết lập, phương thức thiết lập bản sao
để khởi động đối tượng bằng đối tượng cùng kiểu và có
Chương 2. Lớp 119
phương thức huỷ bỏ để dọn dẹp. Ngoài ra còn phải có
phép gán (chương tiếp theo).
Ngược lại, đối tượng đơn giản không cần tài nguyên
riêng thì không cần phương thức thiết lập bản sao và
cũng không cần phương thức huỷ bỏ.
2.6 Một số ví dụ về lớp
Lớp Diem biểu diễn khái niệm điểm trong mặt phẳng
với hai thành phần toạ độ x và y. Khai báo lớp Diem
được đặt trong tập tin diem.h và chi tiết cài đặt các hàm
thành phần được đặt trong tập tin diem.cpp. Có thể xây
dựng một số ứng dụng của lớp điểm như trong tập tin
tdiem.cpp hoặc dongho.cpp với các ứng dụng tương ứng
là tdiem.exe và dongho.exe (hoặc dongho2.exe,
dongho3.exe).
Chương 2. Lớp 120
Lớp DaGiac biểu diễn khái niệm đa giác trong mặt
phẳng có các thao tác tịnh tiến, vị tự, quay, vẽ Phần
khai báo được đặt trong tập tin dagiac.h, phần cài đặt
trong tập tin dagiac.cpp và ứng dụng tdg.cpp cho phép
tạo, vẽ, tịnh tiến và quay đa giác. Tập tin thực thi là
tdg.exe
Các file đính kèm theo tài liệu này:
- lthdt_ch02_0308.pdf