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

Lớp đối tượng và tính đóng gói

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 (92.31 KB, 51 trang )




Chương 2

Lớp, đối tượng và tính đóng gói








Lớp


Hàm tạo, hàm hủy


Tham số của hàm tạo


Nội tuyến tự động


Gán đối tượng


Truyền các đối tượng sang hàm



Trả đối tượng từ hàm


Hàm friend








Chửụng 2
Lụựp
, ẹ
oỏi tửụùng vaứ tớnh ủoựng goựi



17
17


































Chương 2
Lớp
, Đ
ối tượng và tính đóng gói




18
18
I/ Lớp (class)

Cú pháp khai báo lớp

class
class_name
{


private :
// khai báo các biến ;
// khai báo các hàm;

public :
// khai báo các biến ;
// khai báo các hàm;
}
objects_list ;

class_name tên của lớp do người dùng đònh nghiã.
objects_list danh sách các đối tượng, có thể tùy chọn.

Các biến, các hàm khai báo bên trong một lớp gọi là các thành viên của lớp đó.

Từ khoá Ý nghiã
class khai báo một lớp

private : qui đònh các biến, các hàm là các thành viên riêng của lớp, bên
ngoài lớp không thể truy cập được.
public :

qui đònh các biến, các hàm là các thành viên chung của lớp, có
thể truy cập chúng từ các thành viên khác của lớp và bởi các
thành phần khác của chương trình có chứa lớp đó.



Cú pháp đònh nghiã hàm thành viên

data_type class_name :: func_name (arg_list)
{
// body of function
}


data_type kiểu dữ liệu của phương thức trả về
class_name tên lớp chứa hàm
::
toán tử phân giải phạm vi (scope resolution operator)
Chương 2
Lớp
, Đ
ối tượng và tính đóng gói



19

19
func_name tên hàm
arg_list danh sách các đối số


Ví dụ 1.1
Khai báo lớp có tên "myclass"

class
myclass
{

// private to myclass
int a;
public:
void set_a(int num);
int get_a();
};

void myclass::set_a(int num)
// đònh nghiã hàm void set_a(int num)
{
a = num;
}

int myclass::get_a()
// đònh nghiã hàm int get_a()
{
return a;
}


int main()
{
myclass ob1, ob2; // khai báo 2 đối tượng có tên ob1, ob2

ob1.set_a(10); // thiết lập giá trò 10 cho bản sao cuả biến a của ob1
ob2.set_a(99); // thiết lập giá trò 99 cho bản sao cuả biến a của ob2

cout << ob1.get_a() << "\n";
cout << ob2.get_a() << "\n";
return 0;
}


Chương 2
Lớp
, Đ
ối tượng và tính đóng gói



20
20
Ví dụ 1.2
Lỗi biên dòch khi truy cập đến biến riêng a từ bên ngoài lớp myclass

int main()
{
myclass ob1, ob2;


ob1.a = 10; // ERROR! cannot access private member
ob2.a = 99; // by non-member functions.
return 0;
}

Ví dụ 1.3
Khi a là biến chung, có thể truy cập a từ bên ngoài lớp myclass
#include <iostream.h>

class myclass {
public:
// now a is public

int a;
// and there is no need for set_a() or get_a()
};

int main()
{
myclass ob1, ob2;

// here, a is accessed directly
ob1.a = 10;
ob2.a = 99;

cout << ob1.a << "\n";
cout << ob2.a << "\n";

return 0;
}




Chương 2
Lớp
, Đ
ối tượng và tính đóng gói



21
21
Ví dụ 1.4
Tạo lớp stack dùng để chứa các ký tự

#include <iostream.h>
#define SIZE 10
// Declare a stack class for characters
class stack {
char stck[SIZE]; // holds the stack
int tos; // index of top-of-stack
public:
void init(); // initialize stack
void push(char ch); // push character on stack
char pop(); // pop character from stack
};

// Initialize the stack
void stack::init()
{

tos = 0;
}

// Push a character.
void stack::push(char ch)
{
if(tos == SIZE) {
cout << "Stack is full";
return;
}
stck[tos] = ch;
tos++;
}

// Pop a character.
char stack::pop()
{
if(tos == 0) {
cout << "Stack is empty";
return 0; // return null on empty stack
Chương 2
Lớp
, Đ
ối tượng và tính đóng gói



22
22
}

tos--;
return stck[tos];
}

