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

Bài giảng kỹ thuật lập trình_Chương 6: Lớp và đối tượng II ppsx

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 (362.95 KB, 27 trang )

© 2004, HOÀNG MINH SƠN
Chương 1
0101010101010101100001
0101010101010101100001
0101010101010101100001
0101010100101010100101
0101010100101010100101
0101010100101010100101
1010011000110010010010
1010011000110010010010
1010011000110010010010
1100101100100010000010
1100101100100010000010
1100101100100010000010
0101010101010101100001
0101010101010101100001
0101010101010101100001
0101010100101010100101
0101010100101010100101
0101010100101010100101
1010011000110010010010
1010011000110010010010
1010011000110010010010
1100101100100010000010
1100101100100010000010
1100101100100010000010
0101010101010101100001
0101010101010101100001
0101010101010101100001
0101010100101010100101
0101010100101010100101


0101010100101010100101
1010011000110010010010
1010011000110010010010
1010011000110010010010
1100101100100010000010
1100101100100010000010
1100101100100010000010
Kỹ thuật lập trình
10/6/2005
y = A*x + B*u;
x = C*x + d*u;
StateController
start()
stop()
LQGController
start()
stop()
Chương 6: Lớpvà₫ốitượng II
2
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Nộidung chương 6
6.1 Tạovàhủy ₫ốitượng
6.2 Xây dựng các hàm tạovàhàmhủy
6.3 Nạpchồng toán tử
6.4 Khai báo friend
6.5 Thành viên static (tự₫ọc)
3
© 2004, HOÀNG MINH SƠN

Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Có bao nhiêu cách ₫ể tạo/hủy ₫ối tượng?
 Tạo/hủy tự ₫ộng: Định nghĩa một biến thuộc một lớp
—Bộ nhớ của ₫ối tượng (chứa các dữ liệu biến thành viên) ₫ược tự
₫ộng cấp phát giống như với một biến thông thương
—Bộ nhớ của ₫ối tượng ₫ược giải phóng khi ra khỏi phạm vi ₫ịnh
nghĩa
class X {
int a, b;

};
void f( X x1) {
if ( ) {
X x2;

}
}
X x;
6.1 Tạovàhủy ₫ốitượng
Đốitượng ₫ượctạo ra trong ngănxếp
Đốitượng ₫ượctạo ra trong vùng dữ liệuchương trình
Thời ₫iểmbộ nhớ cho x2 ₫ượcgiải phóng
Thời ₫iểmbộ nhớ cho x1 ₫ượcgiải phóng
4
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
 Tạo/hủy ₫ối tượng ₫ộng bằng toán tử new và delete:
X* pX = 0;

void f( ) {
if ( ) {
pX = new X;

}
}
void g( ) {

if (pX != 0) {
delete pX;

}
}
Đốitượng ₫ượctạora
trong vùng nhớ tự do
Bộ nhớ của ₫ốitượng trong
heap ₫ượcgiải phóng
5
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Vấn ₫ề 1: Khởitạotrạng thái ₫ốitượng
 Sau khi ₫ượctạora, trạng thái của ₫ốitượng (bao gồmdữ liệu
bên trong và các mốiquanhệ) thường là bất ₫ịnh => sử dụng
kém an toàn, kém tin cậy, kém thuậntiện
X x; // x.a = ?, x.b = ?
X *px = new X; // px->a = ?, px->b = ?;
class Vector { int n; double *data; };
Vector v; // v.n = ?, v.data = ?
 Làm sao ₫ể ngay sau khi ₫ượctạora, ₫ốitượng có trạng thái

ban ₫ầutheoý muốncủachương trình?
X x = {1, 2}; // Error! cannot access private members
 Làm sao ₫ể tạomột ₫ốitượng là bảnsaocủamột ₫ốitượng có
kiểu khác?
class Y { int c, d; };
Y y = x; // Error, X and Y are not the same type,
// they are not compatible
6
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Vấn ₫ề 2: Quảnlýtàinguyên
 Đốivớicác₫ốitượng sử dụng bộ nhớ₫ộng, việccấpphátvàgiải
phóng bộ nhớ₫ộng nên thựchiệnnhư thế nào cho an toàn?
class Vector {
int nelem;
double *data;
public:
void create(int n) { data = new double[nelem=n];}
void destroy() { delete[] data; nlem = 0; }
void putElem(int i, double d) { data[i] = d; }
};
Vector v1, v2;
v1.create(5);
// forget to call create for v2
v2.putElem(1,2.5); // BIG problem!
// forget to call destroy for v1, also a BIG problem
 Vấn ₫ề tương tự xảyrakhisử dụng tệptin, cổng truyềnthông,
và các tài nguyên khác trong máy tính
7

© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Giảiphápchung: Hàmtạovàhàmhủy
 Mộthàmtạo luôn ₫ượctự₫ộng gọimỗikhi₫ốitượng ₫ượctạo,
hàm hủyluôn₫ượcgọimỗi khi ₫ốitượng bị hủy:
class X { int a,b;
public:
X() { a = b = 0; } // constructor (1)
X(int s, int t) { a = s; b = t;} // constructor (2)
~X() {} // destructor
};
void f(X x1) {
if ( ) {
X x2(1,2);
X x3(x2);

}
}
X *px1 = new X(1,2), *px2 = new X;
delete px1; delete px2;
Gọihàmtạo(1) khôngtham
số (hàm tạomặc ₫ịnh)
Gọihàmtạo(2)
Gọihàmhủy
cho x2, x3
Gọihàm
hủychox1
Gọihàmhủycho*px1 và *px2
Gọihàmtạobảnsao

8
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
 Hàm tạolàcơ hội ₫ể khởitạovàcấp phát tài nguyên
 Hàm hủylàcơ hội ₫ể giải phóng tài nguyên ₫ãcấpphát
 Mộtlớpcóthể có nhiềuhàmtạo (khác nhau ở số lượng các
tham số hoặckiểu các tham số)
 Mặc ₫ịnh, compiler tự₫ộng sinh ra mộthàmtạo không
tham số và mộthàmtạobảnsao
— Thông thường, mã thực thi hàm tạomặc ₫ịnh do compiler sinh
ra là rỗng
— Thông thường, mã thực thi hàm tạobản sao do compiler sinh
ra sao chép dữ liệucủa ₫ốitượng theo từng bit
—Khixâydựng mộtlớp, nếucầncóthể bổ sung các hàm tạomặc
₫ịnh, hàm tạobảnsaovàcáchàmtạokháctheoý muốn
 Mỗilớpcóchínhxácmộthàmhủy, nếuhàmhủy không
₫ược ₫ịnh nghĩathìcompiler sẽ tự sinh ra mộthàmhủy:
— Thông thường, mã hàm hủy do compiler tạoralàrỗng
—Khicầncóthể₫ịnh nghĩahàmhủy ₫ể thực thi mã theo ý muốn
6.2 Xây dựng các hàm tạovàhàmhủy
9
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Ví dụ: LớpTime cảitiến
class Time {
int hour, min, sec;
public:
Time() : hour(0), min(0), sec(0) {}

Time(int h, int m=0, int s=0) { setTime(h,m,s); }
Time(const Time& t)
: hour(t.hour),min(t.min),sec(t.sec) {}

};
void main() {
Time t1; // 0, 0, 0
Time t2(1,1,1); // 1, 1, 1
Time t3(1,1); // 1, 1, 0
Time t4(1); // 1, 0, 0
Time t5(t1); // 0, 0, 0
Time t6=t2; // 1, 1, 1
Time* pt1 = new Time(1,1); // 1, 1, 0

delete pt1;
}
Hàm tạobảnsaovà
hàm hủythựcra
không cần ₫ịnh
nghĩacholớpnày!
10
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Ví dụ: Lớp Vector cảitiến
 Yêu cầutừ ngườisử dụng:
— Khai báo ₫ơngiảnnhư vớicáckiểucơ bản
— An toàn, ngườisử dụng không phảigọicáchàmcấpphátvàgiải
phóng bộ nhớ
 Ví dụ mã sử dụng:

Vector v1; // v1 has 0 elements
Vector v2(5,0); // v2 has 5 elements init. with 0
Vector v3=v2; // v3 is a copy of v2
Vector v4(v3); // the same as above
Vector f(Vector b) {
double a[] = {1, 2, 3, 4};
Vector v(4, a);

return v;
}
// Do not care about memory management
11
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Phiên bảnthứ nhất
class Vector {
int nelem;
double* data;
public:
Vector() : nelem(0), data(0) {}
Vector(int n, double d =0.0);
Vector(int n, double *array);
Vector(const Vector&);
~Vector();
int size() const { return nelem; }
double getElem(int i) const { return data[i];}
void putElem(int i, double d) { data[i] = d; }
private:
void create(int n) { data = new double[nelem=n]; }

void destroy() { if (data != 0) delete [] data; }
};
Các hàm thành viên
const không cho phép
thay ₫ổibiến thành
viên của ₫ốitượng!
12
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Hàm tạo: cấp phát tài nguyên và khởitạo
Hàm hủy: dọndẹp, giảiphóngtàinguyên
Vector::Vector(int n, double d) {
create(n);
while (n > 0)
data[n] = d;
}
Vector::Vector(int n, double* p) {
create(n);
while (n > 0)
data[n] = p[n];
}
Vector::~Vector() {
destroy();
}
13
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Trường hợp ₫ặcbiệt: Hàm tạobảnsao

 Hàm tạobảnsao₫ượcgọi khi sao chép ₫ốitượng:
