© 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
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;
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
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) {}
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]; }
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)
};
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!
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) {
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
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