int main()
{
stack s1, s2; // create two stacks
int i;

// initialize the stacks
s1.init();
s2.init();

s1.push('a');
s2.push('x');
s1.push('b');
s2.push('y');
s1.push('c');
s2.push('z');

for(i=0; i<3; i++) cout << "Pop s1: " << s1.pop() << "\n";
for(i=0; i<3; i++) cout << "Pop s2: " << s2.pop() << "\n";

return 0;
}


@ Kết quả xuất dữ liệu của chương trình ?




Lưu ý
Khai báo lớp là một trừu tượng logic để đònh nghiã một kiểu dữ liệu mới.

Khai báo một đối tượng dựa vào lớp, tạo ra một thực thể vật lý (có điạ chỉ trong bộ
nhớ) có kiểu dữ liệu đó.

Chương 2
Lớp
, Đ
ối tượng và tính đóng gói



23
23
Mỗi đối tượng của một lớp có bản sao riêng của các biến được khai báo trong lớp.


Bài tập I


1. Hãy tạo lớp card để giữ các mục nhập catalog thẻ thư viện, chưá tựa đề sách (kiểu
chuỗi), tên tác giả (kiểu chuỗi) và số bản (kiểu nguyên). Dùng hàm thành viên
chung store() để lưu trữ thông tin về sách và hàm thành viên chung show() để hiển
thò thông tin. Viết chương trình thực hiện yêu cầu trên.

2. Tạo lớp hàng đợi (queue) để giữ hàng các số nguyên. Tạo một kích thước hàng
dài 100 số nguyên. Viết chương trình thực hiện yêu cầu trên.



II/ Hàm tạo & hàm hủy

1/ Khái niệm
Hàm tạo
(constructor) có cùng tên với lớp, là hàm thành phần của một lớp,
không có kiểu trả về.

Mục đích của hàm tạo nhằm tạo ra các khởi đầu cho một đối tượng.
Hàm tạo được gọi tự động mỗi khi đối tượng của lớp đó được tạo ra.

2/ Khai báo

class class_name {
// khai báo các biến và hàm ;
public :
// khai báo các biến và hàm ;
class_name()
; // khai báo hàm tạo
} objects_list ;


Ví dụ 2.1
Lớp myclass có hàm tạo myclass() và hàm show()
#include <iostream.h>

Chương 2
Lớp
, Đ
ối tượng và tính đóng gói




24
24
class myclass {
int a;
public:

myclass()
; // constructor
void show();
};

myclass::
myclass()
// đònh nghiã hàm tạo
{
cout << "In constructor\n";
a = 10;
}

void myclass::show()
{
cout << a;
}

int main()
{
myclass ob; // hàm tạo myclass() được gọi tự động khi đối tượng ob được tạo ra

ob.show();
return 0;
}

Đối với các
đối tượng chung
, một hàm tạo của đối tượng được gọi một lần khi
chương trình bắt đầu thi hành lần đầu.

Đối với các
đối tượng riêng
, hàm tạo của đối tượng được gọi mỗi khi lệnh khai báo
được thi hành.

3/ Khái niệm hàm hủy (destructor)
Hàm hủy
có cùng tên với lớp, có kèm theo dấu
~
đứng trước, là hàm thành phần của
một lớp, không có kiểu trả về.

Mục đích của hàm hủy nhằm thi hành một số tác động khi đối tượng bò hủy bỏ,
chẳng hạn một đối tượng yêu cầu cấp phát bộ nhớ khi đối tượng được tạo ra và giải
Chương 2
Lớp
, Đ
ối tượng và tính đóng gói




25
25
phóng bộ nhớ khi đối tượng bò hủy bỏ.

Hàm hủy được gọi tự động mỗi khi đối tượng của lớp đó bò hủy bỏ.

4/ Khai báo
class class_name {
// khai báo các biến và hàm ;
public :
// khai báo các biến và hàm ;
class_name(); // khai báo hàm tạo
~class_name()
; // khai báo hàm hủy
} objects_list ;


Ví dụ 2.2
Lớp myclass có hàm tạo myclass() và hàm hủy ~myclass()
#include <iostream.h>

class myclass {
int a;
public:
myclass(); // constructor

~myclass()
; // destructor
void show();
};


myclass::myclass()
{
cout << "In constructor\n";
a = 10;
}

myclass::
~myclass()

{
cout << "Destructing...\n";
}


Chương 2
Lớp
, Đ
ối tượng và tính đóng gói



26
26
void myclass::show()
{
cout << a << "\n";
}

int main()

{
myclass ob;
ob.show();
return 0;
}


Đối với các
đối tượng chung
, hàm hủy của đối tượng được gọi khi chương trình kết
thúc.

