khai baùo maûng, moãi ñoái töôïng trong maûng phaûi coù khaû naêng töï khôûi ñoäng, nghóa laø coù theå ñöôïc thieát laäp khoâng caàn tham soá. Ñoái töôïng coù khaû naêng töï khôûi ñoä[r]
(1)Chương 2 Lớp 2.1 Mở đầu
Một kiểu liệu biểu diễn cụ thể khái niệm thực tế Ví dụ kiểu int biểu diễn cụ thể khái niệm số nguyên toán học
Trong C++, kiểu liệu nội (built-in data types) :int, long, float, double, char
cho phép kiểm tra lúc biên dịch phát sinh mã chương trình tối ưu Các kiểu liệu cung cấp giao diện tự nhiên độc lập với phần cài đặt
Lớp C++ cài đặt kiểu liệu trừu tượng người sử dụng định nghĩa, cho
phép kết hợp liệu, phép toán, hàm liên quan để tạo đơn vị chương trình Các lớp có đầy đủ ưu điểm tiện lợi kiểu liệu nội Lớp tách rời phần giao diện (chỉ liên quan với người sử dụng) phần cài đặt lớp
2.2 Lớp thành phần lớp
Ví dụ so sánh: Xây dựng kiểu 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; struct Stack
{
Item *st, *top; int size;
};
void StackInit(Stack *ps, int sz) {
ps->st = ps->top = new Item[ps->size=sz]; }
void StackCleanUp(Stack *ps) {
delete [] ps->st; }
bool StackFull(Stack *ps) {
return (ps->top - ps->st >= ps->size); }
(2)return (ps->top <= ps->st); }
bool StackPush(Stack *ps, Item x) {
if (StackFull(ps)) return false; *ps->top++ = x;
return true; }
bool StackPop(Stack *ps, Item *px) {
if (StackEmpty(ps)) return false; *px = * ps->top;
return true; }
void XuatHe16(long n) {
static char hTab[] =
{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E' ,'F'};
Stack s;
StackInit(&s,8); int x;
do {
StackPush(&s, n%16); n /= 16;
} while(n);
while(StackPop(&s,&x)) cout << hTab[x];
StackCleanUp(&s); }
Nhận xét:
Giải vấn đề
Khai báo cấu trúc liệu nằm riêng, hàm xử lý liệu nằm riêng nơi khác
Do khó theo dõi quản lý hệ thống lớn Vì khó bảo trì
Mọi thao tác có tham số trỏ đến đối tượng cần thao tác Tư tưởng
thể hàm hay thủ tục đóng vai trị trọng tâm Đối tượng gởi đến cho hàm xử lý
Trình tự sử dụng qua bước: Khởi động, sử dụng thực sự, dọn dẹp
2 Cách tiếp cận dùng hàm thành phần: //
struct Stack {
(3)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); };
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; }
void XuatHe16(long n) {
static char hTab[] =
{'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F'};
Stack s; s.Init(8); int x; {
s.Push(n%16); n /= 16;
} while(n);
while(s.Pop(&x)) cout << hTab[x]; s.CleanUp();
}
Nhaän xeùt:
Giải vấn đề
Dữ liệu hàm xử lý liệu gom vào chỗ bên cấu trúc Do dễ
theo dõi quản lý, dễ bảo trì nâng caáp
Các thao tác bớt tham số so với cách tiếp cận cổ điển Vì việc lập
trình gọn Tư tưởng thể đối tượng đóng vai trị trọng tâm Đối tượng thực thao tác
(4)Các hàm thành phần.
Là hàm khai báo lớp
Hàm thành phần định nghĩa bên bên ngồi lớp
Hàm thành phần có nghi thức giao tiếp giống với hàm bình thường khác: có tên,
danh sách tham số, giá trị trả veà
Lớp
Trong cách tiếp cận dùng struct hàm thành phần, người sử dụng có toàn quyên truy
xuất, thay đổi thành phần liệu đố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++)
s.Push(i);
Vì vậy, ta khơng có an toàn liệu Lớp phương tiện để khắc phục nhược
điểm
Lớp có cách thay từ khoá struct từ khoá class
Trong lớp thành phần riêng tư (private) nghĩa giới bên
ngồi khơng phép truy xuất Do có an toàn liệu class Stack
{
Item *st, *top; 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); };
Phaùt bieåu:
s.size = 100; // Bao sai
Sẽ bị báo sai lúc biên dịch, nhờ tránh lỗi run-time khó tìm thực chương trình
Thuộc tính truy xuất
Tuy nhiên lớp trở thành vơ dụng hàm thành phần trở thành
private không dùng Điều khắc phục nhờ thuộc tính truy xuất
Thuộc tính truy xuất thành phần lớp rõ phần chương trình phép
truy xuất đến
o Thuộc tính private o Thuộc tính public
Các thành phần nội lớp, bao gồm liệu hàm phục vụ nội
(5)Ví dụ lớp thuộc tính truy xuất
// Stack.h
typedef int bool; typedef int Item;
const bool false = 0, true = 1; 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() {delete [] st;}
bool Full() const {return (top - st >= size);} bool Empty() const {return (top <= st);}
bool Push(Item x); bool Pop(Item *px); };
// 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;
return true; }
Ví dụ lớp thuộc tính truy xuất
// he16.cpp
#include "stack.h" void XuatHe16(long n) {
static char hTab[] = {'0','1','2','3','4','5','6','7', '8','9','A','B','C','D','E','F'};
Stack s(8); int x;
do {
(6)} while(n);
while(s.Pop(&x)) cout << hTab[x]; }
Tự tham chiếu
Là tham số ngầm định hàm thành phần trỏ đến đối tượng, có tên this Nhờ
hàm thành phần biết thao tác đối tượng
Khi đối tượng gọi thao tác, địa đối tượng gởi cách ngầm
định với tên this, tên thành phần đối tượng hiểu đối tượng có địa this
Phương thức thiết lập hủy bỏ
Phương thức thiết lập
Phương thức thiết lập xây dựng nhằm mục đích khắc phục lỗi quên khởi động
đối tượng khởi động dư Việc quên khởi động đối tượng thường gây lỗi khó tìm
Phương thức thiết lập hàm thành phần đặc biệt tự động gọi đến
đối tượng thuộc lớp tạo Người ta thường lợi dụng đặc tính để 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 với hàm thành phần
khác
Có thể có nhiều phiên khác phương thức thiết lập
typedef int bool; typedef int Item;
const bool false = 0, true = 1; 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() {delete [] st;}
bool Full() const {return (top - st >= size);} bool Empty() const {return (top <= st);}
bool Push(Item x); bool Pop(Item *px); };
Hàm bạn (friends)
Nguyên tắc chung thao tác lớp thơng qua hàm thành phần Tuy nhiên
có trường hợp ngoại lệ, hàm phải thao tác hai lớp
Hàm bạn lớp hàm khai báo bên phép truy xuất
(7) Ta làm hàm trở thành hàm bạn lớp cách đưa khai báo hàm vào
trong lớp, thêm từ khoá friend đầu
Ta dùng hàm bạn trường hợp hàm phải hàm toàn cục có liên quan mật
thiết với lớp, hàm thành phần lớp khác
Ví dụ: Nhân ma trận, không dùng hàm bạn
const int N = 4; class Vector {
double a[N]; public:
double Get(int i) const {return a[i];} void Set(int i, double x) {a[i] = x;} };
class Matrix {
double a[N][N]; public:
double Get(int i, int j) const {return a[i][j];} void Set(int i, int j, double x) {a[i][j] = x;} //
};
Vector Multiply(const Matrix &m, const Vector &v) {
Vector r;
for (int i = 0; i < N; i++) {
r.Set(i, 0);
for (int j = 0; j < N; j++)
r.Set(i, r.Get(i)+ m.Get(i,j)*v.Get(j)); }
return r; }
Ví dụ minh hoạ: Sử dụng hàm bạn
const int N = 4; class Matrix; class Vector {
double a[N]; public:
double Get(int i) const {return a[i];} void Set(int i, double x) {a[i] = x;}
friend Vector Multiply(const Matrix &m, const Vector &v); };
class Matrix {
(8)double Get(int i, int j) const {return a[i][j];} void Set(int i, int j, double x) {a[i][j] = x;}
friend Vector Multiply(const Matrix &m, const Vector &v); };
Ví dụ minh hoạ: Sử dụng hàm bạn
Vector Multiply(const Matrix &m, const Vector &v) {
Vector r;
for (int i = 0; i < N; i++) {
r.a[i] = 0;
for (int j = 0; j < N; j++)
r.a[i] += m.a[i][j]*v.a[j]; }
return r; }
Hàm thành phần (const member functions)
Hàm thành phần hàm thành phần áp dụng cho đối tượng
haèng
Ta qui định hàm thành phần cách thêm từ khoá const vào cuối khai
báo
Ta khai báo hàm thành phần không thay đổi thành phần liệu
của đối tượng
extern char *strdup(const char *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);} };
inline char *strdup(const char *s) {
return strcpy(new char[strlen(s) + 1], s); }
void main() {
const string Truong("Dai Hoc Ban Cong Ton Duc Thang"); string s("ABCdef");
(9)Truong.Output();
Truong.ToLower(); // Bao loi }
Thành phần tónh (static members)
Thành phần liệu tĩnh thành phần liệu dùng chung cho đối tượng thuộc
lớp
Hàm thành phần tĩnh hàm thành phần hoạt động khơng cần liệu đối
tượng, nói cách khác, khơng cần đối tượng
Ta dùng hàm thành phần tĩnh thay hàm tồn cục có liên quan mật thiết với
lớp
Ta dùng hàm thành phần tĩnh để tạo đối tượng có giá trị trả cho biết việc tạo
đối tượng có thành cơng theo nghĩa luận lý hay khơng
Ví dụ 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:
static bool LeapYear(int y) {return y%400 == || y%4==0 && y%100 != 0;}
static int DayOfMonth(int m, int y);
static bool ValidDate(int d, int m, int y); void Input();
};
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]; }
bool betw(int x, int a, int b) {
return x >= a && x <= b; }
bool CDate::ValidDate(int d, int m, int y) {
(10)}
void CDate::Input() {
int d,m,y;
cin >> d >> m >> y;
while (!ValidDate(d,m,y)) {
cout << "Please enter a valid date: "; cin >> d >> m >> y;
}
day = d; month = m; year = y; }
Ví dụ thành phần tónh: Stack
// stack.h
typedef int Item; class Stack
{
Item *st, *top; int size;
void Init(int sz) {st = top = new Item[size=sz];} void CleanUp() {if (st) delete [] st;}
public:
Stack(int sz = 20) {Init(sz);} ~Stack() {CleanUp();}
static Stack *Create(int sz);
bool Full() const {return (top - st >= size);} bool Empty() const {return (top <= st);}
bool Push(Item x); bool Pop(Item *px); };
// Stack.cpp
#include <iostream.h> #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;
return true; }
(11){
Stack *ps = new Stack(sz); if (!ps->st)
{
delete ps; return NULL; }
return ps; }
void main() {
Stack *ps = new Stack(50000); // khong biet tao duoc stack khong
Stack *pr = Stack::Create(50000); if (!pr)
{ cout << "Khong the tao stack"; return; } else
pr->Push(5); // }
2.3 Thiết lập huỷ bỏ đối tượng
Ta cần kiểm soát phương thức thiết lập gọi, phương thức huỷ bỏ gọi
Khi đối tượng thiết lập gọi? Khi đối tượng tạo ra. Khi phương thức huỷ bỏ gọi? Khi đối tượng bị huỷ đi.
Thời gian từ đối tượng tạo đến bị huỷ gọi thời gian
soáng.
Vậy vấn đề xác định phương thức thiết lập huỷ bỏ gọi trở thành: Khi đối tượng tạo ?
Khi đối tượng bị huỷ ?
Thời gian sống đối tượng khác tuỳ thuộc đối tượng thuộc lớp lưu trữ
(storage class) Trong C++ có lớp lưu trữ sau: auto
global static
free stored
Ta xét loại sau:
Đối tượng tự động Đối tượng toàn cục Đối tượng tĩnh
Đối tượng cấp phát động Đối tượng thành phần
Đối tượng tự động
Đối tượng tự động (automatic objects) đối tượng tự động sinh tự động bị
huỷ
Đối tượng địa phương
(12)Nó tự động sinh chương trình thực ngang dòng lệnh chứa định nghĩa bị huỷ sau chương trình hồn tất khối chứa định nghĩa
Khi khởi động đối tượng đối tượng kiểu, chế tạo đối tượng mặc
nhiên chép bit, đối tượng khởi động chia sẻ tài nguyên với đối tượng nguồn
Đối tượng biến địa phương
#include <iostream.h> #include <string.h>
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;}
void Output() const {cout << p;} };
char *strdup(const char *s) {
return strcpy(new char[strlen(s) + 1], s); }
void main() {
string a("Nguyen Van A"); string b = a; // String b(a) a.Output(); cout << "\n"; b.Output(); cout << "\n"; }
Xuất liệu thực đ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 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 bị huỷ, phần tài nguyên chung bị huỷ lần (SAI)
Đối tượng tham số truyền giá trị
Đối tượng tham số hàm, truyền giá trị tham số hình thức
tham số thực sự, nên có nội dung vật lý giống tham số thực chế chép bit extern char *strdup(const char *s);
class String {
(13)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; };
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 > ? "a > b" : c == ? "a = b" : "a < b") << "\n";
}
Khi thực đoạn chương trình trên, ta xuất liệu (có thể thay đổi lần
thực khác máy khác): delete 0x0f34
a > b
delete 0x0f34 delete 0x0f22
Đối tượng 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;}
void Output() const {cout << p;} bool Compare(String s) const; String UpCase() const;
};
String String::UpCase() const {
String r = *this; strupr(r.p);
(14)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"; }
Đối tượng giá trị trả về
Khi thực đoạn chương trình trên, ta 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ả biểu thức trả Do có chia sẻ tài nguyên (SAI)
Các lỗi sai gây đoạn chương trình chép đối tượng (phát biểu String r =
*this), đối tượng giá trị trả phép gán (A = a.Upcase)
Ta khắc phục lỗi gây phép gán cách thay hai phát biểu khai báo A
và gán phát biểu khởi động: String A = a.UpCase(); 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"; }
Xuất liệu trường hợp
a = Nguyen Van A delete 0x0d32 a = NGUYEN VAN A A = NGUYEN VAN A delete 0x0d32 delete 0x0d32
Null pointer assignment
(15) Các lỗi sai nêu gây chép đối tượng, ta khắc phục cách
“dạy” trình biên dịch chép đối tượng cách luận lý thay chép bit theo nghĩa vật lý Điều thực nhờ phương thức thiết lập chép
Phương thức thiết lập chép phương thức thiết lập có tham số đối tượng
kiểu, dùng để chép đối tượng
Phương thức thiết lập chép thực chép theo nghĩa logic, thông thường
tạo nên tài nguyên (sao chép sâu)
Phương thức thiết lập chép chia sẻ tài nguyên cho đối tượng (sao
chép nông) Trong trường hợp này, cần có chế để kiểm sốt sử huỷ bỏ đối tượng 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);}
~String() {cout << "delete "<< (void *)p << "\n"; delete [] p;}
void Output() const {cout << p;} bool Compare(String s) const; String UpCase() const;
};
char *strdup(const char *s) {
return strcpy(new char[strlen(s)+1], s); }
Phương thức thiết lập chép
bool String::Compare(String s) const {
return strcmp(p,s.p); }
String String::UpCase() const {
String r = *this; strupr(r.p);
return r; }
void main() {
clrscr();
String a("Nguyen Van A"); String b("Le Van Beo"); String aa = a;
int c = a.Compare(b);
(16)cout << "a = "; a.Output(); cout << "\n"; String A = a.UpCase();
cout << "a = "; a.Output(); cout << "\n"; cout << "A = "; A.Output(); cout << "\n"; }
Xuất liệu trường hợp sau:
delete 0x0d84 a > b
a = Nguyen Van A delete 0x0d84 a = Nguyen Van A A = NGUYEN VAN A delete 0x0d96 delete 0x0d72 delete 0x0d62 delete 0x0d50
Mỗi đối tượng có tài ngun riêng nên khơng xảy trường hợp vùng tài
nguyên bị giải phóng nhiều laàn
Phương thức thiết lập chép
Tham số phương thức thiết lập chép bắt buộc tham chiếu
Phương thức thiết lập chép dùng để chép nơng, tài nguyên
được chia sẻ có biến đếm để kiểm soát Lưu ý:
Nếu đối tượng khơng có tài ngun riêng khơng cần phương thức thiết lập
cheùp
Khi truyền tham số đối tượng thuộc lớp có phương thức thiết lập chép, ta nên
truyền tham chiếu có thêm từ khố const
Sao chép nông chép sâu
Dùng phương thức thiết lập chép trên, đối tượng có tài ngun
riêng sao chép sâu
Ta sao chép nơng cách chia sẻ tài nguyên dùng biến đếm để kiểm
soát số thể đối tượng có chia sẻ tài nguyên
Khi đối tượng thay đổi nội dung, phải tách khỏi đối tượng dùng
chung tài nguyên, nói cách khác, phải có tài nguyên riêng (như chép sâu)
Ví dụ chép nông
extern char *strdup(const char *s); class StringRep
{
friend class String; char *p;
int n;
StringRep(const char *s) {p = strdup(s); n = 1;}
(17)char *strdup(const char *s) {
return strcpy(new char[strlen(s)+1], s); }
class String {
StringRep *rep; public:
String(const char *s = "") {rep = new StringRep(s);} String(const String &s) {rep = s.rep; rep->n++;} ~String();
void Output() const {cout << rep->p;} bool Compare(String s) const;
String UpCase() const; void ToUpper();
};
String::~String() {
if ( rep->n <= 0) delete rep; }
bool String::Compare(String s) const {
return strcmp(rep->p,s.rep->p); }
String String::UpCase() const {
String r(rep->p); strupr(r.rep->p); return r;
}
void main() {
clrscr();
String a("Nguyen Van A"); String b("Le Van Beo"); String aa = a;
int c = a.Compare(b);
cout << (c > ? "a > b" : c == ? "a = b" : "a < b") << "\n";
cout << "a = "; a.Output(); cout << "\n"; String A = a.UpCase();
(18) Xuất liệu thực đoạn chương trình sau:
a > b
a = Nguyen Van A a = Nguyen Van A A = NGUYEN VAN A delete 0x0d8a delete 0x0d72 delete 0x0d58 delete 0x0d84 a > b
a = Nguyen Van A delete 0x0d84 a = Nguyen Van A A = NGUYEN VAN A delete 0x0d96 delete 0x0d72 delete 0x0d62 delete 0x0d50 a > b
a = Nguyen Van A a = Nguyen Van A A = NGUYEN VAN A delete 0x0d8a delete 0x0d72 delete 0x0d58
Đối tượng tĩnh
Đối tượng tĩnh đối tượng định nghĩa tồn cục, định nghĩa có thêm
từ khố static (tồn cục địa phương)
Đối tượng toàn cục tĩnh toàn cục tạo bắt đầu chương trình bị huỷ
đi kết thúc chương trình
Đối tượng tĩnh địa phương tạo chương trình thực đoạn mã chứa định
nghĩa đối tượng lần bị huỷ kết thúc chương trình
Trình tự thực chương trình gồm:
Gọi phương thức thiết lập cho đối tượng toàn cục Thực hàm main()
Gọi phương thức huỷ bỏ cho đối tượng toàn cục
Ví dụ đối tượng tồn cục Xét đoạn chương trình sau:
#include <iostream.h> void main()
{
cout << "Hello, world.\n"; }
(19)Hello, world
And then exitting…
Yêu cầu khơng thay đổi hàm main() hình thức
Đoạn chương trình sửa lại sau:
#include <iostream.h> void main()
{
cout << "Hello, world.\n"; }
class Dummy {
public:
Dummy() {cout << "Entering a C++ program saying \n";} ~Dummy() {cout << "And then exitting ";}
};
Đối tượng thành phần lớp
Đối tượng thành phần đối tượng khác, đối tượng thuộc lớp “lớn”
được tạo ra, thành phần tạo Phương thức thiết lập (nếu có) tự động gọi cho đối tượng thành phần
Nếu đối tượng thành phần phải cung cấp tham số thiết lập đố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 đối tượng thành phần
Cú pháp để khởi động đối tượng thành phần dùng dấu hai chấm (:) theo sau tên
thành phần tham số khởi động
Khi đối tượng kết hợp bị huỷ đối tượng thành phần bị huỷ đi,
nghĩa phương thức huỷ bỏ gọi cho đối tượng thành phần, sau phương thức huỷ bỏ đối tượng kết hợp gọi
class Diem {
double x,y; public:
Diem(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 << "\n"; delete [] p;
(20)Đối tượng thành phần lớp
class TamGiac {
Diem A,B,C; public:
void Ve() const; //
};
class SinhVien {
String MaSo; String HoTen; int NamSinh; public:
};
TamGiac t; // Bao sai SinhVien s; // Bao sai
Đối tượng thành phần lớp - thiết lập
class Diem {
double x,y; public:
Diem(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 << "\n"; delete [] p;
// };
Khởi động đối tượng thành phần: Dùng dấu hai chấm (:)
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; //
};
TamGiac t(100,100,200,400,300,300);
Khởi động đối tượng thành phần: Dùng dấu hai chấm (:)
(21)String MaSo; String HoTen; int NamSinh; public:
SinhVien(char *ht, char *ms, int ns):HoTen(ht), MaSo(ms) {NamSinh = ns;}
// };
SinhVien s(“Nguyen Van Beo”, “19920005”, 1985); //Ok
Khởi động đối tượng thành phần: Dùng dấu hai chấm (:)
Cú pháp dùng dấu hai chấm dùng cho đối tượng thành phần thuộc kiểu
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), MaSo(ms), NamSinh(ns){}
// };
Đối tượng thành phần - Huỷ bỏ
Khi đối tượng kết hợp bị huỷ bỏ, đối tượng thành phần bị huỷ bỏ
class SinhVien {
String MaSo, HoTen; int NamSinh;
int SoMon; double *Diem; 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)); }
Đối tượng thành phần mảng
Khi mảng tạo ra, phần tử tạo ra, phương thức
(22) Vì khơng thể cung cấp tham số khởi động cho tất phần tử mảng nên
khai báo mảng, đối tượng mảng phải có khả tự khởi động, nghĩa thiết lập khơng cần tham số
Đối tượng có khả tự khởi động trường hợp sau:
– Lớp khơng có phương thức thiết lập
– Lớp có phương thức thiết lập khơng tham số
– Lớp có phương thức thiết lập mà tham số có giá trị 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 {
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(char *ht, char *ms, int ns):HoTen(ht), MaSo(ms), NamSinh(ns){}
// };
String as[3]; // Bao sai Diem ad[5]; // Bao sai
SinhVien asv[7]; // Bao sai
Dùng 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;} //
(23)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(char *ht = “Nguyen Van A”, char *ms = “19920014”, int ns = 1982):HoTen(ht), MaSo(ms), NamSinh(ns){}
// };
String as[3]; // Ok: Ca ba phan tu deu la chuoi rong Diem ad[5]; // Ok: ca diem deu la (0,0)
SinhVien asv[7]; // Ok: Het sai ca sinh vien deu co cung hoten, maso, namsinh
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;}
// };
class SinhVien {
(24)SinhVien(char *ht, char *ms, int ns):HoTen(ht), MaSo(ms), NamSinh(ns){}
SinhVien():HoTen(“Nguyen Van A”), MaSo(“19920014”), NamSinh(1982){}
// };
String as[3]; // Ca ba phan tu deu la chuoi rong Diem ad[5]; // ca diem deu la (0,0)
SinhVien asv[7];// Ca sinh vien deu co cung hoten, maso, namsinh
Đối tượng cấp phát động
Đối tượng cấp phát động đối tượng tạo phép toán new bị
huỷ phép toán delete
Phép toán new cấp đối tượng vùng heap (hay vùng free store) gọi phương
thức thiết lập cho đối tượng cấp
Dùng new cấp đối tượng dùng delete để huỷ đối tượng Dùng new delete cấp nhiều đối tượng huỷ nhiều đối 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){}; //
}; //
Cấp huỷ đố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 pi;
(25) Trong trường hợp cấp nhiều đối tượng, ta cung cấp tham số cho phần
tử 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 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 khắc phục cách cung cấp phương thức thiết lập để đối tượng có
khả tự khởi độ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){};
// };
Khi phần tử cấp khởi động với giá trị
int *pai = new int[10]; Diem *pad = new Diem[5];
// ca diem co cung toa (0,0) String *pas = new String[5];
// Ca chuoi cung duoc khoi dong bang “Alibaba”
Việc huỷ nhiều đối tượng thực cách dùng delete có thêm dấu []
trước
delete [] pas; delete [] pad; delete [] pai;
(?) Có thể thay ba phát biểu phát biểu sau không ? delete pas,pad,pai; // ??
(26) Lớp có hai phần tách rời, phần giao diện khai báo phần public để người
sử dụng “thấy” sử dụng, hai chi tiết cài đặt bao gồm liệu khai báo phần private lớp chi tiết mã hố hàm thành phần, vơ hình người dùng
Ta thay đổi uyển chuyển chi tiết cài đặt, nghĩa thay đổi tổ chức
liệu lớp, thay đổi chi tiết thực hàm thành phần (do thay đổi tổ chức liệu để cải tiến giải thuật) Nhưng bảo đảm khơng thay đổi phần giao diện khơng ảnh hưởng đến người sử dụng, khơng làm đổ vỡ kiến trúc hệ thống
Lớp ThoiDiem cài đặt với thành phần liệu giờ, phút, giây
tổng số giây tính từ
Lớp ThoiDiem – Cách 1
class ThoiDiem {
int gio, phut, giay;
static bool HopLe(int g, int p, int gy); public:
ThoiDiem(int g = 0, int p = 0, int gy = 0) {Set(g,p,gy);} void Set(int g, int p, int gy);
int LayGio() const {return gio;} int LayPhut() const {return phut;} int LayGiay() const {return giay;} void Nhap();
void Xuat() const; void Tang();
void Giam(); };
Lớp ThoiDiem – Cách 2
class ThoiDiem {
long tsgiay;
static bool HopLe(int g, int p, int gy); public:
ThoiDiem(int g = 0, int p = 0, int gy = 0) {Set(g,p,gy);} void Set(int g, int p, int gy);
int LayGio() const {return tsgiay / 3600;} int LayPhut() const {return (tsgiay%3600)/60;} int LayGiay() const {return tsgiay % 60;}
void Nhap();
void Xuat() const; void Tang();
void Giam(); };
Có thể xem chi tiết đầy đủ lớp thời điểm cài đặt giờ, phút, giây cài đặt
tổng số giây tập tin nguồn thgian.cpp thgian2.cpp,
Tương tự lớp Stack cài đặt dạng mảng dạng danh sách liên
keát
(27) Khi ta nghĩ đến “nó” khái niệm riêng rẽ, xây dựng lớp biểu diễn khái
niệm Ví dụ lớp SinhVien
Khi ta nghĩ đến “nó” thực thể riêng rẽ, tạo đối tượng thuộc lớp Ví dụ đối
tượng Sinh viên “Nguyen Van A” (và thuộc tính khác mã số, năm sinh…)
Lớp biểu diễn cụ thể khái niệm, lớp ln ln DANH TỪ Các thuộc tính lớp thành phần liệu, nên chúng luôn DANH TỪ Các hàm thành phần thao tác rõ hoạt động lớp nên hàm
ĐỘNG TỪ
Các thuộc tính liệu phải vừa đủ để mô tả khái niệm, không dư, không thiếu
// SAI
class TamGiac {
Diem A,B,C;
double ChuVi, DienTich; public:
// };
// DUNG
class TamGiac {
Diem A,B,C; public:
//
double ChuVi()const; double DienTich() const; };
Cá biệt có số thuộc tính suy diễn đòi hỏi nhiều tài nguyên thời gian để thực tính tốn, ta khai báo liệu thành phần Ví dụ tuổi trung bình dân Việt Nam.
class QuocGia {
long DanSo;
double DienTich; double TuoiTrungBinh; //
public:
double TinhTuoiTB() const; //
};
Chi tiết cài đặt, bao gồm liệu phần mã hố hàm thành phần thay đổi
(28) Dữ liệu thành phần nên kết hợp thay phân rã
Chi tiết cài đặt, bao gồm liệu phần mã hố hàm thành phần thay đổi
uyển chuyển phần giao diện, nghĩa phần khai báo hàm thành phần cần phải cố định để không ảnh hưởng đến người sử dụng (xem phần 2.4)
Dữ liệu thành phần nên kết hợp thay phân rã
Trong trường hợp, nên có phương thức thiết lập để khởi động đối tượng Nên có phương thức thiết lập có khả tự khởi động khơng cần tham số Nếu đối tượng có nhu cầu cấp phát tài ngun phải có phương thức thiết lập,
phương thức thiết lập chép để khởi động đối tượng đối tượng kiểu có phương thức huỷ bỏ để dọn dẹp Ngồi cịn phải có phép gán (chương tiếp theo)
Ngược lại, đối tượng đơn giản khơng cần tài ngun riêng khơng cần phương thức
thiết lập chép không cần phương thức huỷ bỏ
2.6 Một số ví dụ lớp
Lớp Diem biểu diễn khái niệm điểm mặt phẳng với hai thành phần toạ độ x
y Khai báo lớp Diem đặt tập tin diem.h chi tiết cài đặt hàm thành phần đặt tập tin diem.cpp Có thể xây dựng số ứng dụng lớp điểm tập tin tdiem.cpp dongho.cpp với ứng dụng tương ứng tdiem.exe dongho.exe
(hoặc dongho2.exe, dongho3.exe)
Lớp DaGiac biểu diễn khái niệm đa giác mặt phẳng có thao tác tịnh tiến, vị
tự, quay, vẽ… Phần khai báo đặt tập tin dagiac.h, phần cài đặt tập tin