Bài giảng Lập trình - Chương 4: Lớp và đối tượng

Khái niệm

 Đối tượng là gì?

– Mô hình đại diện của một đối tượng vật lý:

 Person, student, employee, employer

 Car, bus, vehicle,

– Đối tượng logic

 Trend, report, button, window,

 Một đối tượng có:

– Các thuộc tính

– Trạng thái

– Hành vi

– Căn cước

– Ngữ nghĩa

Lớp là gì?

 Là sự thực thi của các đối tượng có chung các thuộc

tính, hành vi, quan hệ, ngữ nghĩa.

 Lớp là một kiểu dữ liệu mới có cấu trúc, trong đó việc

truy nhập các biến thành viên được kiểm soát thông

qua các hàm thành viên.

 Các dữ liệu của lớp  biến thành viên

 Các hàm của lớp  hàm thành viên

 Một biến của một lớp  một đối tượng

 

Bài giảng Lập trình - Chương 4: Lớp và đối tượng trang 1

Trang 1

Bài giảng Lập trình - Chương 4: Lớp và đối tượng trang 2

Trang 2

Bài giảng Lập trình - Chương 4: Lớp và đối tượng trang 3

Trang 3

Bài giảng Lập trình - Chương 4: Lớp và đối tượng trang 4

Trang 4

Bài giảng Lập trình - Chương 4: Lớp và đối tượng trang 5

Trang 5

Bài giảng Lập trình - Chương 4: Lớp và đối tượng trang 6

Trang 6

Bài giảng Lập trình - Chương 4: Lớp và đối tượng trang 7

Trang 7

Bài giảng Lập trình - Chương 4: Lớp và đối tượng trang 8

Trang 8

Bài giảng Lập trình - Chương 4: Lớp và đối tượng trang 9

Trang 9

Bài giảng Lập trình - Chương 4: Lớp và đối tượng trang 10

Trang 10

Tải về để xem bản đầy đủ

pdf 47 trang duykhanh 5360
Bạn đang xem 10 trang mẫu của tài liệu "Bài giảng Lập trình - Chương 4: Lớp và đối tượng", để tải tài liệu gốc về máy hãy click vào nút Download ở trên

Tóm tắt nội dung tài liệu: Bài giảng Lập trình - Chương 4: Lớp và đối tượng

Bài giảng Lập trình - Chương 4: Lớp và đối tượng
trạng thái ban đầu theo ý muốn của người sử dụng?
 . Giải pháp: sử dụng hàm tạo
Chương 4: Lớp và đối tượng 17
 4.7 Hàm tạo và hàm hủy ()
 . Vấn đề 2: đối tượng sử dụng bộ nhớ động
 class Array{
 int n; // số phần tử của array
 int *data; // mảng chứa giá trị các phần tử
 public:
 ...
 };
 . Câu hỏi: làm thế nào để cấp phát bộ nhớ và hủy bộ 
 nhớ cho biến thành viên data một cách an toàn
 . Giải pháp: sử dụng hàm tạo và hàm hủy.
Chương 4: Lớp và đối tượng 18
 4.7 Hàm tạo và hàm hủy ()
 . Hàm tạo: luôn được gọi khi đối tượng được tạo ra
 . Hàm hủy: luôn được gọi khi đối tượng bị hủy
 . Cú pháp:
 class A{
 int a, b;
 public:
 A(){a = 0; b = 0;} 1
 A(int _a){ a = _a;} 2
 A(int _a, int _b){ a = _a; b = _b;} 3
 ~A();
 };
 . Một lớp có thể có nhiều hàm tạo
 – Hàm tạo 1: hàm tạo không đối
 – Hàm tạo 2: hàm tạo một đối
 – Hàm tạo 3: hàm tạo hai đối
 . Một lớp chỉ có duy nhất một hàm hủy
