1. Trang chủ
  2. » Công Nghệ Thông Tin

Lập trình hướng đối tượng C++

153 374 1
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Lập trình hướng đối tượng C++
Trường học Trường Đại Học Công Nghệ Thông Tin - Đại Học Quốc Gia Hà Nội
Chuyên ngành Lập trình hướng đối tượng C++
Thể loại Giáo trình
Thành phố Hà Nội
Định dạng
Số trang 153
Dung lượng 1,14 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Lập trình hướng đối tượng C++

Trang 1

Lời nói đầu 4

Chương 1: Mở đầu 6

1.1 So sánh giữa lập trình hướng đối tượng và lập trình hướng thủ tục 6 1.1.1 Lập trình hướng thủ tục 6 1.1.2 Lập trình hướng đối tượng 7 1.2 Giới thiệu ngôn ngữ C++ 8

1.2.1 Chú thích trong C và trong C++ 8 1.2.2 Các từ khóa mới 8 1.2.3 Phép chuyển kiểu bắt buộc 8 1.2.4 Khai báo biến 8 1.2.5 Hàm main 9 1.2.6 Truyền tham số cho hàm 10 1.2.7 Nhắc lại về biến con trỏ 13 1.2.8 Sử dụng tham số truyền ngầm định trong hàm 15 1.2.9 Cấp phát và giải phóng bộ nhớ 16 1.2.10 Các hàm tải bội (Overloaded function) 17

1.2.11 Hàm inline 18

1.2.12 Khả năng vào ra mới của C++ 21

1.3 Bài tập chương 1 23

Chương 2: Lớp các đối tượng 27

2.1 Lớp các đối tượng 27

2.1.1 Các khái niệm cơ bản 27 2.1.2 Khai báo lớp các đối tượng 27 2.1.3 Các thành phần trong lớp (Data member and function member) 31 2.1.4 Đối tượng trong tham số của hàm 42 2.2 Constructor và destructor 44

2.2.1 Khái niệm chung 44 2.2.2 Khai báo và định nghĩa constructor và destructor 44 2.2.3 Con trỏ this 46 2.3 Toán tử tải bội và toán tử tải bội thân thiện 48

Trang 2

2.3.1 Khái quát chung 48 2.3.2 Toán tử tải bội 48 2.3.3 Toán tử tải bội thân thiện 52 2.3.4 Chuyển đổi kiểu 59

2.4 Vài vấn đề về sử dụng từ khoá const 66

2.4.1 Sử dụng biến kiểu const 66 2.4.2 Truyền tham số kiểu const cho hàm 67 2.4.3 Đối tượng hằng của lớp 68 2.4.4.Hàm thành phần const (hàm thành phần có khai báo const phía sau) 70 2.4.5.Con trỏ this kiểu const (hàm thành phần có khai báo const phía trước)

3.1 Sự tương ứng trong kế thừa 84

3.1.1 Khái niệm chung 84 3.1.2 Kế thừa đơn 86 3.1.3 Thành phần protected trong kế thừa 88 3.1.4 Kế thừa đa mức 92 3.1.5 Kế thừa phân cấp 93 3.1.6 Kế thừa bội 94 3.1.7 Kế thừa kép (lai ghép) 95 3.1.8 Constructor trong các lớp kế thừa 99

3.2 Con trỏ xác định thành phần của lớp và con trỏ xác định đối tượng 102

3.2.1 Các quy tắc cú pháp về con trỏ 102 3.2.2.Các ví dụ minh hoạ cách sử dụng con trỏ xác định đối tượng và thành phần (dữ liệu và hàm) của lớp 103 3.2.3 Con trỏ xác định đối tượng 105

3.3 Hàm ảo và tương ứng bội trong kế thừa 106

3.3.1 Các hàm dịch chuyển (Override function) 106 3.3.2 Con trỏ xác định đối tượng trong quan hệ kế thừa 107

Trang 3

3.3.3 Con trỏ hàm ảo và tương ứng bội 108 3.3.4 Kế thừa lai ghép và lớp cơ sở ảo 111 3.3.5 Hàm rỗng (Null Function) 114 3.3.6 Hàm ảo thực sự và lớp trừu tượng (pure function & abstract class) 115 3.3.7 Hàm thân thiện ảo và toán tử ảo dịch chuyển 116

3.4 Bài tập chương 3 116

Chương 4: Làm việc với Tệp 120

4.1 Các Thao tác cơ bản với tệp 120

4.1.1 Đọc dữ liệu từ tệp 120 4.1.2 Ghi dữ liệu vào tệp 122

4.2 Một số vấn đề khác làm việc với tệp 125

4.2.1 Mở tệp bằng open 125 4.2.2 Các chế độ làm việc với tệp 125 4.2.3 Đọc/ ghi tệp nhị phân 126

4.3 Bài tập chương 4 128

Phụ lục 1: Lớp mẫu và hàm mẫu 130

Khái quát 130 4.3.2 Hàm mẫu 130 4.3.3 Lớp mẫu 132

Phụ lục 2: Giới thiệu Một số bài toán phân tích thiết kế hướng đối tượng

trên C++ 137

4.3.1 Sơ lược 5 bước thực hiện 137 4.3.2 Xây dựng lớp hình học phẳng 137 4.3.3 Quản lý các lô đất 146

Tài liệu tham khảo 153

Trang 4

LỜI NÓI ĐẦU

Lập trình trên ngôn ngữ C là một một ví dụ về phương pháp tiếp cận lập trình hướng chức năng Phương pháp này tập trung vào thuật toán chính để giải quyết bài toán đặt ra và có thể phải phân rã bài toán xuất phát này thành các bài toán con nhỏ hơn Việc tìm thuật toán cho từng bài toán con thậm chí lại phải tiếp tục chia thành các bài toán con nhỏ hơn nữa và quá trình đó là quá trình làm mịn dần, có nghĩa là việc phân chia thành các bài toán con sẽ dừng lại cho đến khi thu được tập các bài toán con đơn giản nhất, có thể tìm ngay ra lời giải Khi xây dựng các phần mềm lớn, phương pháp lập trình hướng chức năng đã gặp khó khăn trong việc cập nhật và sửa đổi, có nghĩa là việc thay đổi một phần chức năng và dữ liệu trong chương trình có thể làm ảnh hưởng đến toàn bộ hệ thống và mất nhiều công sức để điều chỉnh

Cần phải có một hướng tiếp cận khác trong lập trình để hạn chế được những nhược điểm của lập trình truyền thống: trước tiên phải chú trọng đến những đối tượng xuất hiện trong bài toán, tìm cách đặc tả đối tượng (gồm thông tin và hành vi) sao cho phù hợp với thế giới thực Có nghĩa là thay vì quan tâm đến chức năng, nhiệm vụ đã đặt ra, kỹ thuật lập trình mới sẽ đặt trọng tâm vào việc xử lý các dữ liệu

