Kĩ thuật lập trình - Chương 6: Lớp và ₫ối tượng II

pdf 27 trang vanle 2490
Bạn đang xem 20 trang mẫu của tài liệu "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:

  • pdfki_thuat_lap_trinh_chuong_6_lop_va_oi_tuong_ii.pdf

Nội dung text: Kĩ thuật lập trình - Chương 6: Lớp và ₫ối tượng II

  1. 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 ©
  2. 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 ©
  3. 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 ©
  4. ƒ 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 ©
  5. 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 ©
  6. 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 ©
  7. 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 ©
  8. 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 ©
  9. 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 ©
  10. 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 ©
  11. 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 ©
  12. 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 ©
  13. 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 ©
  14. 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 ©
  15. 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 ©
  16. 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 ©
  17. 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 ©
  18. 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 ©
  19. #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 ©
  20. 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 ©
  21. 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 ©
  22. 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 ©
  23. 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 ©
  24. ƒ 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 ©
  25. 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 ©
  26. 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 ©
  27. 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 ©