Bài giảng Kỹ thuật lập trình - Chương 6: Lớp và đối tượng II
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 II", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
Tài liệu đính kèm:
- bai_giang_ky_thuat_lap_trinh_chuong_6_lop_va_doi_tuong_ii.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 II
- Kỹ thuật lập trình ng 1 ươ 010101010101010110000101010101010101011000010101010101010101100001 Chương 6: LớpvàStateController010101010010101010010101010101001010101001010101010100101010100101₫ốitượng II start() 101001100011001001001010100110001100100100101010011000110010010010 Ch stop() 110010110010001000001011001011001000100000101100101100100010000010 010101010101010110000101010101010101011000010101010101010101100001 010101010010101010010101010101001010101001010101010100101010100101 N Ơ 101001100011001001001010100110001100100100101010011000110010010010y = A*x + B*u; 110010110010001000001011001011001000100000101100101100100010000010x = C*x + d*u; LQGController010101010101010110000101010101010101011000010101010101010101100001 start() 010101010010101010010101010101001010101001010101010100101010100101 stop() 101001100011001001001010100110001100100100101010011000110010010010 110010110010001000001011001011001000100000101100101100100010000010 10/6/2005 2004, HOÀNG MINH S ©
- 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) N Ơ Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 2 © 2005 - HMS ©
- 6.1 Tạovàhủy ₫ốitượ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 ( ) { N Ơ X x2; Đốitượng ₫ượctạo ra trong ngănxếp Thời ₫iểmbộ nhớ cho x2 ₫ượcgiải phóng } } Thời ₫iểmbộ nhớ cho x1 ₫ượcgiải phóng X x; Đốitượng ₫ượctạo ra trong vùng dữ liệuchương trình Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 3 © 2005 - HMS ©
- Tạo/hủy ₫ối tượng ₫ộng bằng toán tử new và delete: X* pX = 0; void f( ) { if ( ) { Đốitượng ₫ượctạora pX = new X; trong vùng nhớ tự do } } void g( ) { if (pX != 0) { Bộ nhớ của ₫ốitượng trong delete pX; heap ₫ượcgiải phóng N Ơ } } Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 4 © 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ó N Ơ 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 Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 5 © 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 N Ơ 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 Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 6 © 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) { Gọihàmtạo(1) khôngtham if ( ) { số (hàm tạomặc ₫ịnh) X x2(1,2); Gọihàmtạo(2) X x3(x2); Gọihàm hủychox1N Gọihàmhủy Ơ } } cho x2, x3 Gọihàmtạobảnsao X *px1 = new X(1,2), *px2 = new X; delete px1; delete px2; Gọihàmhủycho*px1 và *px2 Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 7 © 2005 - HMS ©
- 6.2 Xây dựng các hàm tạovàhàmhủy 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 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 Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 8 © 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 Hàm tạobảnsaovà Time t2(1,1,1); // 1, 1, 1 hàm hủythựcra Time t3(1,1); // 1, 1, 0 không cần ₫ịnh Time t4(1); // 1, 0, 0 nghĩacholớpnày! N Ơ Time t5(t1); // 0, 0, 0 Time t6=t2; // 1, 1, 1 Time* pt1 = new Time(1,1); // 1, 1, 0 delete pt1; } Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 9 © 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); N Ơ return v; } // Do not care about memory management Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 10 © 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); 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; } N private:Ơ void create(int n) { data = new double[nelem=n]; } void destroy() { if (data != 0) delete [] data; } }; Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 11 © 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]; N Ơ } Vector::~Vector() { destroy(); } Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 12 © 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; N f(x1); Ơ return x1; } Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 13 © 2005 - HMS ©
- 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() { N Ơ toàn cho bản chính => cú pháp X x1; chuẩn! X x2(x1); } Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 14 © 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); 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: N Ơ Vector::Vector(const Vector& a) { create(a.nelem); for (int i=0; i < nelem; ++i) data[i] = a.data[i]; } Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 15 © 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ụ: N class Y { int a, b; Y(const&); Ơ }; void main() { Y y1; Y y2=y1; // error! } Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 16 © 2005 - HMS ©
- 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; // ??? N Ơ 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 Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 17 © 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); } N Complex operator*(const Complex&) const; Ơ Complex operator/(const Complex&) const; Complex& operator +=(const Complex&); Complex& operator -=(const Complex&); }; Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 18 © 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() { N Ơ Complex a(1,1), b(1,2); Complex c = a+b; a = c += b; // a.operator=(c.operator+=(b)); if (c == a) { } } return ? Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 19 © 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: == != > = > >>= * , 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 ? : N Ơ Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 20 © 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ý N 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 Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 21 © 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: N Ơ double operator[](int i) const { return data[i]; } double& operator[](int i) { return data[i]; } }; Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 22 © 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) { N Ơ nelem = b.nelem; data = b.data return *this; } Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 23 © 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 a.nelem : 5 b.nelem : 5 c.nelem : 5 a.data b.data c.data N Ơ 0 0 0 0 0 0 0 0 Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 24 © 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; } N Ơ Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 25 © 2005 - HMS ©
- 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(); N Ơ } bool operator==(const Complex& a, const Complex& b) { return a.re == b.re && a.im == b.im; } Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 26 © 2005 - HMS ©
- 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. N Ơ Ch2004, HOÀNG MINH S ương 6: Lớpvàđốitượng II 27 © 2005 - HMS ©