để thực hiện các chức năng đó Kỹ thuật lập trình mới này mang tên là kỹ thuật lập trình hướng đối tượng (Object Oriented Programing) và trên kỹ thuật này nhiều trình biên dịch đã được thiết kế, trong đó có C++

Việc sử dụng C++ bao gồm những thuận lợi và những khó khăn Lớp các đối

tượng của C++ cho phép cài đặt riêng rẽ giao diện (interface) và thể hiện

(implementation) và có thể ẩn việc thể hiện chi tiết ở bên trong Rõ ràng C++ hỗ trợ khái niệm trừu tượng cũng giống như là một số ngôn ngữ khác hỗ trợ nó đặc biệt như

là Pascal, C Sự thuận lợi của C++ là C++ có thể sử dụng rộng rãi trong nhiều ngành nghề, học sinh có thể nắm bắt được các kỹ năng lý thuyết cũng như thực hành và điều này giúp họ có thể tìm kiếm việc làm dễ dàng Nhưng sự khó khăn của C++ là nó khác xa ngôn ngữ sư phạm và vì vậy cần phải chú ý về mặt thực hành để

có thể đạt được kỹ năng lập trình nhất định Điều không thuận lợi thứ hai là C++ vẫn

không phải là ngôn ngữ cố định vì vậy có các chương trình dịch khác nhau C++ mà chúng ta sẽ sử dụng đi cùng với tài liệu này là Turbo C++ version 3.0 của Borland (1990 - 1992)

Lời cảm ơn

Tác giả xin bầy tỏ lòng biết ơn chân thành tới: Phó giáo sư, tiến sỹ Nguyễn Xuân Huy (Viện Công nghệ thông tin); Giáo sư Phạm Văn Ất (Đại học giao thông); Tiến sỹ Dương Tử Cường (Học viện kỹ thuật quân sự); Tiến sỹ Phó Đức Toàn (Đại học sư phạm Hà Nội I); Phó giáo sư, tiến sỹ Đoàn Văn Ban (Viện Công nghệ thông tin) đã có những ý kiến đóng góp quý báu để cuốn giáo trình được hoàn chỉnh và có chất lượng hơn

Trang 5

Tác giả cũng xin chân thành cảm ơn sự tổ chức, hợp tác và giúp đỡ nhiệt tình của Ban giám hiệu, phòng Đào tạo và tổ chuyên môn trường THKT Tin học Hà Nội

- ESTIH

Trang 6

Vậy trọng tâm của lập trình hướng thủ tục là phân rã bài toán thành các hàm chức năng theo kỹ thuật top-down Bài toán được giải quyết bởi một số hàm

chính Các hàm chính lại có thể phân chia thành các bài toán con - tức là các hàm chức năng nhỏ hơn nữa Một số hàm có thể gọi thực hiện (dùng chung) một số hàm chức năng nhỏ hơn Như vậy, cấu trúc các hàm của một chương trình lập trình hướng thủ tục là cấu trúc phân cấp các hàm:

Việc chú ý tới các hàm trong chương trình sẽ không quan tâm nhiều đến dữ liệu Dữ liệu của toàn bộ chương trình được các hàm dùng chung và biến đổi

từ hàm này sang hàm khác Bản thân trong các hàm cũng có dữ liệu riêng

• Vậy đặc trưng của lập trình hướng thủ tục là

1 Tập trung vào công việc cần thực hiện - thuật toán

2 Chương trình được chia thành các hàm nhỏ

Trang 7

5 Hàm biến đổi dữ liệu từ dạng này sang dạng khác

6 Thiết kế chương trình theo kỹ thuật top-down

Lập trình hướng đối tượng

• Lập trình hướng đối tượng đặt trọng tâm vào đối tượng và không cho dữ liệu chuyển động trong hệ thống Dữ liệu gắn chặt vào các hàm và thủ tục tạo thành một vùng riêng và không cho các hàm bên ngoài truy nhập một cách tuỳ tiện

• Lập trình hướng đối tượng phân tích bài toán thành các thực thể gọi là đối tượng, sau đó xây dựng các hàm xung quanh đối tượng đó

• Dữ liệu của đối tượng chỉ có thể truy nhập được bởi các hàm của đối tượng Chỉ có một số hàm của đối tượng này có thể gọi thực hiện các hàm của đối tượng khác biểu thị sự trao đổi thông báo giữa các đối tượng

• Như vậy đặc trưng của lập trình hướng đối tượng là:

1 Tập trung vào dữ liệu thay cho hàm

2 Chia chương trình thành các đối tượng

3 Dữ liệu được thiết kế sao cho đặc tả được đối tượng

4 Các hàm được xây dựng trên các vùng dữ liệu của đối tượng và được gắn liền với nhau trên cấu trúc dữ liệu đó

5 Dữ liệu được bao bọc, che dấu không cho các hàm ngoại lai truy nhập

6 Các đối tượng trao đổi thông báo với nhau thông qua các hàm

7 Dữ liệu và các hàm dễ dàng bổ sung vào đối tượng

8 Chương trình được thiết kế theo kỹ thuật botom up

Đối tượng A

Dữ liệu Các hàm

Đối tượng B

Dữ liệu Các hàm

Đối tượng C

Dữ liệu Các hàm

Trang 8

GIỚI THIỆU NGÔN NGỮ C++

Trong phần này: với mục đích so sánh ngôn ngữ C và C++ sẽ làm rõ một số

đặc điểm mạnh của C++ so với C, đồng thời nhấn mạnh thêm hoặc giải thích chi tiết một số vấn đề quan trọng, thường gặp khi lập trình với C++

Chú thích trong C và trong C++

Trước hết C++ được phát triển từ C nên nó vẫn sử dụng lại tất cả các cú pháp ngôn ngữ như C và bổ sung thêm một số cú pháp mới

C++ đưa ra một kiểu chú thích mới để tiện chú thích trên một dòng nếu dùng

ký pháp // và cũng tiện để viết trên nhiều dòng liên tục nếu dùng cặp ký pháp /* */

/* dong 1 chu thich cua C */

/* dong 2 chu thich cua C */

x=y=0; /*chu thich sau lenh */

/* Bat dau chu thich Dong 2 chu thich Ket thuc chu thich */

x=y=10; // chu thich sau lenh // co the chu thich tren 1 dong

Các từ khóa mới

C++ bổ sung vào C một số từ khóa mới

asm friend new protected this

catch operator inline virtual class

public private cdecl pascal delete templete

Phép chuyển kiểu bắt buộc

Ngoài phép chuyển kiểu như trong ngôn ngữ C, C++ còn đưa ra phép chuyển kiểu mới Xét ví dụ sau đây:

int x=0;

long z=(long)x; // chuyen kieu cua C

long z=long(x); // chuyen kieu cua C++