Chương 4: Lớp và đối tượng 19
 . Sử dụng
 void main{
 A a(); //gọi hàm tạo (?)
 A a1(10); // gọi hàm tạo (?)
 A a2(1,2); // gọi hàm tạo (?)
 } //gọi hàm hủy cho ?
 void f(A a){ gọi hàm tạo (?)
 A b(0,0); 
 if(...){ gọi hàm tạo (?)
 A c;
 ... gọi hàm hủy cho ?
 }
 } gọi hàm hủy cho ?
 void main{
 A *a = new A(10); //gọi hàm tạo (?)
 . . . //sử dụng
 delete a; //gọi hàm hủy
 }
Chương 4: Lớp và đối tượng 20
 4.7 Hàm tạo và hàm hủy ()
 . Làm thế nào để không phải định nghĩa nhiều hàm 
 tạo như ví dụ trên?
 . Giải pháp: sử dụng hàm tạo có tham biến mặc định 
 một lớp chỉ cần một hàm tạo duy nhất
 class A{
 int a, b;
 public:
 A(int _a = 0, int _b = 0) { a = _a; b = _b;} 
 . . .
 };
 void main{
 A a1; // a1.a = ?; a1.b = ?
 A a2(1); // a2.a = ?; a2.b = ?
 A a3(1,2); // a3.a = ?; a3.b = ?
 };
Chương 4: Lớp và đối tượng 21
 Tóm tắt về hàm tạo và hàm hủy
 . Hàm tạo được sử dụng để:
 – Cấp phát bộ nhớ động 
 – Khởi tạo các trạng thái ban đầu cho đối tượng
 . Một lớp có thể có nhiều hàm tạo. Chúng khác nhau ở số lượng 
 các tham số hoặc kiểu của các tham số.
 . Nếu không định nghĩa hàm tạo thì compiler sẽ tự động sinh ra 
 một hàm tạo với mã thực thi là rỗng, dẫn đến:
 – Trạng thái ban đầu của các biến thành viên là bất định
 – Không cấp phát bộ nhớ động cho các biến thành viên dạng mảng 
 động
 . Hàm hủy là duy nhất
 . Hàm hủy không bao giờ có đối
 . Nếu không định nghĩa hàm hủy thì compiler cũng tự động sinh 
 ra nhưng mã thực thi của hàm hủy này là rỗng.
 . Khi sử dụng đối tượng động (có sử dụng toán tử new) thì luôn 
 phải nhớ hủy bộ nhớ đã cấp phát cho bộ nhớ động khi không cần 
 dùng đến chúng nữa (sử dụng toán tử delete)
 . Hàm tạo và hàm hủy có thể được định nghĩa bên ngoài phần 
 khai báo lớp.
Chương 4: Lớp và đối tượng 22
 Ví dụ về lớp Array
 //sử dụng
 //khai báo lớp Array void main(){
 class Array{ Array a(5);
 int n; Array *pa = new Array(5,1);
 int *data; ...
 delete pa;
 public: }
 Array(int _n = 0, int _d = 0);
 ~ Array();
 . . .
 }; Câu hỏi:
 1. Các giá trị của mảng data của a, pa 
 // định nghĩa hàm tạo và hàm hủy bằng bao nhiêu?
 Array :: Array(int _n, int _d){ 2. Không sử dụng delete pa có được 
 n = _n; không?
 data = new int[n]; 3. Biến a được hủy khi nào?
 for(int i = 0; i < n; i++)
 data[i] = _d;
 }
 Array ::~ Array(){
 delete [] data;
 }
Chương 4: Lớp và đối tượng 23
4.8 Hàm tạo bản sao
 . Hàm tạo bản sao được gọi khi sao chép đối tượng 
 (xem các ví dụ sau)
 . Cú pháp chuẩn:
 class A{ Sao chép tham 
 int a, b;
 số từ a1, a1 
 public:
 A(const A& a1); không bị thay 
 . . . đổi do vô tình
 };
 //định nghĩa hàm sao chép
 A::A(const A& a1){
 a = a1.a;
 b = a1.b;
 }
 //sử dụng
 void main(){
 A a; Gọi hàm tạo 
 A a1(a); bản sao
 }
