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

LTHDT-Ch01-Lop [Compatibility Mode] OTTN

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 (955.76 KB, 118 trang )

<span class='text_page_counter'>(1)</span><div class='page_container' data-page=1>

Lập Trình Hướng Đối Tượng


Lập Trình Hướng Đối Tượng


Lập Trình Hướng Đối Tượng


Lập Trình Hướng Đối Tượng



</div>
<span class='text_page_counter'>(2)</span><div class='page_container' data-page=2>

Chương 1


Chương 1



</div>
<span class='text_page_counter'>(3)</span><div class='page_container' data-page=3>

Giới thiệu


Giới thiệu



 Phân tích thiết kế và lập trình theo hướng đối tượng tuy sinh sau đẻ


muộn nhưng đã chứng tỏ được những ưu điểm vượt trội so với cách
tiếp cận cổ điển.


 Trong lãnh vực phân tích và thiết kế hệ thống, hướng tiếp cận mới


mẻ này đã thu hút nhiều nhà nghiên cứu tên tuổi. Nhiều kiểu mẫu,
phương pháp luận, mơ hình phân tích đã được đưa ra với những
mức độ thành công khác nhau.


Ta sẽ nghiên cứu phương hướng phân tích theo quan điểm của


 Ta sẽ nghiên cứu phương hướng phân tích theo quan điểm của


</div>
<span class='text_page_counter'>(4)</span><div class='page_container' data-page=4>

Phương pháp phân tích bằng mô hình


Phương pháp phân tích bằng mô hình



 Phân tích dựa trên cơ sở mơ hình hóa các đối tượng



trong thế giới thực.


 Dùng mơ hình để xây dựng một thiết kế khơng phụ


thuộc ngôn ngữ được tổ chức xung quanh các đối tượng.


 So với cách tổ chức cổ điển, mô hình hố và thiết kế


</div>
<span class='text_page_counter'>(5)</span><div class='page_container' data-page=5>

Phương pháp phân tích bằng mô hình


Phương pháp phân tích bằng mô hình



 Các khái niệm trong thế giới thực được mơ hình hố


bằng các ký hiệu đồ hoạ mơ tả các đối tượng của chúng
(cấu trúc dữ liệu và hành vi) độc lập với ngôn ngữ.


 Các khái niệm và ký hiệu này có thể được dùng thống


nhất suốt quá trình phát triển hệ thống từ phân tích,


thiết kế đến cài đặt mà khơng cần thay đổi qua các giai
đoạn như một số phương pháp luận khác.


đoạn như một số phương pháp luận khác.


 Không quan tâm đến chi tiết cài đặt cho đến giai đoạn


</div>
<span class='text_page_counter'>(6)</span><div class='page_container' data-page=6>

Phương pháp phân tích bằng mô hình


Phương pháp phân tích bằng mô hình




 Các khái niệm liên quan đến máy tính chỉ được đưa ra


ở bước mã hóa sau cùng, nhờ đó giữ được sự uyển


</div>
<span class='text_page_counter'>(7)</span><div class='page_container' data-page=7>

Phương pháp luận hướng đối tượng


Phương pháp luận hướng đối tượng



 Mô hình hóa và thiết kế theo hướng đối tượng là một lối


suy nghĩ mới về vấn đề cần giải quyết dùng các mơ
hình được tổ chức xung quanh các khái niệm trong thế
giới thực.


 Trong một hệ thống thông tin hướng đối tượng, mọi thứ,


hay hầu như mọi thứ, được quan điểm như các đối
tượng.


hay hầu như mọi thứ, được quan điểm như các đối
tượng.


 Mỗi đối tượng là sự kết hợp của cả hai thành phần đặc


</div>
<span class='text_page_counter'>(8)</span><div class='page_container' data-page=8>

Phương pháp luận hướng đối tượng


Phương pháp luận hướng đối tượng



 Phương pháp luận theo quan điểm của J.Rumbaugh bao


gồm xây dựng một mơ hình của hệ thống trong lãnh vực
ứng dụng và thêm chi tiết cài đặt trong quá trình thiết


kế hệ thống.


 Các ký hiệu đồ họa được sử dụng để biểu diễn các khái


niệm hướng đối tượng.
niệm hướng đối tượng.


 Cách tiếp cận này được gọi là kỹ thuật thiết kế bằng mô


</div>
<span class='text_page_counter'>(9)</span><div class='page_container' data-page=9>

Phương pháp luận hướng đối tượng