Như vậy phép chuyển kiểu trong C++ có dạng như một hàm số chuyển kiểu đang được gọi Cách chuyển kiểu này rõ ràng vừa khoa học vừa dễ dọc

Khai báo biến

Nhìn chung phạm vi hoạt động của biến phụ thuộc vào kiểu của biến và vị trí khai báo của biến trong chương trình

Trang 9

Ngôn ngữ C đòi hỏi phải khai báo biến trước phạm vi mà các biến đang sử dụng Cách khai báo đơn giản nhất trong C là khai báo biến toàn cục lên trước tất cả các hàm và khai báo biến cục bộ ở đầu thân từng hàm

Trong C++, khác với C, cho phép một cách khai báo biến mới, với cách khai báo này, một biến có thể khai báo ở vị trí bất kỳ miễn sao là trước khi sử dụng nó, phạm vi hoạt động của biến trong trường hợp này là trong khối nơi biến đó được khai báo

{ int n; // khai bao cuc bo

for(int i=0;i<10;i++) // cho phep khai bao i trong for

Hàm main trong C không được định nghĩa kiểu

Hàm main trong C++ được định nghĩa kiểu để trả lại một giá trị nguyên cho hệ điều hành DOS, do đó phải khai báo là kiểu int, ngầm định-vì vậy sẽ là int Tuy nhiên ta vẫn có thể định nghĩa kiểu void cho hàm main

Khi chương trình bắt đầu thực hiện, hàm main được gọi bởi một chương trình khởi động đặc biệt (start up code) có sẵn trong C++

Start up code trong C++ có bốn cách gọi (tùy chọn vào Option\ Application): Dos standard (tệp C0.ASM)

Dos overlays (tệp C0.ASM)

Windows Application (tệp C0w.ASM)

Windows DLL (tệp C0d.ASM)

Giá trị trả lại của hàm main sẽ được Dos sử dụng để kiểm tra lỗi khi thực hiện chương trình Giá trị này bằng 0 nếu không có lỗi Các giá trị khác 0 được C++ biện luận theo các lỗi khi thực hiện chương trình

Trang 10

Truyền tham số cho hàm

Trước hết phải thống nhất về thuật ngữ: Lúc khai báo và định nghĩa hàm thì

phần khai báo trong ngoặc đơn (nếu có) sau tên hàm gọi là tham số hình thức của

hàm Khi một hàm (có tham số hình thức) được gọi ra sử dụng thì chương trình gọi

hàm phải truyền vào hàm các tham số thực sự để thay thế các tham số hình thức

Thuật ngữ "truyền tham số cho hàm" là có ý nói tới sự liên quan giữa hai việc: việc khai báo, định nghĩa hàm với tham số hình thức và việc gọi hàm với tham số thực sự

Tuy nhiên, để cho ngắn gọn: tham số hình thức thường được gọi là đối và tham

số thực sự nếu không nhầm lẫn với tham số hình thức vẫn thường được gọi tắt là

tham số

C++ vẫn sử dụng hai cách truyền tham số cho hàm trong C Đó là hai cách truyền tham số tương ứng với hai trường hợp:

Đối của hàm là biến thông thường, gọi là truyền tham số kiểu tham trị

Đối của hàm là biến con trỏ, gọi là truyền tham số kiểu con trỏ

Tuy nhiên, C++ có thêm một cách mới để truyền tham số cho hàm tương ứng với trường hợp đối của hàm là địa chỉ của biến Cách truyền tham số như vậy gọi là

truyền tham số kiểu tham chiếu Cách mới này rất hay sử dụng vì thích hợp truyền

tham số cho hàm là địa chỉ của đối tượng - một kiểu dữ liệu mới chỉ có trong C++ (kiểu lớp)

Dưới đây ta nhắc lại hai cách truyền thống và cung cấp cách mới thứ ba

Truyền tham số kiểu tham trị

Khi đối của hàm là biến đơn thuần thì khi gọi hàm, tham số được truyền vào hàm có thể là giá trị cụ thể (ví dụ 1.3) hoặc là một biến xác định (ví dụ 1.4) được khai báo trong chương trình gọi hàm

Về bản chất, theo cách truyền tham số vào hàm là biến thì chỉ có bản sao giá trị của biến được hàm sử dụng Trong thân hàm, các bản sao này có thể bị thay đổi, từ

đó suy ra, khi ra khỏi hàm thì giá trị ban đầu của biến truyền vào hàm vẫn giữ nguyên Chính vì đặc điểm trên nên ta gọi là "truyền tham số kiểu tham trị"

float S(float r) {return Pi*r*r;}

Trong ví dụ 1.3 ta thấy rằng S là một hàm có đối là biến bình thường kiểu float Khi hàm main gọi S thì tham số có giá trị cụ thể bằng 50.0 được truyền vào hàm

Trang 11

void swap(int x,int y)

{int z=x; x=y; y=z;}

Ví dụ 1.4 đã minh hoạ rằng: khi hàm swap có hai đối là các biến int thông thường thì khi gọi hàm: tham số truyền vào hàm (x=10, y=5) vẫn giữ nguyên giá trị khi ra khỏi hàm, mặc dù, trong hàm, giá trị của x và y đã bị thay đổi (x=5, y=10)

Truyền tham số kiểu con trỏ

Khi khai báo và định nghĩa hàm có đối là biến con trỏ thì lúc gọi hàm, các con trỏ này sẽ thay thế bằng địa chỉ của các biến (ví dụ 1.5) để truyền vào hàm hoặc là một con trỏ hoàn toàn xác định địa chỉ mà nó trỏ vào (chẳng hạn đang chứa một địa chỉ của một biến xác định như ví dụ 1.6)

Về bản chất, theo cách truyền tham số kiểu con trỏ thì hàm được truyền vào bản

sao địa chỉ của biến Như vậy, mọi sự thay đổi về giá trị của biến (cũng vẫn là địa chỉ đó) trong thân hàm sẽ được giữ lại khi hàm kết thúc

Trang 12

void swap(int *x,int *y){int z=*x; *x=*y; *y=z;}

Chú ý: Việc sử dụng hàm để truyền tham số kiểu con trỏ phải ghi nhớ các cú

pháp sau đây:

Khi khai báo: void swap(int*,int*)

Khi định nghĩa:

void swap(int *x,int *y)

{ // trong thân hàm, viết : *x và *y; }

Khi sử dụng:

swap(&x,&y); nếu x và y là hai biến swap(px,py); nếu px và py là hai biến con trỏ

Truyền tham số kiểu tham chiếu cho hàm

Về mặt tác dụng, việc truyền tham số cho hàm kiểu tham chiếu cũng giống như

việc truyền tham số cho hàm kiểu con trỏ Có nghĩa là giá trị của tham số bị hàm thay đổi cũng sẽ được giữ lại khi ra khỏi hàm

Bản chất của cách truyền tham số kiểu tham chiếu khác bản chất của hai cách truyền tham số truyền thống: kiểu tham trị và kiểu con trỏ Theo cách truyền tham số truyền thống thì chỉ có bản sao giá trị (hoặc giá trị của biến hoặc giá trị là địa chỉ biến) truyền vào hàm, còn trong cách truyền tham số kiểu tham chiếu thì hàm làm việc trực tiếp trên biến truyền vào hàm, thông qua "bí danh" của nó

void swap(int &x,int &y)

{int z=x; x=y; y=z;}

Kết quả

x = 5 y=10

Trang 13

So sánh ví dụ 1.7 với các ví dụ 1.6 và 1.5 ta thấy rằng: Cùng một tác dụng như nhau nhưng rõ ràng việc sử dụng tham chiếu đơn giản hơn so với con trỏ Xét về cú pháp, khi truyền tham số cho hàm theo kiểu tham chiếu thì ta chỉ cần báo hiệu địa chỉ của biến ở phần khai báo, còn ở trong thân hàm và trong lời gọi hàm, ta vẫn viết tên biến một cách bình thường

Hai cách truyền tham số kiểu con trỏ và truyền tham số kiểu tham chiếu, mặc

dù cùng nhận vào địa chỉ biến nhưng có những khác biệt sau đây cần được lưu ý:

Không được cấp phát bộ nhớ động cho biến tham chiếu Trong khi đó, biến con trỏ thì được phép xin cấp phát bộ nhớ động

Không dùng các phép toán tăng giảm địa chỉ cho biến tham chiếu Trong khi

đó biến con trỏ cho phép các phép toán này Việc tăng giảm địa chỉ của biến con trỏ

là làm thay đổi địa chỉ bộ nhớ (biến) mà con trỏ đang trỏ vào Ngược lại biến tham chiếu gắn liền với địa chỉ tĩnh trong bộ nhớ và do đó không được phép thay đổi địa chỉ này

Nhắc lại về biến con trỏ

So sánh con trỏ và tham chiếu địa chỉ của biến

Chẳng hạn nếu có khai báo:

int *p; và gán *p=10; thì đương nhiên có thể viết (*p)++; nhưng thậm chí có thể viết p++; Trong khi đó ta không thể có lệnh tăng địa chỉ (&x)++ nếu có khai báo int x;

C++ cho phép viết như vậy và hai cách viết có tác dụng khác nhau: Lệnh (*p)++ là tăng 1 đơn vị cho biến nguyên xác định bởi con trỏ p Lệnh p++ là tăng địa chỉ của con trỏ p, tức là con trỏ p sẽ trỏ vào 2 byte nhớ tiếp theo (2 bytes ứng với kiểu int) Vậy lệnh p++ chỉ sử dụng khi dùng p để trỏ vào không phải là 1 số nguyên

mà là một dãy các số nguyên chứa trong một dãy các biến động (các bytes nhớ) liên tiếp nhau

Biến con trỏ có 2 cách sử dụng trong chương trình:

Sử dụng con trỏ để chứa địa chỉ của biến

Trang 14

getch();

}

Sử dụng con trỏ để chứa địa chỉ của biến

Nếu ta có khai báo

Trang 15

delete [] p;

getch();

}

