Giáo trình Kĩ thuật lập trình - Chương 6: Lớp và đối tượng 2
Bạn đang xem 20 trang mẫu của tài liệu "Giáo trình Kĩ thuật lập trình - Chương 6: Lớp và đối tượng 2", để 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:
- giao_trinh_ki_thuat_lap_trinh_chuong_6_lop_va_doi_tuong_2.pdf
Nội dung text: Giáo trình Kĩ thuật lập trình - Chương 6: Lớp và đối tượng 2
- Kỹ thuật lập trình ng 1 Chương 6: Lớpvà₫ốitượng II 0101010101010101100001 ươ 01010101010101011000010101010101010101100001 StateController010101010010101010010101010101001010101001010101010100101010100101 start() 101001100011001001001010100110001100100100101010011000110010010010 Ch stop() 110010110010001000001011001011001000100000101100101100100010000010 010101010101010110000101010101010101011000010101010101010101100001 010101010010101010010101010101001010101001010101010100101010100101 101001100011001001001010100110001100100100101010011000110010010010y = A*x + B*u; 110010110010001000001011001011001000100000101100101100100010000010x = C*x + d*u; LQGController010101010101010110000101010101010101011000010101010101010101100001 start() 010101010010101010010101010101001010101001010101010100101010100101 stop() 101001100011001001001010100110001100100100101010011000110010010010 110010110010001000001011001011001000100000101100101100100010000010 10/30/2007
- 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) Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 2
- 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 ( ) { 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 Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 3
- 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 } } Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 4
- 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 Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 5
- Vấn ₫ề 2: Quản lý tài nguyê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 Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 6
- Giải pháp chung: Hàm tạ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ủychox1 } 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 Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 7
- 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ảiphóngtàinguyê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 Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 8
- 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! Time t5(t1); // 0, 0, 0 Time t6=t2; // 1, 1, 1 Time* pt1 = new Time(1,1); // 1, 1, 0 delete pt1; } Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 9
- Ví dụ: LớpVector 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 Chương 6: Lớpvàđốitượng II © 2007 AC - HUT 10
- 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