Phương pháp luận hướng đối tượng



 Kỹ thuật mơ hình hố OMT bao gồm các bước:


Phân tích Thiết kế
hệ thống


Thiết kế


</div>
<span class='text_page_counter'>(10)</span><div class='page_container' data-page=10>

Các khái niệm hướng đối tượng


Các khái niệm hướng đối tượng



 Trừu tượng hố :


• Nhấn mạnh vào các khía cạnh cốt yếu vốn có của một thực thể


và bỏ qua những tính chất riêng biệt.


• Sử dụng trừu tượng hố trong phân tích có nghĩa là làm việc với



các khái niệm trong lãnh vực ứng dụng và bỏ qua chi tiết cài
đặt.


• Hầu hết các ngơn ngữ lập trình hiện đại đều hổ trợ trừu tượng
• Hầu hết các ngơn ngữ lập trình hiện đại đều hổ trợ trừu tượng


hoá. Nhưng sự trừu tượng hóa được tận dụng trong tiếp cận đối
tượng với tính kế thừa (inheritance) và tính đa dạng


</div>
<span class='text_page_counter'>(11)</span><div class='page_container' data-page=11>

Các khái niệm hướng đối tượng


Các khái niệm hướng đối tượng



 Tính đóng gói :


• Tách rời các khía cạnh giao diện với bên ngồi của đối tượng


với chi tiết cài đặt bên trong.


• Tính đóng gói ngăn chặn khả năng một chương trình trở nên


quá phụ thuộc lẫn nhau dẫn tới hậu quả một sự thay đổi nhỏ
có thể ảnh hưởng lớn đến tồn bộ hệ thống.


• Trong tiếp cận O.O. khả năng kết hợp dữ liệu và hành vi trong
• Trong tiếp cận O.O. khả năng kết hợp dữ liệu và hành vi trong


</div>
<span class='text_page_counter'>(12)</span><div class='page_container' data-page=12>

Các khái niệm hướng đối tượng


Các khái niệm hướng đối tượng



 Kết hợp dữ liệu và hành vi:



• Trong cách tiếp cận thủ tục cổ điển, hệ thống được xây dựng


trên hai sơ đồ phân cấp chằng chịt: sơ đồ phân cấp dữ liệu và
sơ đồ phân cấp thủ tục, trong đó sự liên hệ giữa một loại dữ liệu
và các thủ tục xử lý dữ liệu rất mờ nhạt, dẫn đến khó khăn


trong việc sửa chữa, nâng cấp trong tương lai.


• Cách tiếp cận O.O. loại bỏ những nhược điểm kể trên bằng
• Cách tiếp cận O.O. loại bỏ những nhược điểm kể trên bằng


</div>
<span class='text_page_counter'>(13)</span><div class='page_container' data-page=13>

Các khái niệm hướng đối tượng


Các khái niệm hướng đối tượng



Sơ đồ phân cấp dữ liệu




Kết hợp dữ liệu và hành vi



Sơ đồ phân cấp lớp
Sơ đồ phân cấp dữ liệu


</div>
<span class='text_page_counter'>(14)</span><div class='page_container' data-page=14>

Đối tượng và lớp


Đố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ó


</div>
<span class='text_page_counter'>(15)</span><div class='page_container' data-page=15>

Đối tượng và lớp


Đố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.


tính của Người.


 Một thao tác (operation) là một hàm hay một phép biến


</div>
<span class='text_page_counter'>(16)</span><div class='page_container' data-page=16>

Sơ đồ đối tượng


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 đó.


</div>
<span class='text_page_counter'>(17)</span><div class='page_container' data-page=17>

Sơ đồ lớp và sơ đồ thể hiện


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


Điểm TB
Đi học


Đi thi


Phân loại


9.2


</div>
<span class='text_page_counter'>(18)</span><div class='page_container' data-page=18>

Đối tượng và 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


 Một đối tượng cụ thể thuộc một lớp được gọi là một thể


</div>
<span class='text_page_counter'>(19)</span><div class='page_container' data-page=19>

Cài đặt lớp trong C++


Cài đặt lớp trong C++



 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.


đặt lớp.


 Lớp trong C++ được cài đặt sử dụng từ khoá struct và


</div>
<span class='text_page_counter'>(20)</span><div class='page_container' data-page=20>

Ví dụ so sánh: Xây dựng kiểu dữ liệu stack.


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:
<b>// Stack1.cpp : </b>