Ví dụ 1.10 là một cách đơn giản tạo ra một "danh sách liên kết" các biến động

mà biến động đầu danh sách được trỏ bởi con trỏ p Khi duyệt trên danh sách này luôn sử dụng thêm một biến con trỏ phụ (trong ví dụ 1.10 nêu trên là biến con trỏ q)

để chạy trên các biến động, con trỏ p phải được giữ nguyên có vai trò lưu giữ địa chỉ của danh sách Ta gọi là danh sách liên kết để tạo ra một cảm giác các biến động có liên kết với nhau Về mặt bản chất đó là các ô nhớ liên tục được cấp phát động, chứ không có liên kết gì cả Lúc chạy chương trình, phép tăng địa chỉ q++ làm cho q chỉ vào 2 bytes tiếp theo nghĩa là truy xuất đến biến động kiểu int tiếp theo tương ứng

Sử dụng tham số truyền ngầm định trong hàm

C++ còn mạnh hơn C ở chỗ nó cho phép khởi tạo mặc định cho các tham số truyền cho hàm Ta gọi tắt các tham số có khởi tạo giá trị mặc định là các tham số ngầm định

Khi có tham số ngầm định truyền cho hàm, có hai trường hợp sau xảy ra lúc gọi hàm:

Nếu lời gọi hàm khuyết các tham số ngầm định thì các giá trị ngầm định được sử dụng

Nếu lời gọi hàm có mặt các tham số ngầm định thì các giá trị mới sẽ được sử dụng thay vì sử dụng các giá trị mặc định

Khi dùng các tham số ngầm định, có hai nguyên tắc sau đây:

Các tham số ngầm định phải nằm ở cuối danh sách tham số

Khi gọi hàm khuyết các tham số ngầm định thì phải khuyết từ phải sang trái

và các tham số được gọi cần liên tục không được ngắt quãng

Ví dụ 1.12

Khai báo sau đây là sai:

void g(int a = 1, int b, int c = 3, int d = 4); // sai

vì các tham số ngầm định phải khai báo cuối danh sách tham số

Khai báo sau đây là đúng:

void g(int a, int b=2, int c = 3, int d = 4); // đúng

Trang 16

Với khai báo đúng trên, xét các lời gọi hàm sau đây:

g(); // sai vì a không có giá trị mặc định

/* Cấp phát bộ nhớ cho con trỏ s kiểu char */

* để có thể chứa được dãy ký tự trong */

Mặc dù new và malloc đều dùng để chỉ đến địa chỉ đầu của vùng nhớ xin được cấp phát nhưng giữa chúng vẫn có điểm khác biệt

Ví dụ 1.13

#include <alloc.h>

// alloc.h cần thiết cho sử dụng malloc

void main()

{ int* ip = new int[0];

//trả lại giá trị không xác định của con trỏ

int* ip = (int*) malloc (0);

// trả lại giá trị NULL pointer

}

Qua ví dụ trên ta thấy toán tử new trả lại giá trị khác 0 của con trỏ thậm chí ta không đòi hỏi phải cấp phát bất cứ một bytes nào trong bộ nhớ Giá trị này của con trỏ nếu được sử dụng cho mục đích khác sẽ sinh lỗi trong chương trình Ngược lại

Trang 17

hàm malloc trong trường hợp này trả lại giá trị NULL, điều này có nghĩa là con trỏ chưa trỏ đến bất cứ vị trí nào trong bộ nhớ

Cũng cần lưu ý là trong C++, hàm malloc có kiểu trả lại là void* do đó khi sử dụng phải thực hiện việc chuyển đổi kiểu tương ứng với kiểu của đối tượng xin cấp phát bộ nhớ

