• Không có kết quả nào được tìm thấy

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

N/A
N/A
Protected

Academic year: 2022

Chia sẻ "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"

Copied!
118
0
0

Loading.... (view fulltext now)

Văn bản

(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

Ôn tập tốt nghiệp

(2)

Chương 1 Chương 1

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

(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

Rumbaugh: Mô hình hoá và thiết kế theo hướng đối tượng.

(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 hoá và thiết kế hướng đối tượng giúp hiểu rõ hơn yêu cầu của vấn đề, hướng đối tượng giúp hiểu rõ hơn yêu cầu của vấn đề, thiết kế trong sáng hơn, và kết quả là những hệ thống dễ dàng bảo trì hơn.

(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 hoá

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 cuối của qui trình phát triển hệ thống.

(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

chuyển, linh động và có được tự do quyết định trong giai đoạn phân tích và thiết kế.

(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 trưng là cấu trúc dữ liệu (các thuộc tính) và hoạt động (các thủ tục xử lý dữ liệu).

(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ô hình (OMT: Object Modeling Technique).

(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 hoá OMT bao gồm các bước:

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

Thiết kế

đối tượng Cài đặt

(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 hoá :

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

(polymorphism) mang lại hiệu quả cao hơn.

(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 ngoà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 toà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 một thực thể duy nhất giúp cho tính đóng gói rõ ràng hơn và hiệu quả hơn.

(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

cách kết hợp dữ liệu và phần thủ tục xử lý dữ liệu vào trong một thực thể duy nhất, hệ thống trở thành một sơ đồ phân cấp duy nhất các lớp đối tượng.

(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

Được thay bằng

(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ó 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 các đối tượng được phân biệt với nhau do sự tồn tại vốn có của chúng chứ không phải các tính chất mà chúng có.

(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 đổi có thể áp dụng vào hay áp dụng bởi các đối tượng trong lớp.

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

đối tượng trong lớp đó.

(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

Thao tác

(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ể hiện (instance) của lớp đó. Joe Smith, 25 tuổi, nặng

58kg, là một thể hiện của lớp người.

(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à class.

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

// Stack1.cpp :

//Dung cau truc va ham toan cuc

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

};

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

return (ps->top - ps->st >= ps->size);

}

(22)

Ví dụ so sánh (tt) Ví dụ so sá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;

return true;

}

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

return true;

}

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

StackCleanUp(&s);

}

(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 sự, dọn dẹp.

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

bool Pop(Item *px);

};

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

return true;

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

}

(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 sự, dọn dẹp.

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

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

Stack s, *ps = &s;

s.Init(10);

for (int i = 0; i < 20; i++) ps->Push(i);

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

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

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

bool Push(Item x);

bool Pop(Item *px);

(33)

Lớp 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.

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

Thuộc tính private

Thuộc tính public

Thuộc tính public

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

Ví dụ sau minh hoạ thuộc tính truy xuất.

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

bool Pop(Item *px);

};

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

if (Empty()) return false;

*px = *--top;

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

cout << hTab[x];

}

(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 tiến giải thuật các hàm thành phần.

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

}

(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 biệt nó với các hàm thành phần khác.

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

Chỉ có thể có tối đa một phương thức hb.

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

bool Pop(Item *px);

};

(44)

Hàm bạn (friends) 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 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 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.

(45)

Hà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ừ 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.

đổi các thành phần dữ liệu của đối tượng.

(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() const {cout << p;}

void ToLower() {strlwr(p);}

};

(47)

Hà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();

Truong.ToLower(); // Bao loi }

(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 toà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ó 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.

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

static bool ValidDate(int d, int m, int y);

void Input();

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

return x >= a && x <= b;

}

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

}

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

Khi nào đối tượng được tạo ra ? Khi nào đối tượng bị huỷ đi ?

(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

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

(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 hoà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

nguyên với đối tượng nguồn.

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

~string() {cout << "delete "<< (void *)p <<

"\n"; delete [] p;}

(56)

void main() {

string a("Nguyen Van A");

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

b.Output(); cout << "\n";

}

(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ỷ, A”). Khi kết thúc hàm main() hai đối tượng này bị huỷ, phần tài nguyên chung bị huỷ 2 lần (SAI).

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

void Output() const {cout << p;}

bool Compare(String s) const;

};

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

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

(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

delete 0x0f34 delete 0x0f22 delete 0x0f22

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

String UpCase() const;

};

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

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

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

}

(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

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

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

String A = a.UpCase();

String A = a.UpCase();

(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";

} }

(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

Null pointer assignment

(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 có tham số là đối tượng cùng kiểu, dùng để sao chép đối có tham số là đối tượng cùng kiểu, dùng để sao chép đối tượng.

(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 soát sử huỷ bỏ đối

tượng.

tượng.

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

String UpCase() const;

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

return r;

}

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

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

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

(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 chiế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 soát.

Lưu ý:

Nếu đối tượng không có tài nguyên riêng thì không cần Nếu đối tượng không có tài nguyên 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 thức thiết lập sao chép, ta nên truyền bằng tham chiếu và có thêm từ khoá const.

(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 ra khỏi các đối tượng dùng chung tài nguyên, nói cách ra khỏi các đối tượng dùng chung tài nguyên, nói cách khác, nó phải có tài nguyên riêng (như sao chép sâu).

(75)

Ví duï veà sao cheùp noâng Ví duï veà sao cheùp noâ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 <<

"\n";}

};

(76)

Ví duï veà sao cheùp noâng Ví duï veà sao cheù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();

};

(77)

Ví duï veà sao cheùp noâng Ví duï veà sao cheù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 {

String r(rep->p);

strupr(r.rep->p);

return r;

(78)

Ví duï veà sao cheùp noâng Ví duï veà sao cheùp noâ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();

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

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

}

(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

delete 0x0d72 delete 0x0d72 delete 0x0d58

(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

delete 0x0d72 delete 0x0d58

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

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.

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

(83)

Ví dụ về đối tượng toàn cục Ví dụ về đối tượng toà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…

Yêu cầu không thay đổi hàm main() dưới bất kỳ hình thức nào.

(84)

Ví dụ về đối tượng toàn cục Ví dụ về đối tượng toà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...";}

};

Dummy d;

(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ố khi thiết lập thì đối tượng kết hợp (đối tượng lớn) phải khi thiết lập thì đối tượng kết hợp (đối tượng lớn) phải 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.

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

phương thức huỷ bỏ của đối tượng kết hợp được gọi.

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

// ...

};

TamGiac t; // Bao sai

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

};

SinhVien s; // Bao sai

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

// ...

};

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

// ...

};

TamGiac t(100,100,200,400,300,300);

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

//...

};

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

//...

};

SinhVien s(“Nguyen Van Beo”, “19920005”, 1985);

//Ok

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

SinhVien(char *ht, char *ms, int ns):HoTen(ht),

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

memcpy(Diem = new double[SoMon], d, SoMon*sizeof(double));

}

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

Lớp có phương thức thiết lập không tham số.

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

"\n"; delete [] p;}

// ...

(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 SinhVien asv[7]; // Bao sai

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

String(const String &s) {p = strdup(s.p);}

~String() {cout << "delete "<< (void *)p <<

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

//...

};

String as[3]; // Ok: Ca ba phan tu deu la chuoi

(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(“”);}

~String() {cout << "delete "<< (void *)p <<

"\n"; delete [] p;}

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

//...

};

(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à huỷ nhiều đối tượng.

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

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

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

//...

delete pa;

delete pd;

delete pj;

delete pj;

delete pi;

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

Lỗi trên được khắc phục bằng cách cung cấp phương

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

//...

};

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

Tài liệu tham khảo

Tài liệu liên quan

- về dữ liêu: Một lớp dẫn xuất sẽ thừa kế tất cả các thành phần dữ liệu của lớp cơ sở. - về hàm thành phần: tất cả trừ hàm tạo, hàm hủy, hàm bạn