<b>//Dung cau truc va ham toan cuc</b>


#include <iostream.h>
typedef int bool;


typedef int Item;


const bool false = 0, true = 1;
const bool false = 0, true = 1;
struct Stack



{


Item *st, *top;
int size;


</div>
<span class='text_page_counter'>(21)</span><div class='page_container' data-page=21>

Ví dụ so sánh (tt)


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;
delete [] ps->st;
}


bool StackFull(Stack *ps)
{


</div>
<span class='text_page_counter'>(22)</span><div class='page_container' data-page=22>

Ví dụ so sánh (tt)


Ví dụ so saùnh (tt)



bool StackEmpty(Stack *ps)
{



return (ps->top <= ps->st);
}


bool StackPush(Stack *ps, Item x)
{


{


if (StackFull(ps)) return false;
*ps->top++ = x;


</div>
<span class='text_page_counter'>(23)</span><div class='page_container' data-page=23>

Ví dụ so sánh (tt)


Ví dụ so sánh (tt)



bool StackPop(Stack *ps, Item *px)
{


if (StackEmpty(ps)) return false;
*px = *--ps->top;


</div>
<span class='text_page_counter'>(24)</span><div class='page_container' data-page=24>

Ví dụ so sánh (tt)


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);
StackPush(&s, n%16);
n /= 16;


} while(n);


while(StackPop(&s,&x))
cout << hTab[x];


</div>
<span class='text_page_counter'>(25)</span><div class='page_container' data-page=25>

Ví dụ so sánh (tt)


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


 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
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


</div>
<span class='text_page_counter'>(26)</span><div class='page_container' data-page=26>

Ví dụ so sánh (tt)


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];}


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);


</div>
<span class='text_page_counter'>(27)</span><div class='page_container' data-page=27>

Ví dụ so sánh (tt)


Ví dụ so sánh (tt)



bool Stack::Push(Item x)
{


if (Full()) return false;
*top++ = x;


return true;
}


bool Stack::Pop(Item *px)
{


if (Empty()) return false;
*px = *--top;


</div>
<span class='text_page_counter'>(28)</span><div class='page_container' data-page=28>

Ví dụ so sánh (tt)


Ví dụ so sánh (tt)



void XuatHe16(long n)
{


static char hTab[] = “0123456789ABCDEF”;
Stack s;


s.Init(8);


int x;


do {


s.Push(n%16);
n /= 16;


} while(n);


while(s.Pop(&x))
cout << hTab[x];
s.CleanUp();


</div>
<span class='text_page_counter'>(29)</span><div class='page_container' data-page=29>

Ví dụ so sánh (tt)


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
cổ điển. Vì vậy việc lập trình gọn hơn. Tư tưởng thể hiện
ở đâ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


</div>
<span class='text_page_counter'>(30)</span><div class='page_container' data-page=30>

Các hàm thành phần.


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 ngồ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 tốn dấu chấm (.) hoặc


dấu mũi tên (->).
dấu mũi tên (->).


Stack s, *ps = &s;
s.Init(10);


</div>
<span class='text_page_counter'>(31)</span><div class='page_container' data-page=31>

Lớp


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++)
for (int i = 0; i < 20; i++)


s.Push(i);


 Vì vậy, ta khơng có sự an tồ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á


</div>
<span class='text_page_counter'>(32)</span><div class='page_container' data-page=32>

Lớp


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 ngồi khơng được phép
truy xuất. Do đó có sự an tồn dữ liệu.


class Stack
{



Item *st, *top;
int size;


int size;


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);}


</div>
<span class='text_page_counter'>(33)</span><div class='page_container' data-page=33>

Lớp


Lớp



 Phát biểu:


s.size = 100; // Bao sai


</div>
<span class='text_page_counter'>(34)</span><div class='page_container' data-page=34>

Thuộc tính truy xuất


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ó.


</div>
<span class='text_page_counter'>(35)</span><div class='page_container' data-page=35>

Thuộc tính truy xuất


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.


</div>
<span class='text_page_counter'>(36)</span><div class='page_container' data-page=36>

// 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);}
Stack(int sz = 20) {Init(sz);}
~Stack() {delete [] st;}


bool Full() const {return (top - st >= size);}



bool Empty() const {return (top <= st);}
bool Push(Item x);


</div>
<span class='text_page_counter'>(37)</span><div class='page_container' data-page=37>

Ví dụ về lớp và thuộc tính truy xuất


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;
}


}