Ngoài hai khác biệt trên về bản chất cả new và malloc đều dùng để cấp phát bộ nhớ động cho các biến trong quá trình thực hiện chương trình và con trỏ trả lại theo hai cách này khi thành công đều không có gì khác biệt

Trong lập trình C++, toán tử new được ưa dùng vì nó mềm dẻo do khả năng định nghĩa chồng các toán tử của lớp

Các hàm tải bội (Overloaded function)

Danh động từ overloading trong tiếng Anh dịch là "Việc làm quá tải" Cụm từ

overloaded function trong trường hợp này dịch là hàm định nghĩa chồng hoặc hàm tải bội

C++ đưa ra một khả năng hoàn toàn mới và rất mạnh so với C đó là khả năng định nghĩa chồng các hàm, nghĩa là cho phép định nghĩa các hàm thực hiện các chức

năng khác nhau nhưng có cùng một tên Một hàm được định nghĩa chồng còn gọi ngắn gọn là hàm tải bội

Ngoài hàm tải bội, C++ còn cho phép toán tử tải bội để bình thường hóa các toán tử quen thuộc đối với một kiểu dữ liệu mới do người lập trình định nghĩa

Khi các hàm được tải bội thì phải tuân theo hai nguyên tắc sau đây:

Các hàm phải khác nhau về số lượng tham số hoặc kiểu dữ liệu của các tham

Tải bội hàm abs (là hàm có sẵn) để tính giá trị tuyệt đối của một số bất kỳ:

int abs(int i);

long abs(long l);

double abs(double d);

Khi đó, xét lời gọi các hàm sau đây:

abs(-10); // gọi hàm int abs(int i);

abs(-100000); // gọi hàm long abs(long l);

abs(-34.12); // gọi hàm double abs(double d);

abs('a'); // gọi hàm int abs(int i);

Ví dụ 1.15

Tải bội hàm display (là hàm tự định nghĩa) để hiển thị một giá trị có kiểu đơn giản bất kỳ:

Trang 18

void Display(char *string) {puts(string);}

void Display(long value) {printf("%ld",value);}

void Display(double value) {printf("%lf",value);}

Khi thực hiện chương trình, có lỗi tại hàm Display(123) với thông báo:

"Ambiguity between 'Display(double)' and 'Display(long)' " Có nghĩa là tham số có

giá trị 123 có thể hiểu là thuộc tập giá trị của long hoặc của double, do đó có sự nhập nhằng trong việc chọn hàm nào trong hai hàm Display(long) và Display(double) trong lời gọi hàm Display(123) Để tránh lỗi, ta sẽ sẽ ép kiểu 123 sang long hoặc double như sau: Display(long(123)); hoặc Display(double(123))

Hàm inline

Nhắc lại macro trong ngôn ngữ C

Macro (dịch theo nghĩa thông thường là vĩ mô) hiểu theo nghĩa tin học là một lệnh riêng lẻ viết bằng một ngôn ngữ lập trình nhưng kết quả là một chuỗi lệnh bằng ngôn ngữ máy tính

Trong ngôn ngữ C cho phép tạo ra các macro để đúng như ý nghĩa của nó: cho phép tạo ra một hằng hoặc thậm chí tạo ra một hàm và nói chung tạo ra một tên thay thế (tên macro) để ghép macro này như một đoạn trình mã máy vào chương trình nguồn lúc thực hiện

Trước hết macro được định nghĩa bằng chỉ thị #define (và được hủy bỏ bằng

#undef) Các chỉ thị #define (phép thay thế lệnh), #include (phép chèn tệp), #if (phép lựa chọn các dòng lệnh để biên dịch): đều là các chỉ thị tiền xử lý, không phải là các câu lệnh thông thường của chương trình Khi một chương trình C được biên dịch, trước hết các chỉ thị tiền xử lý sẽ chỉnh lý văn bản chương trình nguồn, sau đó bản chỉnh lý này mới được dịch Các chỉ thị tiền xử lý, do đó làm cho chương trình ngắn gọn hơn, giúp cho việc tổ chức biên dịch, gỡ rối chương trình hiệu quả hơn

Có 2 loại macro: macro thay thế đơn giản và macro thay thế theo đối (như các hàm)

Macro đơn giản

#define tên_macro biểu_thức

Có tác dụng thay thế tên_macro bằng biểu_thức đứng sau nó Khi biểu thức ký

tự dài có thể thêm một dấu \ trước khi xuống dòng Trước khi dùng lại tên_macro cho một biểu_thức khác thì phải huỷ bỏ nó bằng chỉ thị

#undef tên_macro

Ví dụ 1.16

Trang 19

(2) Khi định nghĩa một macro như một đoạn chương trình thì phải viết đoạn

chương trình trong khối lệnh { }

Đối của macro có thể không cần khai báo kiểu cụ thể

Tên hàm (macro) phải viết liền với dấu mở ngoặc bắt đầu khai báo đối Tất cả các đối hình thức trong thân hàm (macro) phải viết trong ngoặc đơn

Trang 20

Khi biên dịch, chương trình sẽ biên dịch sẽ thay

tich(x+y,z) bằng x+y*z như vậy sẽ không nhận được tích (x+y)*z như mong muốn Vậy phải khai báo lại là: #define tich(a,b) (a)*(b)

Hàm inline

Trong ngôn ngữ C++, hàm inline có cách hoạt động giống như một macro trong ngôn ngữ C Nghĩa là các hàm inline sẽ được thay thế trực tiếp thành dãy lệnh

mã máy tại chỗ gọi nó trong chương trình lúc thực hiện

Ưu điểm của macro và inline là ở chỗ nó cho phép trình bầy chương trình ngắn

gọn và quan trọng là cho phép thực hiện nhanh hơn các hàm thông thường Bởi vì

mỗi lần gọi hàm inline (hoặc macro), trình biên dịch sẽ ghép trực tiếp câu lệnh mã máy của nó tại vị trí gọi nó trong chương trình (mã máy) lúc thực hiện và không đòi hỏi các thủ tục bổ sung khi gọi hàm và trả giá trị về, nói cách khác không có cơ chế quản lý lời gọi và trả về (không cần lưu ngữ cảnh) như đối với các hàm thông thường

Nhược điểm của inline (và macro) là khi chúng quá lớn và gọi thường xuyên thì kích thước chương trình sẽ tăng lên rất nhanh Vì mỗi lần gọi inline (hoặc macro) thì các chỉ thị tương ứng sẽ được sinh ra (không có cơ chế lưu ngữ cảnh để giải phóng

bộ nhớ) do đó chí phí lưu trữ tăng lên khi gọi hàm nhiều lần Vậy inline và macro tiết kiệm thời gian nhưng không tiết kiệm bộ nhớ, cho nên thân các hàm inline không nên chứa các cấu trúc lặp

Việc sử dụng inline trong C++ tốt hơn macro trong C ở chỗ hàm inline không cần phải viết các tham số trong dấu ngoặc như đối với các hàm mà macro mô tả