Đối với các
đối tượng riêng
, hàm hủy của đối tượng được gọi khi chúng ra khỏi phạm
vi.


Lưu ý
: Không thể biết được đòa chỉ của hàm tạo hoặc hàm hủy.

Ví dụ 2.3
Dùng hàm tạo stack() để tự động khởi đầu ngăn xếp khi đối tượng được
tạo

#include <iostream.h>
#define SIZE 10
// Declare a stack class for characters.
class stack {
char stck[SIZE]; // holds the stack

int tos; // index of top-of-stack
public:
stack(); // constructor
void push(char ch); // push character on stack
char pop(); // pop character from stack
};

// Initialize the stack.
Chửụng 2
Lụựp
, ẹ
oỏi tửụùng vaứ tớnh ủoựng goựi



27
27
stack::stack()
{
cout << "Constructing a stack\n";
tos = 0;
}

// Push a character.
void stack::push(char ch)
{
if(tos==SIZE) {
cout << "Stack is full\n";
return;
}

stck[tos] = ch;
tos++;
}

// Pop a character.
char stack::pop()
{
if(tos==0) {
cout << "Stack is empty\n";
return 0; // return null on empty stack
}
tos--;
return stck[tos];
}

int main()
{
// Create two stacks that are automatically initialized.
stack s1, s2;
int i;

s1.push('a');
s2.push('x');
s1.push('b');
Chương 2
Lớp
, Đ
ối tượng và tính đóng gói




28
28
s2.push('y');
s1.push('c');
s2.push('z');

for(i=0; i<3; i++) cout << "Pop s1: " << s1.pop() << "\n";
for(i=0; i<3; i++) cout << "Pop s2: " << s2.pop() << "\n";

return 0;
}


Ví dụ 2.4
Dùng hàm tạo strtype() và hàm hủy ~strtype() để tự động cấp phát
bộ nhớ cho chuổi *p và giải phóng bộ nhớ khi đối tượng bò hủy.

#include <iostream.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>

#define SIZE 255

class strtype {
char *p;
int len;
public:
strtype(); // constructor

~strtype(); //destructor
void set(char *ptr);
void show();
};

// Initialize a string object.
strtype::strtype()
{
p = (char *) malloc(SIZE);
if(!p) {
cout << "Allocation error\n";
Chửụng 2
Lụựp
, ẹ
oỏi tửụùng vaứ tớnh ủoựng goựi



29
29
exit(1);
}
*p = '\0';
len = 0;
}

// Free memory when destroying string object.
strtype::~strtype()
{
cout << "Freeing p\n";

free(p);
}

void strtype::set(char *ptr)
{
if(strlen(ptr) >= SIZE) {
cout << "String too big\n";
return;
}
strcpy(p, ptr);
len = strlen(p);
}

void strtype::show()
{
cout << p << " - length: " << len;
cout << "\n";
}

int main()
{
strtype s1, s2;

s1.set("This is a test.");
s2.set("I like C++.");

s1.show();
Chương 2
Lớp
, Đ

ối tượng và tính đóng gói



30
30
s2.show();

return 0;
}


Ví dụ 2.5
Dùng đối tượng của lớp timer để xác đònh khoảng thời gian
khi một đối tượng kiểu timer được tạo và cho đến khi bò hủy.

#include <iostream.h>
#include <ctime.h>

class timer {
clock_t start;
public:
timer(); // constructor
~timer(); // destructor
};

timer::timer()
{
start = clock();
}


timer::~timer()
{
clock_t end;

end = clock();
cout << "Elapsed time: " << (end-start) / CLOCKS_PER_SEC << "\n";
}

int main()
{
timer ob;
char c;

Chương 2
Lớp
, Đ
ối tượng và tính đóng gói



31
31
// delay ...
cout << "Press a key followed by ENTER: ";
cin >> c;

return 0;
}


Chương trình này dùng hàm thư viện chuẩn clock() để trả về số chu kỳ đồng hồ xảy
ra từ khi chương trình bắt đầu chạy. Chia giá trò này cho hằng số CLK-TCK để
chuyển thành giá trò giây. (CLK-TCK đònh nghiã số tic-tắc của đồng hồ trong một
giây)


Bài tập II

1. Thực hiện lại lớp queue trong bài tập I/ 2. chương 2 để thay hàm khởi đầu bằng
hàm tạo.