bool Stack::Pop(Item *px)
{


</div>
<span class='text_page_counter'>(38)</span><div class='page_container' data-page=38>

Ví dụ về lớp và thuộc tính truy xuất


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 {
do {


s.Push(n%16);
n /= 16;


} while(n);


while(s.Pop(&x))


</div>
<span class='text_page_counter'>(39)</span><div class='page_container' data-page=39>

Sử dụng phạm vi truy xuất


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



</div>
<span class='text_page_counter'>(40)</span><div class='page_container' data-page=40>

Tự tham chiếu


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.


địa chỉ this này.


bool Stack::Push(Item x)
{


if (Full()) // if (this->Full())
return false;


*top++ = x; // this->top++ = x;
return true;


</div>
<span class='text_page_counter'>(41)</span><div class='page_container' data-page=41>

Phương thức thiết lập và hủy bỏ.


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
tạo ra. Người ta thường lợi dụng đặc tính trên để khởi
độ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


</div>
<span class='text_page_counter'>(42)</span><div class='page_container' data-page=42>

Phương thức thiết lập và hủy bỏ.


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
bởi tên lớp để phân biệt nó với các hàm thành phần
khác.



</div>
<span class='text_page_counter'>(43)</span><div class='page_container' data-page=43>

Phương thức thiết lập và hủy bỏ.


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);}
Stack(int sz = 20) {Init(sz);}
~Stack() {CleanUp();}


bool Full() const {return (top - st >= size);}


bool Empty() const {return (top <= st);}
bool Push(Item x);


</div>
<span class='text_page_counter'>(44)</span><div class='page_container' data-page=44>

Hàm bạn (friends)


Hàm bạn (friends)



 Ngun 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
 Ta làm một hàm trở thành hàm bạn của lớp bằng cách


đư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


</div>
<span class='text_page_counter'>(45)</span><div class='page_container' data-page=45>

Haøm thành phần hằng


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ừ khố 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



</div>
<span class='text_page_counter'>(46)</span><div class='page_container' data-page=46>

Hàm thành phần hằng


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;
public:


string(char *s = "") {p = strdup(s);}
~string() {delete [] p;}


string(const string &s2) {p = strdup(s2.p);}
void Output() <b>const {cout << p;}</b>


</div>
<span class='text_page_counter'>(47)</span><div class='page_container' data-page=47>

Haøm thành phần hằng


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();
s.Output();


Truong.Output();


</div>
<span class='text_page_counter'>(48)</span><div class='page_container' data-page=48>

Thành phần tónh (static members)


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 tồn cục vì


nó có liên quan mật thiết với lớp.
nó có liên quan mật thiết với lớp.


 Ta cịn dùng hàm thành phần tĩnh để tạo đối tượng có


</div>
<span class='text_page_counter'>(49)</span><div class='page_container' data-page=49>

Ví dụ về thành phần tónh: CDate


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:
public:


static bool LeapYear(int y) {return y%400
== 0 || y%4==0 && y%100 != 0;}


static int DayOfMonth(int m, int y);


</div>
<span class='text_page_counter'>(50)</span><div class='page_container' data-page=50>

Ví dụ về thành phần tónh : CDate


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];


return dayTab[LeapYear(y)][m];
}