— Khi khai báo các biến x2-x4 như sau:
X x1;
X x2(x1);
X x3 = x1;
X x4 = X(x1);
— Khi truyềnthamsố qua giá trị cho mộthàm, hoặckhimộthàmtrả
về một ₫ốitượng
void f(X x) { }
X g( ) {
X x1;
f(x1);

return x1;
}
14
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Cú pháp chuẩnchohàm tạobảnsao?
class X {
int a, b;
public:
X() : a(0), b(0) {}
X(X x); // (1)
X(const X x); // (2)
X(X& x); // (3)
X(const X& x); // (4)

};

void main() {
X x1;
X x2(x1);

}
?
(1) Truyềnthamsố qua giá trị
yêu cầu sao chép x1 sang x!!!
(2) Như (1)
(3) Không sao chép tham số,
nhưng x có thể bị vô tình thay
₫ổitronghàm
(4) Không sao chép tham số, an
toàn cho bản chính => cú pháp
chuẩn!
15
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Khi nào cần ₫ịnh nghĩahàmtạobảnsao?
 Khi nào hàm tạobảnsaomặc ₫ịnh không ₫áp ứng ₫ượcyêucầu.
 Ví dụ, nếuhàmtạobảnsaokhông₫ược ₫ịnh nghĩa, mã do
compiler tự₫ộng tạoracholớpVector sẽ có dạng:
Vector::Vector(const Vector& b)
: nelem(b.nelem), data(b.data) {}
 Vấn ₫ề: Sao chép con trỏ thuần túy, hai ₫ốitượng cùng sử dụng
chung bộ nhớ phầntử
Vector a(5);
Vector b(a);
 Trường hợpnày, phải ₫ịnh nghĩalạinhư sau:

Vector::Vector(const Vector& a) {
create(a.nelem);
for (int i=0; i < nelem; ++i)
data[i] = a.data[i];
}
0 0 0 0 0
a.nelem : 5
a.data
b.nelem : 5
b.data
16
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Mộtsố₫iểmcầnlưuý
 Nhiềuhàmtạonhưng chỉ có mộthàmhủy=> hàmhủyphải
nhấtquánvớitấtcả hàm tạo
—Trongvídụ lớp Vector, có hàm tạocấpphátbộ nhớ, nhưng hàm tạo
mặc ₫ịnh thì không => hàm hủycầnphânbiệtrõcáctrường hợp
 Khi nào hàm tạocócấp phát chiếmdụng tài nguyên thì cũng
cần ₫ịnh nghĩalạihàmhủy
 Trong mộtlớpmàcó₫ịnh nghĩahàmhủythìgầnnhư chắcchắn
cũng phải ₫ịnh nghĩahàmtạobảnsao(nếunhư cho phép sao
chép)
 Mộtlớpcóthể cấmsaochépbằng cách khai báo hàm tạobản
sao trong phần private, ví dụ:
class Y { int a, b; Y(const&);
};
void main() { Y y1;
Y y2=y1; // error!

}
17
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
 Mộttrongnhững kỹ thuậtlập trình hay nhấtcủaC++
 Chophépápdụng các phép toán vớisố phứchoặcvớivector sử
dụng toán tử +, -, *, / tương tự như vớicácsố thực. Ví dụ:
class Complex {
double re, im;
public:
Complex(double r = 0, double i =0): re(r),im(i) {}

};
Complex z1(1,1), z2(2,2);
Complex z = z1 + z2; // ???
 Bảnchấtcủavấn ₫ề? Dòng mã cuối cùng thựcracóthể viết:
Complex z = z1.operator+(z2);
hoặc
Complex z = operator+(z1,z2);
6.3 Nạpchồng toán tử
Hàm toán tử có thể thực
hiện là hàm thành viên
hoặc hàm phi thành viên
18
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Ví dụ: bổ sung các phép toán số phức
class Complex {

double re, im;
public:
Complex(double r = 0, double i =0): re(r),im(i) {}
double real() const { return re; }
double imag() const { return im; }
Complex operator+(const Complex& b) const {
Complex z(re+b.re, im+b.im);
return z;
}
Complex operator-(const Complex& b) const {
return Complex(re-b.re,im-b.im);
}
Complex operator*(const Complex&) const;
Complex operator/(const Complex&) const;
Complex& operator +=(const Complex&);
Complex& operator -=(const Complex&);

};
19
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
#include “mycomplex.h”
Complex Complex::operator*(const Complex& b) const {
// left for exercise!
}
Complex Complex::operator/(const Complex& b) const {
// left for exercise!
}
Complex& Complex::operator +=(const Complex& b) {

re += b.re; im += b.im;
return *this;
}
Complex& operator -=(const Complex&) { }
bool operator==(const Complex& a, const Complex& b) {
return a.real() == b.real() && a.imag() == b.imag();
}
void main() {
Complex a(1,1), b(1,2);
Complex c = a+b;
a = c += b; // a.operator=(c.operator+=(b));
if (c == a) { }
}
return ?
20
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Các toán tử nào có thể nạpchồng?
 Hầuhết các toán tử có trong C++, ví dụ