2. Tạo lớp stopwatch để so với đồng hồ bấm giờ trong việc xác đònh thời gian trôi
qua, được tính bằng giây. Các yêu cầu :
- Lớp stopwatch có 2 biến riêng start và end lưu số giây
- Viết hàm tạo để đạt thời gian trôi qua lúc đầu về zero.
- Hai hàm thành viên start() và stop() để lần lượt mở và tắt chế độ đònh giờ.
- Hàm thành viên show() để hiển thò thời gian trôi qua.
- Viết hàm hủy để tự động hiển thò thời gian trôi qua khi đối tượng stopwatch bò hủy.

3. Sửa lỗi trong đoạn chương trình sau :
class sample {
double a, b, c;
public :
double sample() ;
}




III/ Tham số của hàm tạo

Chương 2
Lớp
, Đ
ối tượng và tính đóng gói



32
32

1/ Khái niệm
Hàm tạo có thể có các tham số, chỉ cần bổ sung các tham số thích hợp trong khai báo
lớp và trong đònh nghóa hàm tạo.

Khi khai báo một đối tượng, cần chỉ rõ các tham số này làm đối số.

Ví dụ 3.1
Hàm tạo myclass(int x) có một tham số
#include <iostream.h>

class myclass {
int a;
public:
myclass(
int x
); // constructor
void show();
};
myclass::myclass(int x)
{

cout << "In constructor\n";
a = x;
}

void myclass::show()
{
cout << a << "\n";
}

int main()
{
myclass
ob(4)
;
ob.show();
return 0;
}

Giá trò 4 trong khai báo đối tượng ob(4), là đối số được truyền cho tham số x của
hàm tạo myclass(int x), dùng để khởi tạo biến a.
Cách viết khác (dài hơn) myclass ob = myclass (4) ;
Chương 2
Lớp
, Đ
ối tượng và tính đóng gói



33
33


Lưu ý hàm hủy không có tham số. Do không có cơ chế nào để truyền đối số cho
một đối tượng bò hủy.


Ví dụ 3.2
Có thể truyền nhiều tham số cho hàm tạo

#include <iostream.h>
class myclass {
int a, b;
public:
myclass(
int x, int y
); // constructor
void show();
};

myclass::myclass(int x, int y)
{
cout << "In constructor\n";
a = x;
b = y;
}

void myclass::show()
{
cout << a << ' ' << b << "\n";
}


int main()
{
myclass ob(
4, 7
);
ob.show();
return 0;
}




Ví dụ 3.3
Dùng hàm tạo được tham số hóa để truyền "tên" cho stack.
Chửụng 2
Lụựp
, ẹ
oỏi tửụùng vaứ tớnh ủoựng goựi



34
34

#include <iostream.h>
#define SIZE 10
// Declare a stack class for characters.
class stack {
char stck[SIZE]; // holds the stack
int tos; // index of top-of-stack

char who; // identifies stack
public:
stack(
char c
); // constructor
void push(char ch); // push character on stack
char pop(); // pop character from stack
};

// Initialize the stack.
stack::stack(char c)
{
tos = 0;
who = c;
cout << "Constructing stack " << who << "\n";
}

// Push a character.
void stack::push(char ch)
{
if(tos == SIZE) {
cout << "Stack " << who << " is full\n";
return;
}
stck[tos] = ch;
tos++;
}

// Pop a character.
char stack::pop()

{
if(tos == 0) {
Chương 2
Lớp
, Đ
ối tượng và tính đóng gói



35
35
cout << "Stack " << who << " is empty\n";
return 0; // return null on empty stack
}
tos--;
return stck[tos];
}

int main()
{
// Create two stacks that are automatically initialized.
stack s1(
'A'
), s2(
'B'
);
int i;

s1.push('a');
s2.push('x');

s1.push('b');
s2.push('y');
s1.push('c');
s2.push('z');

// This will generate some error messages.
for(i=0; i<5; i++) cout << "Pop s1: " << s1.pop() << "\n";
for(i=0; i<5; i++) cout << "Pop s2: " << s2.pop() << "\n";
return 0;
}


@ Mục đích : dùng để nhận ra tên stack nào được tham chiếu khi có lỗi xảy ra.
Điều này rất có ích trong quá trình gỡ rối (debug).


Ví dụ 3.4
Đối số của hàm tạo là một chuổi.

#include <iostream.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
Chửụng 2
Lụựp
, ẹ
oỏi tửụùng vaứ tớnh ủoựng goựi




36
36

class strtype {
char *p;
int len;
public:
strtype(
char *ptr
);
~strtype();
void show();
};

strtype::strtype(char *ptr)
{
len = strlen(ptr);
p = (char *) malloc(len+1);
if(!p) {
cout << "Allocation error\n";
exit(1);
}
strcpy(p, ptr);
}