bool betw(int x, int a, int b)
{


</div>
<span class='text_page_counter'>(51)</span><div class='page_container' data-page=51>

Ví dụ về thành phần tónh : CDate


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;
int d,m,y;


cin >> d >> m >> y;


while (!ValidDate(d,m,y))
{


cout << "Please enter a valid date: ";
cin >> d >> m >> y;



</div>
<span class='text_page_counter'>(52)</span><div class='page_container' data-page=52>

2.3 Thiết lập và huỷ bỏ đối tượng


2.3 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ỷ


• Thời gian từ khi đối tượng được tạo ra đến khi nó bị huỷ


đ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:


</div>
<span class='text_page_counter'>(53)</span><div class='page_container' data-page=53>

Thiết lập và huỷ bỏ đối tượng


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
free stored


 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


</div>
<span class='text_page_counter'>(54)</span><div class='page_container' data-page=54>

Đối tượng tự động


Đố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 hồn tất
khối chứa định nghĩa đó.



khối chứa định nghĩa đó.


 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


</div>
<span class='text_page_counter'>(55)</span><div class='page_container' data-page=55>

Đối tượng là biến địa phương


Đối tượng là biến địa phương


#include <iostream.h>


#include <string.h>


char *strdup(const char *s)
{


return strcpy(new char[strlen(s) + 1], s);
}


class string
class string
{


char *p;
public:


string(char *s = "") {p = strdup(s);}


</div>
<span class='text_page_counter'>(56)</span><div class='page_container' data-page=56>

void main()
{



string a("Nguyen Van A");


string b = a; // String b(a)
a.Output(); cout << "\n";


</div>
<span class='text_page_counter'>(57)</span><div class='page_container' data-page=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


</div>
<span class='text_page_counter'>(58)</span><div class='page_container' data-page=58>

Đối tượng là tham số truyền bằng giá trị


Đố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;


char *p;
public:


String(char *s = "") {p = strdup(s);}


~String() {cout << "delete "<< (void *)p <<
"\n"; delete [] p;}


</div>
<span class='text_page_counter'>(59)</span><div class='page_container' data-page=59>

Đối tượng là tham số truyền bằng giá trị


Đối tượng là tham số truyền bằng giá trị



bool String::Compare(String s) const
{


return strcmp(p,s.p);
}


void main()
{


String a("Nguyen Van A");
String b("Le Van Beo");
int c = a.Compare(b);


</div>
<span class='text_page_counter'>(60)</span><div class='page_container' data-page=60>

Đối tượng là tham số truyền bằng giá trị


Đố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


</div>
<span class='text_page_counter'>(61)</span><div class='page_container' data-page=61>

Đối tượng là giá trị trả về


Đố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;}


delete [] p;}


void Output() const {cout << p;}
bool Compare(String s) const;


</div>
<span class='text_page_counter'>(62)</span><div class='page_container' data-page=62>

Đối tượng là giá trị trả về


Đối tượng là giá trị trả về



String String::UpCase() const


{


String r = *this;
strupr(r.p);


return r;
}


void main()
{


{


clrscr();


String a("Nguyen Van A");


cout << "a = "; a.Output(); cout << "\n";
String A;


A = a.UpCase();


</div>
<span class='text_page_counter'>(63)</span><div class='page_container' data-page=63>

Đối tượng là giá trị trả về


Đố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
delete 0x0f36


Null pointer assignment


</div>
<span class='text_page_counter'>(64)</span><div class='page_container' data-page=64>

Đối tượng là giá trị trả về


Đố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:


</div>
<span class='text_page_counter'>(65)</span><div class='page_container' data-page=65>

Đối tượng là giá trị trả về


Đố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";
}


</div>
<span class='text_page_counter'>(66)</span><div class='page_container' data-page=66>

Đối tượng là giá trị trả về


Đố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
delete 0x0d32


</div>
<span class='text_page_counter'>(67)</span><div class='page_container' data-page=67>

Phương thức thiết lập sao chép


Phương thức thiết lập sao chép



 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 sao chép.


 Phương thức thiết lập sao chép là phương thức thiết lập


</div>
<span class='text_page_counter'>(68)</span><div class='page_container' data-page=68>

Phương thức thiết lập sao chép


Phương thức thiết lập sao chép



 Phương thức thiết lập sao chép 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 sao chép 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 sốt sử huỷ bỏ đối


</div>
<span class='text_page_counter'>(69)</span><div class='page_container' data-page=69>

Phương thức thiết lập sao chép


Phương thức thiết lập sao chép


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);}
strdup(s.p);}


~String() {cout << "delete "<< (void *)p
<< "\n"; delete [] p;}


void Output() const {cout << p;}
bool Compare(String s) const;


</div>
<span class='text_page_counter'>(70)</span><div class='page_container' data-page=70>

Phương thức thiết lập sao chép


Phương thức thiết lập sao chép



bool String::Compare(String s) const
{


return strcmp(p,s.p);
}


String String::UpCase() const
{


String r = *this;
String r = *this;
strupr(r.p);


</div>
<span class='text_page_counter'>(71)</span><div class='page_container' data-page=71>

Phương thức thiết lập sao chép


Phương thức thiết lập sao chép


void main()


{



clrscr();


String a("Nguyen Van A");
String b("Le Van Beo");
String aa = a;


int c = a.Compare(b);


cout << (c > 0 ? "a > b" : c == 0 ? "a =
b" : "a < b") << "\n";


cout << "a = "; a.Output(); cout << "\n";
String A = a.UpCase();


</div>
<span class='text_page_counter'>(72)</span><div class='page_container' data-page=72>

Phương thức thiết lập sao chép


Phương thức thiết lập sao chép



 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
A = NGUYEN VAN A
delete 0x0d96



delete 0x0d72
delete 0x0d62
delete 0x0d50


 Mỗi đối tượng đều có tài ngun riêng nên khơng xảy


</div>
<span class='text_page_counter'>(73)</span><div class='page_container' data-page=73>

Phương thức thiết lập sao chép


Phương thức thiết lập sao chép



 Tham số của phương thức thiết lập sao chép bắt buộc là


tham chieáu.


 Phương thức thiết lập sao chép 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 sốt.


Lưu ý:


Nếu đối tượng khơng có tài ngun riêng thì khơng cần


 Nếu đối tượng khơng có tài ngun riêng thì khơng cần


phương thức thiết lập sao chép.


 Khi truyền tham số là đối tượng thuộc lớp có phương


</div>
<span class='text_page_counter'>(74)</span><div class='page_container' data-page=74>

Sao chép nông và sao chép sâu



Sao chép nông và sao chép sâu



 Dùng phương thức thiết lập sao chép 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


</div>
<span class='text_page_counter'>(75)</span><div class='page_container' data-page=75>

Ví dụ về sao chép nông


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 <<
~StringRep() {cout << "delete ” << (void *)p <<


</div>
<span class='text_page_counter'>(76)</span><div class='page_container' data-page=76>

Ví dụ về sao chép nông



Ví dụ về sao chép noâng



class String
{


StringRep *rep;
public:


String(const char *s = "") {rep = new
StringRep(s);}


String(const String &s) {rep = s.rep;
rep->n++;}


rep->n++;}
~String();


void Output() const {cout << rep->p;}
bool Compare(String s) const;


String UpCase() const;
void ToUpper();


</div>
<span class='text_page_counter'>(77)</span><div class='page_container' data-page=77>

Ví dụ về sao chép nông


Ví dụ về sao chép noâng



String::~String()
{


if (--rep->n <= 0)


delete rep;


}


bool String::Compare(String s) const
{


return strcmp(rep->p,s.rep->p);
return strcmp(rep->p,s.rep->p);
}


String String::UpCase() const
{


</div>
<span class='text_page_counter'>(78)</span><div class='page_container' data-page=78>

Ví dụ về sao chép nông


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 << (c > 0 ? "a > b" : c == 0 ? "a = b" : "a
cout << (c > 0 ? "a > b" : c == 0 ? "a = b" : "a


< b") << "\n";


cout << "a = "; a.Output(); cout << "\n";
String A = a.UpCase();


</div>
<span class='text_page_counter'>(79)</span><div class='page_container' data-page=79>

Ví dụ về sao chép nông


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


</div>
<span class='text_page_counter'>(80)</span><div class='page_container' data-page=80>

Ví dụ về sao chép nông


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.


delete 0x0d84


a = Nguyen Van A
A = NGUYEN VAN A
delete 0x0d96


delete 0x0d72
delete 0x0d62
delete 0x0d50


A = NGUYEN VAN A
delete 0x0d8a


</div>
<span class='text_page_counter'>(81)</span><div class='page_container' data-page=81>

Đối tượng tĩnh


Đố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ừ khố static (tồ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
 Đối tượng tĩnh địa phương được tạo ra khi chương trình


</div>
<span class='text_page_counter'>(82)</span><div class='page_container' data-page=82>

Đối tượng tĩ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 tồ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


</div>
<span class='text_page_counter'>(83)</span><div class='page_container' data-page=83>

Ví dụ về đối tượng tồn cục


Ví dụ về đối tượng tồn cục



 Xét đoạn chương trình sau:
#include <iostream.h>
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:
Hãy sửa lại đoạn chương trình trên để có xuất liệu:


Entering a C++ program saying...
Hello, world.



And then exitting…


</div>
<span class='text_page_counter'>(84)</span><div class='page_container' data-page=84>

Ví dụ về đối tượng tồn cục


Ví dụ về đối tượng tồn cục



 Đoạn chương trình được sửa lại như sau:
#include <iostream.h>


void main()
{


cout << "Hello, world.\n";
}


class Dummy
class Dummy
{


public:


Dummy() {cout << "Entering a C++ program
saying...\n";}


~Dummy() {cout << "And then exitting...";}
};


</div>
<span class='text_page_counter'>(85)</span><div class='page_container' data-page=85>

Đối tượng là thành phần của lớp


Đố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ố


</div>
<span class='text_page_counter'>(86)</span><div class='page_container' data-page=86>

Đối tượng là thành phần của lớp


Đố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


</div>
<span class='text_page_counter'>(87)</span><div class='page_container' data-page=87>

Đối tượng là thành phần của lớp


Đố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
{


{


Diem A,B,C;
public:


void Ve() const;
// ...


};


</div>
<span class='text_page_counter'>(88)</span><div class='page_container' data-page=88>

Đối tượng là thành phần của lớp


Đố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;


//...
};


};


class SinhVien
{


String MaSo;
String HoTen;
int NamSinh;
public:


};


</div>
<span class='text_page_counter'>(89)</span><div class='page_container' data-page=89>

Đối tượng thành phần - thiết lập


Đố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;}
// ...


</div>
<span class='text_page_counter'>(90)</span><div class='page_container' data-page=90>

Khởi động đối tượng thành phần



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;


void Ve() const;
// ...


};


</div>
<span class='text_page_counter'>(91)</span><div class='page_container' data-page=91>

Đối tượng là thành phần của lớp


Đố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;
"\n"; delete [] p;
//...


</div>
<span class='text_page_counter'>(92)</span><div class='page_container' data-page=92>

Khởi động đối tượng thành phần


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;}


MaSo(ms){NamSinh = ns;}
//...


};


</div>
<span class='text_page_counter'>(93)</span><div class='page_container' data-page=93>

Khởi động đối tượng thành phần


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){}
// ...


};
};


class SinhVien
{


String MaSo, HoTen;
int NamSinh;


public:


</div>
<span class='text_page_counter'>(94)</span><div class='page_container' data-page=94>

Đối tượng thành phần - Huỷ bỏ


Đố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:


public:


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)
{


</div>
<span class='text_page_counter'>(95)</span><div class='page_container' data-page=95>

Đối tượng là thành phần của mảng


Đố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ố.


là có thể được thiết lập khơng cần tham số.


 Đố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.


</div>
<span class='text_page_counter'>(96)</span><div class='page_container' data-page=96>

Đối tượng là thành phần của mảng


Đố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
class String
{


char *p;
public:


String(char *s) {p = strdup(s);}


String(const String &s) {p = strdup(s.p);}
~String() {cout << "delete "<< (void *)p <<


</div>
<span class='text_page_counter'>(97)</span><div class='page_container' data-page=97>

Đối tượng là thành phần của mảng


Đố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){}


MaSo(ms), NamSinh(ns){}
//...



};


String as[3]; // Bao sai


Diem ad[5]; // Bao sai


</div>
<span class='text_page_counter'>(98)</span><div class='page_container' data-page=98>

Phương thức thiết lập với tham số có giá trị


mặc nhiên



Phương thức thiết lậ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
{



char *p;
public:


String(char *s = “”) {p = strdup(s);}


</div>
<span class='text_page_counter'>(99)</span><div class='page_container' data-page=99>

Dùng phương thức thiết lập với tham số


có giá trị mặc nhiên



Dùng phương thức thiết lậ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 =


“19920014”, int ns =


1982):HoTen(ht), MaSo(ms),
NamSinh(ns){}


//...
};



</div>
<span class='text_page_counter'>(100)</span><div class='page_container' data-page=100>

Dùng phương thức thiết lập không tham số


Dùng phương thức thiết lậ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
{


char *p;
public:


String(char *s) {p = strdup(s);}
String() {p = strdup(“”);}


</div>
<span class='text_page_counter'>(101)</span><div class='page_container' data-page=101>

Dùng phương thức thiết lập không


tham số



Dùng phương thức thiết lập không



tham số



class SinhVien
{


String MaSo;
String HoTen;
int NamSinh;
public:


SinhVien(char *ht, char *ms, int
SinhVien(char *ht, char *ms, int


ns):HoTen(ht), MaSo(ms),
NamSinh(ns){}


SinhVien():HoTen(“Nguyen Van A”),


MaSo(“19920014”), NamSinh(1982){}
//...


</div>
<span class='text_page_counter'>(102)</span><div class='page_container' data-page=102>

Đối tượng được cấp phát động


Đố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 để
 Dùng new có thể cấp một đối tượng và dùng delete để


huỷ một đối tượng.


 Dùng new và delete cũng có thể cấp nhiều đối tượng và


</div>
<span class='text_page_counter'>(103)</span><div class='page_container' data-page=103>

Đối tượng được cấp phá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;}


//...
};


};



class Diem
{


double x,y;
public:


</div>
<span class='text_page_counter'>(104)</span><div class='page_container' data-page=104>

Cấp và huỷ một đối tượng


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");
//...


</div>
<span class='text_page_counter'>(105)</span><div class='page_container' data-page=105>

Cấp và huỷ nhiều đối tượng


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:
 Thơng báo lỗi cho đoạn chương trình trên như sau:


Cannot find default constructor to initialize array element of type
'Diem'


Cannot find default constructor to initialize array element of type
String’


</div>
<span class='text_page_counter'>(106)</span><div class='page_container' data-page=106>

Cấp và huỷ nhiều đối tượ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;}


//...
};


class Diem
{


double x,y;


public:


Diem(double xx, double yy):x(xx),y(yy){};
Diem():x(0),y(0){};


</div>
<span class='text_page_counter'>(107)</span><div class='page_container' data-page=107>

Cấp và huỷ nhiều đối tượng


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


// 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


delete và có thêm dấu [] ở trước.


</div>
<span class='text_page_counter'>(108)</span><div class='page_container' data-page=108>

2.4 Giao diện và chi tiết cài đặt


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ã hố 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ể
có thể thay đổi tổ chức dữ liệu của lớp, cũng như có thể
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


</div>
<span class='text_page_counter'>(109)</span><div class='page_container' data-page=109>

Lớp ThoiDiem – Cách 1


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);}