Ví dụ nếu định nghĩa một macro:

#define square(x) { x++*x++;}

thì lời gọi square (a) sẽ sinh ra biểu thức a++*a++ làm thay đổi giá trị của a tới hai lần và hơn nữa lời gọi square(3) sẽ không được chấp nhận vì không được phép tăng giảm đối với hằng số

Hàm inline được định nghĩa và sử dụng như một hàm bình thường, điểm khác nhau duy nhất là phải đặt mô tả inline trước khai báo hàm

Cuối cùng, cần nhớ rằng giống như macro, đặc tả inline là một yêu cầu chứ không phải là một chỉ thị Nếu vì một lý do nào đó mà trình biên dịch không đáp ứng được yêu cầu của inline (chẳng hạn như bên trong inline có cấu trúc lặp) thì yêu cầu của inline bị bỏ qua và nó được biên dịch như một hàm bình thường (đây cũng là một điểm tiến bộ của inline so với macro)

Trang 21

Khả năng vào ra mới của C++

Tổng quan về stream trong C++

Nhìn lại C thấy thấy rằng C sử dụng rất nhiều hàm để nhập và xuất dữ liệu ví

dụ như: printf(), scanf(), sprintf(), sscanf(), Các hàm này được khai báo trong tệp tiêu đề stdio.h Về một khía cạnh, chúng không nhất quán về thứ tự và ngữ nghĩa của các tham số

C++ đưa ra streams thông qua các lớp và gọi các lớp này là thư viện các dòng nhập/xuất Streams tạo ra một khả năng rất mạnh cho phép sửa đổi và mở rộng và vì vậy có thể nhập/xuất đối với các kiểu dữ liệu mới do người dùng định nghĩa

Các dòng nhập (istream) cho phép nhập dữ liệu vào stream, các dòng xuất (ostream) cho phép xuất dữ liệu từ stream Thư viện các dòng nhập/xuất là một cấu trúc cây các lớp và chúng ta sẽ nghiên cứu sau

Trong tệp tiêu đề iostream.h C++ định nghĩa 2 đối tượng cin và cout tương ứng là hai thiết bị chuẩn vào/ra và được sử dụng cùng với hai toán tử tải bội >> (vào) và << (ra) Thông thường, ta hiểu cin là bàn phím còn cout là màn hình

Ghi dữ liệu lên thiết bị ra chuẩn (màn hình) bằng <<

Trang 22

<< là một toán tử 2 ngôi: toán hạng bên trái là nơi kết xuất thông tin (có thể là một thiết bị ngoại vi chuẩn hay một tập tin) Toán hạng bên phải là một biểu thức

nào đó Toán tử << có khả năng tải bội mà trước hết nó đúng đối với các kiểu sau

sẽ được xem xét trong lần đọc sau

Đối với xâu ký tự, dấu phân cách cũng là SPACE, TAB, CR còn đối với ký

tự, dấu phân cách là ký tự CR Trong trường hợp này không có khái niệm "ký tự không hợp lệ" Mã sinh ra do bấm phím Enter của lần nhập trước đó vẫn được xem xét cho lần nhập xâu ký tự tiếp theo, do đó sẽ có thể không nhập được ký tự mong

Trang 23

muốn Giải pháp đặt ra là trước mỗi lần nhập xâu ký tự hoặc ký tự ta làm rỗng bộ đệm bàn phím bằng một trong 2 lệnh sau đây:

fflush(stdin); // trong tệp tiêu đề stdio.h

cin.clear(); // hàm thành phần của lớp định nghĩa đối tượng cin

Trong trường hợp muốn nhập xâu ký tự có cả dấu cách thì nên dùng lệnh gets(), khai báo trong tệp tiêu đề stdio.h

cout<<"nhap mot xau: "; gets(s)

cout<<"xau vua nhap: "; puts(s);

}

Streams được sử dụng hoàn toàn độc lập với stdio nhưng việc sử dụng đồng thời hai thư viện này có thể làm phát sinh ra một số vấn đề, chẳng hạn sẽ không làm xuất ra dữ liệu theo đúng thứ tự mong muốn

Đoạn trình 1:

char *name, s[30];

name = "le phuong thuy";

gets(s);

Trang 24

int f(int a,int b)