— Các toán tử số học: ++ + - * / % += -=
— Các toán tử logic, logic bit: && || ! & &= | |=
— Các toán tử so sánh: == != > < >= <=
— Các toán tử thao tác bit: << >> >>= <<=
— Các toán tử khác: [] () -> * ,
 Chỉ có 4 toán tử không nạpchồng ₫ược:
—Toántử truy nhậpphạmvi (dấuhaichấm ₫úp) ::
—Toántử truy nhập thành viên cấutrúc(dấuchấm) .
—Toántử gọihàmthànhviênqua con trỏ *->
—Toántử₫iềukiện ? :

21
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Mộtsố qui ₫ịnh
 Có thể thay ₫ổingữ nghĩacủamộttoántử cho các kiểumới,
nhưng không thay ₫ổi ₫ượccúpháp(vídụ số ngôi, trình tựưu
tiên thựchiện, )
 Trong mộtphéptoán₫ịnh nghĩalại, phảicóítnhấtmộttoán
hạng có kiểumới (struct, union hoặc class) => không ₫ịnh nghĩa
l
ạichocáckiểudữ liệucơ bảnvàkiểudẫnxuấttrựctiếp ₫ược!
—Vídụ không thể₫ịnh nghĩalạitoántử ^ là phép tính lũythừacho
các kiểusố họccơ bản (int, float, double, )
 Chỉ nạpchồng ₫ược các toán tử có sẵn, không ₫ưathêm₫ược
các toán tử mới
—Vídụ không thể bổ sung ký hiệutoántử ** cho phép toán lũythừa
 Nạpchồng toán tử thựcchấtlànạpchồng tên hàm => cầnlưuý
các qui ₫ịnh về nạpchồng tên hàm
 Đasố hàm toán tử có thể nạpchồng hoặc dướidạng hàm
thành viên, hoặc dướidạng hàm phi thành viên
 Mộtsố toán tử chỉ có thể nạpchồng bằng hàm thành viên
 Mộtsố toán tử chỉ nên nạpchồng bằng hàm phi thành viên
22
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Nạpchồng toán tử []
 Yêu cầu: truy nhập các phầntử củamột ₫ốitượng thuộclớp
Vector vớitoántử [] giống như₫ốivớimộtmảng

Vector v(5,1.0);
double d = v[0]; // double d = v.operator[](0);
v[1] = d + 2.0; // v.operator[](1) = d + 2.0;
const Vector vc(5,1.0);
d = vc[1]; // d = operator[](1);
 Giảipháp
class Vector {

public:
double operator[](int i) const { return data[i]; }
double& operator[](int i) { return data[i]; }

};
23
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Nạpchồng toán tử gán (=)
 Giống như hàm tạobảnsao, hàmtoántử gán ₫ược compiler tự
₫ộng bổ sung vào mỗilớp ₫ốitượng => mã hàm thựchiệngán
từng bit dữ liệu
 Cú pháp chuẩncủahàmtoántử gán cho mộtlớpX tương tự cú
pháp các phép tính và gán:
X& operator=(const X&);
 Khi nào cần ₫ịnh nghĩalạihàmtạobảnsaothìcũng cần(và
cũng mớinên) ₫ịnh nghĩalại hàm toán tử gán
 Ví dụ, nếuhàmtoántử gán không ₫ược ₫ịnh nghĩa, mã do
compiler tự₫ộng tạoracholớpVector sẽ có dạng:
Vector& Vector::operator=(const Vector& b) {
nelem = b.nelem;

data = b.data
return *this;
}
24
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
 Vấn ₫ề tương tự như hàm tạobảnsaomặc ₫ịnh, thậmchícòn
tồitệ hơn

{
Vector a(5), b(3), c;
b = a;
c = a;
} // calling destructor for a, b and c causes
// 3 times calling of delete[] operator for the
// same memory space
0 0 0 0 0
a.nelem : 5
a.data
b.nelem : 5
b.data
0 0 0
c.nelem : 5
c.data
25
© 2004, HOÀNG MINH SƠN
Chương 6: Lớpvàđốitượng II
© 2005 - HMS
Nạpchồng toán tử gán cho lớp Vector

Vector& Vector::operator=(const Vector& b) {
if (nelem != b.nelem) {
destroy();
create(b.nelem);
}
for (int i=0; i < nelem; ++i)
data[i] = b.data[i];
return *this;
}

×