</div>
<span class='text_page_counter'>(110)</span><div class='page_container' data-page=110>

Lớp ThoiDiem – Cách 2


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);
void Set(int g, int p, int gy);


int LayGio() const {return tsgiay / 3600;}


int LayPhut() const {return (tsgiay%3600)/60;}
int LayGiay() const {return tsgiay % 60;}


void Nhap();


</div>
<span class='text_page_counter'>(111)</span><div class='page_container' data-page=111>

Giao diện và chi tiết cài đặt


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


</div>
<span class='text_page_counter'>(112)</span><div class='page_container' data-page=112>

2.5 Các nguyên tắc xây dựng lớp


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
 Lớp là biểu diễn cụ thể của một khái niệm, vì vậy lớp


ln ln 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Ừ.


</div>
<span class='text_page_counter'>(113)</span><div class='page_container' data-page=113>

Các nguyên tắc xây dựng lớp


Các nguyên tắc xây dựng lớp



// SAI
class TamGiac
{
// DUNG
class TamGiac
{


 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 tốn. Chu
vi, diện tích tam giác là thuộc tính suy diễn.


{


Diem A,B,C;


</div>
<span class='text_page_counter'>(114)</span><div class='page_container' data-page=114>

Các nguyên tắc xây dựng lớp


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 ngun hoặc thời gian để thực hiện tính tố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;
double DienTich;


double TuoiTrungBinh;
//...


public:


double TinhTuoiTB() const;
//...


</div>
<span class='text_page_counter'>(115)</span><div class='page_container' data-page=115>

Các nguyên tắc xây dựng lớp


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ã hố 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



// NEN


class PhanSo
{


int tu, mau;
public:


// KHONG NEN
class PhanSo
{


</div>
<span class='text_page_counter'>(116)</span><div class='page_container' data-page=116>

Các nguyên tắc xây dựng lớp


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,
public:
//...
};
class HinhTron
{
Diem Tam;
double BanKinh;
public:


xB, yB, xC,
yC;
public:
//...
};
class HinhTron
{


</div>
<span class='text_page_counter'>(117)</span><div class='page_container' data-page=117>

Các nguyên tắc xây dựng lớp


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).


 Dữ liệu thành phần nên được kết hợp thay vì phân rã



// NEN // KHONG NEN


// NEN


class TamGiac
{


Diem A,B,C;
public:


//...


// KHONG NEN
class TamGiac
{


double xA, yA,


</div>
<span class='text_page_counter'>(118)</span><div class='page_container' data-page=118>

Các nguyên tắc xây dựng lớp


Các nguyên tắc xây dựng lớp



 Trong mọi trường hợp, cầ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 ngun thì phải


có phương thức thiết lập, phương thức thiết lập sao chép
để khởi động đối tượng bằng đối tượng cùng kiểu và có
để khởi động đối tượng bằng đối tượng cùng kiểu và có
phương thức huỷ bỏ để dọn dẹp. Ngồ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


</div>

<!--links-->

×