{while (a!=b) if (a>b) a-=b; else b-=a;

Trang 25

printf("Numerator: "); scanf("%d", &p.a);

printf("Denominator: "); scanf("%d", &p.b);

Bài 1.7

Hãy viết lại chương trình tìm phân số tối giản trong bài tập 1.6 bằng cách thay đổi cách truyền tham số kiểu con trỏ trong hàm reduce bằng cách truyền tham số kiểu tham chiếu

Bài 1.8

Chương trình sau đây đã xây dựng hàm vetamgiac với các tham số có giá trị ngầm định và hàm main đã thực hiện một số lệnh (có chú thích số thứ tự lệnh) gọi hàm này để vẽ một số tam giác trên màn hình Tuy nhiên khi khi chạy chương trình thấy lần lượt một số lời gọi hàm bị báo lỗi

Hãy cho biết những lệnh nào bị sai và cần phải bỏ đi

#include <graphics.h>#include <stdlib.h>

#include <stdio.h>

#include <conio.h>

void vetamgiac(int x1,int y1,int x2=200,int y2=300,

int x3=400,int y3=100)

Trang 26

int driver = DETECT, mode;

initgraph(&driver, &mode, "c:\\tc\\bgi");

Trang 27

LỚP CÁC ĐỐI TƯỢNG

LỚP CÁC ĐỐI TƯỢNG

Các khái niệm cơ bản

Khái niệm đối tượng (Object)

Đối tượng là mô hình của thực thể, bao gồm:

Thông tin mô tả đối tượng (dữ liệu), gọi là trạng thái (status) của đối tượng Các hàm tác động lên đối tượng làm thay đổi trạng thái của đối tượng, gọi là phương thức (method) của đối tượng

Dữ liệu và các hàm của đối tượng gắn liền với nhau để đặc tả đối tượng

Đối tượng bao gồm cả sự vật, hiện tượng và các khái niệm

Ví dụ: Đối tượng học sinh bao gồm các thông tin như họ tên, năm sinh, địa chỉ Các hàm tác động lên dữ liệu của học sinh trước hết phải kể đến các hàm nhập dữ liệu, xem dữ liệu Đối tượng hình chữ nhật bao gồm hai kích thước dài và rộng và các hàm liên quan đến một hình chữ nhật thường là hàm tính chu vi và diện tích của

Lớp đối tượng (Class)

Các đối tượng có cùng thành phần dữ liệu và phương thức hình thành lên một lớp Vậy có thể xem đối tượng là một thể hiện (instance) của lớp

Lớp là kiểu dữ liệu được định nghĩa bởi người dùng nên nó cũng có các tính chất như một kiểu dữ liệu cơ sở

Ví dụ nếu A là lớp thì viết A a; cũng giống như khi viết int x; Tức là a là một biến kiểu A, tuy nhiên ta sẽ gọi là a là một đối tượng của lớp A và lệnh này gọi là lệnh tạo lập đối tượng

Trừu tượng hoá dữ liệu và bao gói thông tin

(Abstraction and Encapsulation)

Việc đóng gói dữ liệu và các hàm gắn với nó vào một đơn vị cấu trúc tức là một lớp các đối tượng được xem như một nguyên tắc bao gói thông tin Nguyên tắc bao

gói dữ liệu ngăn cấm sự truy nhập trực tiếp trong lập trình gọi là sự che dấu thông

tin

Trừu tượng hoá dữ liệu là cách biểu diễn những đặc tính chung nhất của các đối tượng có cùng một bản chất, bỏ qua những thông tin chi tiết của từng đối tượng riêng lẻ Trong lập trình, lớp được sử dụng như kiểu dữ liệu trừu tượng và nâng lên mức khuôn mẫu lớp Phương pháp lập trình hướng đối tượng bao gồm cả trừu tượng hoá dữ liệu và trừu tượng hoá chức năng

Khai báo lớp các đối tượng

Khai báo

Trang 28

Tạo lập đối tượng

Tạo lập đối tượng ngay sau khi khai báo

class class_name

{ // Các thành phần của lớp

} obj_name;

Tạo lập đối tượng bên ngoài lớp

class class_name obj_name;

Phạm vi hoạt động của các thành phần bên trong lớp

Những thành phần được khai trong lớp được chia thành hai nhóm:

Những thành phần private không thể truy nhập bởi những hàm bên ngoài lớp

mà chỉ có thể được truy nhập bởi các hàm thành phần khác bên trong chính lớp đó, tức là chúng chỉ được sử dụng bên trong thân các hàm thành phần của lớp

Những thành phần public có thể được truy nhập bởi các hàm thành phần khác bên trong lớp và các hàm bên ngoài lớp Nếu truy nhập bởi các hàm bên ngoài lớp thì phải sử dụng thông qua đối tượng của lớp Các đối tượng thuộc lớp sẽ truy nhập tới các thành phần public thông qua toán tử xác định "."

Vùng private

Data function

Data function Vùng public

Trang 29

Nhắc lại rằng: C++ là ngôn ngữ mở rộng từ C, nên trong C++ vẫn cho phép dùng các hàm như trong C Hàm không gắn với lớp nào, còn phương thức (hàm thành phần) luôn thuộc một lớp nào đó

Các hàm get_data() và put_data() đều được định nghĩa trực tiếp ngay trong định nghĩa lớp

{ cout<<"Enter name: "; gets(name);

cout<<"Enter age: "; cin>>age; }

Trang 30

Các hàm nhap() và xem() minh hoạ cách định nghĩa hàm thành phần bên ngoài định nghĩa lớp

{ cout<<"Nhap ho ten sinh vien: "; gets(hoten);

cout<<"Nhap nam sinh: "; cin>>namsinh; }

void SINHVIEN::xem()

{ cout<<"Ho ten: "<<hoten<<endl;

cout<<"Nam sinh: "<<namsinh<<endl;}

Trang 31

Ngoài cách dùng từ khoá class ta còn có thể sử dụng các cách sau đây để định nghĩa lớp:

Sử dụng cấu trúc (struct) để định nghĩa lớp, khi đó, ngầm định các trường trong struct là thành phần public Ta có thể quy định lại các trường này thuộc thành phần private hay public

Sử dụng hợp (union) để định nghĩa lớp, khi đó, ngầm định các trường trong union là thành phần public và không thể thay đổi được

Các thành phần trong lớp (Data member and function member)

Khái quát

Dữ liệu thành phần trong một lớp có thể

có các kiểu sau đây: Hàm thành phần trong một lớp có thể có các kiểu sau đây:

1 Kiểu private - dữ liệu riêng

2 Kiểu public - dữ liệu chung

3 Kiểu protected - dữ liệu protected

4 Kiểu class - dữ liệu kiểu lớp

5 Kiểu static - dữ liệu tĩnh

1 Kiểu private - hàm thành phần riêng

2 Kiểu public - hàm thành phần chung

3 Kiểu protected-hàm thành phần protect

4 Kiểu static - hàm thành phần tĩnh

5 Constructor

6 Destructor

7 friend - hàm thành phần thân thiện

8 Overloading Operator - Toán tử tải bội

9 Overloading Function - Hàm tải bội

10 virtual function - Hàm ảo

Thành phần private và thành phần public

Các thành phần (dữ liệu và hàm) private chỉ có thể truy nhập bên trong thân các hàm thành phần trong lớp mà không thể truy nhập bên ngoài lớp thông qua đối tượng của lớp

a.x=10; // sai vì x là dữ liệu thành phần private

a.f(); // sai vì f() là hàm thành phần private

a.y=20; // đúng vì y là dữ liệu thành phần public

a.g() ; // đúng vì g() là dữ liệu thành phần public

Trang 32

}

Ví dụ 2.4

Chương trình sau đây làm các nhiệm vụ:

Xây dựng lớp các sinh viên, trong đó mỗi sinh viên bao gồm các thông tin: số báo danh, tên và năm sinh Sử dụng lớp sinh viên này để tạo một mảng các đối tượng

là các sinh viên Thực hiện các thao tác trên danh sách sinh viên, gồm:

Nhập dữ liệu cho các sinh viên

In lên màn hình danh sách các sinh viên có năm sinh nào đó nhập từ bàn phím In lên màn hình danh sách các sinh viên được sắp xếp tăng dần theo tên

Trang 33

cout<<"nhap nam sinh : "; cin>>x;

cout<<"danh sach sinh vien sinh nam "<<x<<":\n";

for(int i=0;i<n;i++)

if (a[i].getns()==x) a[i].xemsv();

cout<<endl; }

void swap(int i,int j)

{SINHVIEN tg=a[i]; a[i]=a[j]; a[j]=tg; }

Về hình thức: Nếu đặt từ khoá static trước các khai báo thành phần thì dữ liệu

đó là dữ liệu thành phần tĩnh (hoặc gọi là dữ liệu thành phần static)

Về bản chất: Dữ liệu thành phần static chỉ có một bản sao duy nhất dùng chung

cho tất cả các đối tượng trong một lớp

a.n

Trang 34

Như vậy, các biến thành phần static có bản chất là các biến toàn cục trong phạm vi của lớp Dữ liệu thành phần tĩnh thường được sử dụng để duy trì những giá

trị dùng chung cho cả lớp

