Bài giảng Kỹ thuật lập trình - Chương 6: Lớp và đối tượng I
6.1 Tạo và hủy ₫ối tượng
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;
Đối tượng ₫ược tạo ra trong ngăn xếp
Đối tượng ₫ược tạo ra trong vùng dữ liệu chương trình
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;
Đối tượng ₫ược tạo ra trong ngăn xếp
Đối tượng ₫ược tạo ra trong vùng dữ liệu chương trình
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Kỹ thuật lập trình - Chương 6: Lớp và đối tượng I", để tải tài liệu gốc về máy hãy click vào nút Download ở trên.
File đính kèm:
- bai_giang_ky_thuat_lap_trinh_chuong_6_lop_va_doi_tuong_i.pdf
Nội dung text: Bài giảng Kỹ thuật lập trình - Chương 6: Lớp và đối tượng I
- 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); Các hàm thành viên Vector(int n, double *array); const không cho phép Vector(const Vector&); thay ₫ổibiến thành ~Vector(); viên của ₫ốitượng! 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; } }; Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 11
- Hàm tạo: cấp phát tài nguyên và khởitạo Hàm hủy: dọndẹp, giải phóng tài nguyê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(); } Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 12
- 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; } Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 13
- Cú pháp chuẩnchohàm tạobảnsao? class X { int a, b; public: (1) Truyềnthamsố qua giá trị X() : a(0), b(0) {} yêu cầu sao chép x1 sang x!!! X(X x); // (1) (2) Như (1) X(const X x); // (2) ? X(X& x); // (3) (3) Không sao chép tham số, X(const X& x); // (4) nhưng x có thể bị vô tình thay ₫ổitronghàm }; (4) Không sao chép tham số, an void main() { toàn cho bản chính => cú pháp X x1; chuẩn! X x2(x1); } Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 14
- 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); a.nelem : 5 b.nelem : 5 Vector b(a); a.data b.data 0 0 0 0 0 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]; } Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 15
- 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! } Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 16
- 6.3 Nạpchồng toán tử 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); Hàm toán tử có thể thực hoặc hiện là hàm thành viên Complex z = operator+(z1,z2); hoặc hàm phi thành viên Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 17
- 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&); }; Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 18
- #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 ? Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 19
- Các toán tử nàocó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: == != > = > >>= * , 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 ? : Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 20
- 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 Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 21
- 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]; } }; Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 22
- 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; } Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 23
- 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 a.nelem : 5 b.nelem : 5 c.nelem : 5 a.data b.data c.data 0 0 0 0 0 0 0 0 Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 24
- Nạpchồng toán tử gán cho lớpVector 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; } Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 25
- 6.4 Khai báo friend Vấn ₫ề: Mộtsố hàm phi thành viên thựchiện bên ngoài, hoặc hàm thành viên củamộtlớp khác không truy nhập ₫ượctrực tiếpvàobiếnriêngcủamột ₫ốitượng => thực thi kém hiệuquả Giải pháp: Cho phép mộtlớp khai báo friend, có thể là mộthàm phi thành viên, một hàm thành viên củamộtlớpkhác, hoặccả mộtlớpkhác Ví dụ class Complex { friend bool operator==(const Complex&,const Complex&); friend class ComplexVector; friend ComplexVector Matrix::eigenvalues(); } bool operator==(const Complex& a, const Complex& b) { return a.re == b.re && a.im == b.im; } Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 26
- Bài tậpvề nhà Hoàn chỉnh lớpVector vớinhững phép toán cộng, trừ, nhân/chia vớisố vô hướng, nhân vô hướng và so sánh bằng nhau Dựatrêncấu trúc List và các hàm liên quan ₫ãthựchiệntrong chương 4, hãy xây dựng lớp ₫ốitượng List với các hàm thành viên cầnthiết. Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 27