Chương 4: Lớp và đối tượng 24
 . Hàm tạo bản sao được gọi khi sao chép đối tượ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ền tham số qua giá trị cho một hàm
 void f(X x) { ... }
 ở đây có sự gọi hàm tạo bản 
 void main(){
 sao để sao chép nội dung của 
 X a; a để truyền vào cho tham biến 
 f(a); hình thức x của hàm f
 }
 - Khi một hàm trả về một đối tượng
 X f() {
 X x1;
 . . . // thực hiện thuật toán
 return x1; ở đây có sự gọi hàm tạo bản sao để 
 } sao chép nội dung của biến tạm x1 
 void main(){ sang cho biến x sau khi thực hiện 
 X x = f(); xong lệnh return trong hàm f
 . . .
 }
Chương 4: Lớp và đối tượng 25
 . Nếu không định nghĩa hàm tạo bản sao thì compiler 
 sẽ tự sinh ra và sao chép từng bít
 . Lớp không có tham biến được cấp phát động thì 
 không cần định nghĩa hàm tạo bản sao
 . Khi có tham biến được cấp phát động thì bắt buộc 
 định nghĩa lại hàm tạo bản sao.
 //khai báo lớp Array không có hàm tạo bản sao
 class Array{
 int n;
 int *data;
 public:
 Array(int _n = 0; int _d = 0) {...}
 ~ Array(){...}
 void set_data(int i, int d){ 
 if((i>=0) && (i<n)) 
 data[i] = d;
 };
Chương 4: Lớp và đối tượng 26
 //sử dụng Gọi hàm tạo bản 
 void main{ sao để sao chép 
 Array a(5,0); a sang b
 Array b(a);
 ... Hỏi: a.data[0] = 
 b.set_data(0,10); ?
 };
 . Do không định nghĩa hàm tạo bản sao, nên ở đây gọi 
 hàm tạo bản sao mặc định do compiler sinh ra, hàm 
 tạo này có dạng:
 Array :: Array(const Array & a){
 n = a.n;
 data = a.data;
 }
 . Khi sử dụng Array b(a); thì mảng data của a và b là một, nên 
 khi thay đổi b thì a sẽ thay đổi theo.
 . Giải pháp: định nghĩa lại hàm tạo bản sao
Chương 4: Lớp và đối tượng 27
 4.8 Hàm tạo bản sao ()
 . Định nghĩa hàm tạo bản sao cho lớp Array như sau
 Array :: Array(const Array & a) {
 n = a.n;
 data = new int[n];
 for (int i=0; i < n; ++i)
 data[i] = a.data[i];
 } 
 . Khi một lớp phải định nghĩa hàm hủy thì cũng cần 
 thiết định nghĩa lại hàm tạo bản sao
 . Trong trường hợp muốn cấm sao chép thì ta khai báo 
 hàm tạo bản sao trong phần private.
Chương 4: Lớp và đối tượng 28
 Con trỏ this
 . Từ khóa this được dùng trong khi định nghĩa các 
 hàm thành viên dùng để trỏ đến đối tượng hiện tại
 . Nói chung, con trỏ this ít khi được sử dụng tường 
 minh, vì nó đã được ngầm sử dụng khi truy nhập vào 
 các thành phần dữ liệu. Nó thường được sử dụng khi 
 chúng ta muốn lấy địa chỉ của đối tượng hiện tại (như 
 để trỏ vào chính đối tượng đó)
Chương 4: Lớp và đối tượng 29/52
 4.9 Hàm toán tử gán
 . Nghiên cứu ví dụ 1:
 class A{
 int a, b;
 public:
 A(int _a, int _b):a(_a), b(_b){} //hàm tạo
 . . .
 };
 Gọi hàm toán tử gán (=).
 void main(){ ở đây sẽ có:
 A a(1,2);
 b.a = a.a OK
 A b; b.b = a.b
 b = a;
 }
 . Không định nghĩa hàm toán tử gán, compiler sẽ tự 
 động sinh ra và gán từng bít (giống với hàm tạo bản 
 sao)
Chương 4: Lớp và đối tượng 30
 4.9 Hàm toán tử gán()
 . Nghiên cứu ví dụ 2:
 class Array{
 int n;
 int *data;
 public:
 Array(int _n = 0; int _d = 0) {...}
 ~ Array(){...}
 ...
 };
 Gọi hàm toán tử gán (=).
 Void main() {
 ở đây sẽ có:
 Array a(5,1);
 a1.n = a.n
 Array a1;
 a1.data = a.data
 a1 = a;
 }
 . a1.data và a.data cùng trỏ vào một vùng nhớ kết quả tương 
 tự với trong trường hợp hàm tạo bản sao ở trên.
 . Trong trường hợp này cần định nghĩa hàm toán tử gán
Chương 4: Lớp và đối tượng 31
 4.9 Hàm toán tử gán()
 . Cú pháp chuẩn hàm toán tử gán:
 class A{
 ...
 public:
 A& operator=(const A&); //khái báo hàm toán tử gán
 . . .
 };
 //định nghĩa hàm toán tử gán:
 A& A::operator=(const A& a1){
 ...//mã gán các biến thành viên
 }
Chương 4: Lớp và đối tượng 32
 4.9 Hàm toán tử gán()
 . Ví dụ định nghĩa hàm toán tử gán cho lớp Array
 class Array{
 int n;
 int *data;
 public:
 Array(int _n = 0; int _d = 0) {...}
 ~Array(){...}
 Array& operator=(const Array& a)
 };
 Array& Array::operator=(const Array& a) {
 if (n != a.n) {
 delete [] data;
 n = a.n;
 data = new int[n];
 }
 for (int i=0; i < n; ++i)
 data[i] = a.data[i];
 return *this;
 }
Chương 4: Lớp và đối tượng 33
 Bài tập
 . Định nghĩa lớp Array có các yêu cầu sau
 – Hàm tạo, hàm hủy, hàm tạo bản sao, hàm toán tử gán
 – Các hàm cho phép nhập dữ liệu vào từ bàn phím và hiển thị 
 ra màn hình cho Array
 – Các hàm cho phép thay đổi/đọc giá trị của một phần tử nào 
 đó trong Array
 – Viết chương trình chính minh họa cách sử dụng
Chương 4: Lớp và đối tượng 34
 4.10 Thành viên tĩnh
. Biến thành viên tĩnh
. Vấn đề: Yêu cầu ghi lại số lượng các đối tượng được tạo ra từ lớp Date
 void main(){
 Date d1(1,1,2010); // count++
 Date d2 = d1; // count++
 }
. Giải pháp: đưa biến count là một biến static của lớp Date
 class Date{ Date::Date(int d, int m, int y){
 int day, month,year; day = d; month = m; year = y;
 static int count; count++;
 public: }
 Date(int d, int m, int y); Date::Date(const Date& d){
 Date(const Date& d); day = d.day; month = d.month;
 ~Date(); year = y.year; count++;
 . . . Khai báo }
 }; biến tĩnh Date::~Date(){
 int Date:: count = 0; count--;
 } Định nghĩa biến tĩnh: bắt buộc và nằm 
Chương 4: Lớp và đối tượng bên ngoài khai báo lớp và ngoài các35 hàm
 4.10 Thành viên tĩnh()
 . Hàm thành viên tĩnh Trong hàm thành viên tĩnh chỉ sử dụng 
 class A{ được các biến thành viên tĩnh và chỉ gọi 
 int n; được các hàm thành viên tĩnh khác. Nếu 
 static int count; muốn truy nhập vào các biến thành viên 
 public: của lớp thì phải khai báo một đối tượng 
 A():n(0){} trung gian, vì trong hàm static không có 
 void f(); đối tượng ngầm định *this.
 static void g();
 . . . 
 };
 int A:: count = 0;
 void A::f(){
 n++; Hàm thành viên tĩnh là hàm 
 } chung cho cả lớp không phải riêng 
 void A::g(){ cho một đối tượng nào
 n = 2; //???
 f(x); //???
 count = 2; //OK
 }
Chương 4: Lớp và đối tượng 36
 Kết luận về thành viên tĩnh
 . Được cấp phát một vùng nhớ cố định, tồn tại ngay cả 
 khi lớp chưa có một đối tượng nào
 . Chung cho cả lớp, không phải của riêng mỗi đối tượng
 . Để biểu thị thành phần tĩnh ta dùng “tên lớp :: tên 
 thành viên tĩnh” hoặc “tên đối tượng . Tên thành viên 
 tĩnh”
 . Được cấp phát bộ nhớ và khởi gán giá trị ban đầu bên 
 ngoài khai báo lớp và ngoài các hàm (kể cả hàm 
 main)
Chương 4: Lớp và đối tượng 37
 4.10 Thành viên tĩnh()
 . Xây dựng lớp HD (hóa đơn) gồm 2 dữ liệu là mshd 
 (mã số hóa đơn) và tienban với các hàm thực hiện 
 chức năng sau:
 – Hàm tạo hóa đơn
 – Hàm hủy hóa đơn
 – Hàm sửa nội dung hóa đơn (sửa tiền bán)
 – Hàm in ra tổng số hóa đơn và tổng số tiền bán sau các thao 
 tác tạo, hủy, sửa hóa đơn.
 – Viết hàm main để ứng dụng
Chương 4: Lớp và đối tượng 38
 4.11 Friend
 . Vấn đề: class A{
 int n;
 public:
 A():n(0){}
 . . . 
 };
 class B{
 int m;
 public:
 B():m(0){}
 void f(A a){ a.n = 5;}//???
 . . . 
 };
 void g(A a){
 a.n = 10; //???
 }
 . Làm thế nào để hàm phi thành viên, hàm thành viên của một 
 lớp khác có thể truy nhập trực tiếp vào biến thành viên của một 
 đối tượng?
Chương 4: Lớp và đối tượng 39
 4.11 Friend()
 . Giải pháp: khai báo bạn bè – friend
 . Cái gì có thể là friend?
 – Hàm phi thành viên định nghĩa ở bên ngoài
 – Hàm thành viên của một lớp khác
 – Cả lớp khác
 class A{ class B{
 int n; int m;
 public: public:
 A():n(0){} B():m(0){}
 friend void g(A a); void f(A a){ a.n = 5;}//OK
 friend void B::f(A a); . . . 
 friend class C; };
 . . . 
 };
 void g(A a){
 a.n = 10; //OK
 }
Chương 4: Lớp và đối tượng 40
 4.12 Nạp chồng toán tử
 . Ví dụ 1: #include 
 class Date{
 int day, month,year;
 public:
 Date(int d, int m, int y){
 day = d; month = m; year = y;
 }
 };
 void main(){
 int n = 5; Tại sao lỗi? Làm thế 
 cout<< n; //OK nào để có thể sử 
 Date d(1,1,2010); dụng được như thế?
 cout<<d; //Lỗi
 }
 . Biến n thuộc kiểu cơ sở, thư viện xuất/nhập đã hỗ trợ hàm toán 
 tử xuất ra màn hình cho các biến cơ sở đó. Biến d thuộc kiểu 
 Date do người sử dụng định nghĩa.
 . Giải pháp: định nghĩa lại toán tử xuất cho lớp Date
Chương 4: Lớp và đối tượng 41
 . Nạp chồng toán tử xuất cho lớp Date
 #include 
 class Date{
 int day, month,year;
 public:
 Date(int d=1, int m=1, int y=2010){
 day = d; month = m; year = y;
 }
 friend ostream& operator<<(ostream& os, const Date& d);
 };
 ostream& operator<<(ostream& os, const Date& d){
 os<<“Ngay: ”<<d.day<<“-”<<d.month<<“-”<<d.year<<“\n”;
 return os;
 }
 void main(){
 Date d(1,7,2010);
 cout<<d;
 }
Chương 4: Lớp và đối tượng 42
 . Ví dụ 2: Nạp chồng toán tử cho lớp số phức complex
 . Vấn đề:
 #include 
 class Complex{
 int real,imag;
 public:
 Complex(int r, int i){
 real = r; imag = i;
 }
 };
 void main(){
 Complex a(1,2), b(5,6);
 Complex c;
 c = a + b; //Lỗi
 }
 . Lý do tương tự như lớp Date ở trên
Chương 4: Lớp và đối tượng 43
 . Thực hiện nạp chồng toán tử
 #include 
 class Complex{
 int real,imag;
 public:
 Complex(int r = 0, int i =0): real(r),imag(i) {}
 Complex operator+(const Complex& b) const {
 Complex z(real + b.real, imag + b.imag);
 return z;
 }
 Complex operator-(const Complex& b) const {
 return Complex(real - b.real, imag - b.imag);
 }
 Complex operator*(const Complex&) const;
 Complex operator/(const Complex&) const;
 Complex& operator +=(const Complex&);
 Complex& operator -=(const Complex&);
 ...
 };
 . Yêu cầu: sinh viên hãy thực hiện nốt các hàm còn lại
Chương 4: Lớp và đối tượng 44
 . Nạp chồng các toán tử cho lớp complex sử dụng hàm bạn
 #include 
 class Complex{
 int real,imag;
 public:
 Complex(int r = 0, int i =0): real(r),imag(i) {}
 friend Complex operator+(const Complex&, const Complex&) const;
 friend Complex operator-(const Complex&, const Complex&) const;
 friend Complex operator*(const Complex&, const Complex&) const;
 friend Complex operator/(const Complex&, const Complex&) const;
 ...
 };
 Complex operator+(const Complex& a,const Complex& b) const{
 Complex z(a.real + b.real, a.imag + b.image);
 return z;
 }
 ...
Chương 4: Lớp và đối tượng 45
 Các phép toán có thể nạp chồng
 . Hầu hết các toán tử có trong C++
 – Các toán tử số học: ++ -- + - * / % += -= ...
 – Các toán tử logic, logic bit: && || ! & &= | |= ...
 – Các toán tử so sánh: == != > = <=
 – Các toán tử thao tác bit: > >>= <<=
 – Các toán tử khác: [] () -> * , ...
 . Các toán tử sau không nạp chồng được:
 – Toán tử truy nhập phạm vi (dấu hai chấm đúp) ::
 – Toán tử truy nhập thành viên cấu trúc (dấu chấm) .
 – Toán tử gọi hàm thành viên qua con trỏ *->
 – Toán tử điều kiện
 ? :
Chương 4: Lớp và đối tượng 46
 Bài tập
 . 1. Hãy nạp chồng các toán tử +,-,*, +=, -=, *=, ==, != 
 sử dụng hàm thành viên, toán tử nhập/xuất cho lớp
 số complex sử dụng hàm bạn
 . 2. Bổ sung các yêu cầu sau vào lớp Array
 – Các hàm nạp chồng toán tử [] (để gán hoặc lấy giá trị của
 một phần tử), 
 – Nạp chồng toán tử +, -, * hai array, hoặc array với một số
 – Định nghĩa toán tử nhập, xuất một array
 – Định nghĩa toán tử gọi hàm () để lấy ra giá trị max của
 mảng.
Chương 4: Lớp và đối tượng 47

File đính kèm:

  • pdfbai_giang_lap_trinh_chuong_4_lop_va_doi_tuong.pdf