Mỗi một đối tượng trong lớp đương nhiên đã có thành phần dữ liệu private là thành phần riêng của nó và được bao bọc, che dấu Thành phần dữ liệu public của một đối tượng suy cho cùng cũng vẫn là sở hữu riêng của đối tượng, nhưng có thể được đối tượng đó thể hiện ra bên ngoài Một thành phần dữ liệu static được khai báo trong một lớp thì sẽ không là của riêng bất kỳ đối tượng nào trong lớp đó và tất cả các đối tượng của lớp sẽ dùng chung thành phần dữ liệu static này

Về ý nghĩa, tác dụng: Thành phần static trong lớp là cần thiết để đảm bảo tính

đầy đủ khi mô tả thế giới thực Các thực thể cùng loại khi tồn tại có những cái riêng được đóng kín, có cái riêng có thể bộc lộ và có cái chung (cái chung đó chỉ có ở loại thực thể đang xét)

Trước khi sử dụng các đối tượng trong lớp phải có một lệnh đặc biệt khởi tạo biến static Nếu không có lệnh này thì chương trình sẽ báo lỗi

Trang 35

a.put(); b.put(); c.put();

getche();return 0; }

Vì c là biến static nên nó chỉ có một bản sao duy nhất dùng chung cho ba đối tượng a,b,c trong chương trình Mỗi khi một đối tượng mới được tạo, giá trị của biến static sẽ được xét tới Trong trường hợp này, giá trị của biến static là c lần lượt tăng đến 3 và dùng chung cho cả a,b,c Ngược lại với c, biến n không phải là biến static nên n có ba bản sao tương ứng với ba đối tượng khác nhau

Nếu muốn sử dụng biến c làm biến đếm tổng số đối tượng và thể hiện thứ tự các đối tượng lần lượt được tạo lập, ta nên thay đổi thứ tự các lệnh trong main như sau:

Giả sử GROUP là một nhóm vài thành viên trong cùng một phòng làm việc

Do yêu cầu công việc, họ có một máy tính mạnh dùng chung và truy cập vào mạng internet bằng phương pháp quay số (dial up) Tất cả nhóm đều thống nhất dùng chung một tài khoản truy nhập mạng (Acount) và mật khẩu đi cùng với tài khoản đó (Password) Tuy nhiên, vì mỗi người được cấp một quyền riêng khi khai thác tài nguyên và hơn nữa, từng người đều có các thiết lập khác nhau về hệ thống nên trước khi truy cập vào mạng mỗi người phải khởi động hệ điều hành và phải đăng nhập tên login (login name) và mật khẩu login (login key) của mình

Vậy, nếu xem xét GROUP là một lớp thì Acount và Password phải là thành phần dữ liệu static của tất cả các đối tượng trong lớp Login name và Login key là thành phần dữ liệu tĩnh (của từng đối tượng) của lớp Ta có chương trình hoàn chỉnh sau đây:

{ static long account;

static long password;

char *loginname;

Kết quả:

c: 1 n = 10 c: 2 n = 20 c: 3 n = 30

Kết quả

login name: thu ha login key: 555 acount:1032004 password:2004310 login name: tran binh login key: 666

acount:1032004

Trang 36

{ cout<<"login name: "<<loginname<<endl;

cout<<"login key: "<<loginkey<<endl;

Nếu đặt từ khoá static trước các khai báo hàm thành phần thì đó là hàm thành phần tĩnh (hay hàm thành phần static)

Hàm thành phần static chỉ có thể truy nhập tới những thành phần tĩnh ở trong

lớp (tất nhiên, ngược lại các thành phần tĩnh (dữ liệu và hàm) vẫn có thể truy nhập

bởi các hàm thành phần bình thường khác) Như vậy hàm thành phần tĩnh được sử dụng để thực hiện những công việc có tính chất chung chung cho tất cả các đối tượng trong lớp, chẳng hạn để truy nhập tới dữ liệu thành phần tĩnh

Cũng giống như dữ liệu thành phần tĩnh, hàm thành phần tĩnh không là của riêng từng đối tượng trong lớp, do đó không truy nhập thông qua tên đối tượng mà được gọi thông qua tên lớp cùng với toán tử phân giải miền xác định

class_name:: static_function(arg_list) ;

Ví dụ 2.6

Trang 37

Lớp MEMBERS mô tả lớp các thành viên của một tổ chức Mỗi một thành viên gồm mã số riêng (code) Tất cả các thành viên đều biết được (dùng chung) thông tin về tổng số thành viên trong tổ chức (biến tĩnh count) thông qua phương thức dùng chung (hàm tĩnh show_count() )

void show_code(){cout<<"code: "<<code<<endl;}

static void show_count()

{cout<<"count of members: "<<count<<endl;}

Dữ liệu thành phần kiểu lớp class

Cũng như kiểu dữ liệu struct, class cũng có thể coi là một kiểu dữ liệu và do đó, một thành phần của lớp cũng có thể có kiểu class

Trang 38

Hàm thành phần thân thiện (friend function)

Hàm thành phần thân thiện là một trong những hàm đặc biệt và quan trọng trong lớp Hàm thành phần thân thiện không thuộc miền xác định của lớp, nơi nó được khai báo là thân thiện (nhờ từ khoá friend) Do đó:

Hàm thành phần thân thiện nếu được định nghĩa bên ngoài lớp thì nó được định nghĩa như một hàm bình thường

Không truy nhập hàm thân thiện bởi đối tượng của lớp mà truy nhập nó như một hàm bình thường

Khai báo hàm thân thiện bên trong lớp:

friend return_type fun_name(arg_list);

Định nghĩa hàm thân thiện ở bên ngoài lớp:

return_type fun_name(arg_list)

{ // nội dung hàm thân thiện}

Sau đây ta xem xét một số trường hợp phổ biến khi sử dụng hàm thành phần thân thiện:

Đối truyền vào hàm thân thiện thường là đối tượng có kiểu dữ liệu là lớp

Trang 39

friend void write(FRAC);

friend void reduce(FRAC&);

friend FRAC sum(FRAC,FRAC);

};

void FRAC::read()

{ cout<<"tu so:"; cin>>t;

do { cout<<"mau so:"; cin>>m;

Trang 40

return 0;}

Vai trò của hàm thân thiện lúc này là cho phép sử dụng chúng như những hàm bình thường để thao tác trên đối tượng hoặc xử lý chính dữ liệu của lớp mà không cần truy nhập qua đối tượng của lớp

Giữa hai lớp có thể thân thiện tại một hàm thành phần (thân thiện) nào đó

Để hàm int f( ) là hàm thân thiện giữa lớp X và lớp Y, ta viết:

class X; // khai báo trước

void setvalue(int a1) { a=a1;}

friend int max(XYZ,ABC);

};

int max(XYZ xyz, ABC abc)

{ return xyz.x>abc.a ? xyz.x:abc.a; }

Ngày đăng: 27/03/2014, 12:49

HÌNH ẢNH LIÊN QUAN

Bảng tóm tắt - Lập trình hướng đối tượng C++
Bảng t óm tắt (Trang 89)

TỪ KHÓA LIÊN QUAN

w