strtype::~strtype()
{
cout << "Freeing p\n";
free(p);
}


void strtype::show()
{
cout << p << " - length: " << len;
cout << "\n";
}

int main()
{
strtype s1(
"This is a test."
) , s2(
"I like C++."
) ;
s1.show();
Chương 2
Lớp
, Đ
ối tượng và tính đóng gói



37
37
s2.show();
return 0;
}

Ví dụ 3.6
Có thể truyền cho hàm tạo của đối tượng một biểu thức hoặc các biến.

#include <iostream.h>

class myclass {
int i, j;
public:
myclass(
int a, int b
);
void show();
};

myclass::myclass(int a, int b)
{
i = a;
j = b;
}

void myclass::show()
{
cout << i << ' ' << j << "\n";
}

int main()
{
int x, y;

cout << "Enter two integers: ";
cin >> x >> y;

// use variables to construct ob

myclass ob(
x, y
);
ob.show();
return 0;}
Chương 2
Lớp
, Đ
ối tượng và tính đóng gói



38
38
Bài tập III


1. Viết chương trình thay đổi lớp stack để cho nó cấp phát bộ nhớ động cho ngăn xếp
chứa các ký tự. Kích thước ngăn xếp được chỉ rõ bằng một tham số với hàm tạo.
Hàm hủy giải phóng bộ nhớ động.

2. Hãy tạo lớp
t_and_d
để truyền ngày và giờ hệ thống hiện hành như một tham số
cho hàm tạo của nó khi được tạo ra. Lớp gồm có hàm thành viên hiển thò ngày giờ
này lên màn hình. Dùng các hàm ngày và giờ chuẩn trong thư viện chuẩn để tìm và
hiện thò ngày.

3. Viết chương trình tạo lớp box có hàm tạo được truyền 3 giá trò double, diễn tả độ
dài các cạnh của hộp. Hãy cho lớp box tính thể tích của hình lập phương và lưu trữ

kết qủa trong biến double. Tạo hàm thành viên vol() để hiển thò thể tích của mỗi đối
tượng box.



IV/ Nội tuyến tự động (in-line)

1/ Khái niệm
Khi đònh nghiã hàm thành phần là đủ ngắn thì có thể đặt đònh nghiã trong khai báo
hàm trong lớp đó. Hàm này sẽ được gọi là
hàm nội tuyến
(in-line func.) và không
cần dùng từ khoá inline đứng trước hàm đó.

Ví dụ 4.1
#include <iostream.h>

class samp {
int i, j;
public:
samp(int a, int b);

// divisible() is defined here and automatically in-lined

int divisible() { return !(i%j) ; }
};
Chương 2
Lớp
, Đ
ối tượng và tính đóng gói




39
39
samp::samp(int a, int b)
{
i = a;
j = b;
}

int main()
{
samp ob1(10, 2), ob2(10, 3);

// this is true
if(ob1.divisible()) cout << "10 divisible by 2\n";

// this is false
if(ob2.divisible()) cout << "10 divisible by 3\n";

return 0;
}

Ví dụ 4.2
Dùng hàm nội tuyến để đònh nghiã các hàm tạo và hàm hủy
#include <iostream.h>

class samp {
int i, j;

public:

samp(int a, int b) { i = a; j = b; }
// inline constructor
int divisible() { return !(i%j); }
};


Ví dụ 4.3
Một hàm ngắn ở bên trong một khai báo lớp
cho dù đặc điểm nội tuyến tự động là không có giá trò.

class myclass {
int i;
public:
myclass(int n) { i = n; }
Chương 2
Lớp
, Đ
ối tượng và tính đóng gói



40
40
void show() { cout << i; }
};


Bài tập IV



1. Viết chương trình chuyển đổi lớp stack trong ví dụ 2.3 chương 2, để lớp này sử
dụng các hàm nội tuyến tự động ở những nơi thích hợp.

2. Viết chương trình chuyển đổi lớp strtype trong ví dụ 2.4 chương 2, để lớp này sử
dụng các hàm nội tuyến tự động.


V/ Gán đối tượng

1/ Khái niệm
Một đối tượng được gán cho một đối tượng khác để cả hai đối tượng có cùng kiểu,
khi đó
sự sao chép từng bit của các thành viên dữ liệu được thực hiện
.

Ví dụ 5.1
// An example of object assignment.
#include <iostream.h>

class myclass {
int a, b;
public:
void set(int i, int j) { a = i; b = j; }
void show() { cout << a << ' ' << b << "\n"; }
};

int main()
{

myclass o1, o2;
o1.set(10, 4);

// assign o1 to o2

×