Lập trình hướng đối tượng trong C++

Chương trình viết bằng các ngôn ngữ hướng thủ tuc (C, Pascal.) bao gồm một chuỗi các câu lệnh nhằm yêu cầu máy tính thực hiện một nhiệm vụ nào đó. Chia chương trình thành các hàm. Mỗi hàm phục vụ cho một nhiệm vụ cụ thể và có giao diện (interface) rõ ràng. Nhóm một số các hàm lại thành các mô-đun hoặc các thành phần (component).

ppt214 trang | Chia sẻ: lylyngoc | Lượt xem: 1897 | Lượt tải: 2download
Bạn đang xem trước 20 trang tài liệu Lập trình hướng đối tượng trong C++, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG TRONG C++ Đặng Thành Trung Bộ môn CNPM – Khoa CNTT trungdt@gmail.com ………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………... Nội dung chương trình Chương 1: Giới thiệu về lập trình hướng đối tượng. Chương 2: Những vấn đề cơ bản trong C++. Chương 3: Đối tượng và lớp Chương 4: Thừa kế Chương 5: Các kiểu quan hệ Chương 6: Đa hình Chương 7: Khuôn hình Chương 8: Quản lý bộ nhớ Chương 9: Mảng Chương 10: Bắt ngoại lệ Chương 11: Stream và File Chương 12: Thiết kế hướng đối tượng Yêu cầu 2 bài kiểm tra giữa kỳ (hệ số 1) Bài tập lớn (hệ số 3) Tài liệu tham khảo The Waite’s Group’s Object-Oriented Programming in C++, 3rd edition, Robert Lafore, SAMS. C++ Programming Language, 3rd edition, Bjarne Stroustrup, Addison-Wesley Practical C++ Programming, Steve Oualline Lập trình hướng đối tượng, Phạm Văn Ất CHƯƠNG 1: GIỚI THIỆU VỀ LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG (OBJECT-ORIENTED PROGRAMMING IN C++) Nội dung chương 1 Tại sao phải lập trình hướng đối tượng Đặc điểm của lập trình hướng đối tượng C và C++ 1.Tại sao phải lập trình hướng đối tượng Chương trình viết bằng các ngôn ngữ hướng thủ tuc (C, Pascal...) bao gồm một chuỗi các câu lệnh nhằm yêu cầu máy tính thực hiện một nhiệm vụ nào đó. Chia chương trình thành các hàm. Mỗi hàm phục vụ cho một nhiệm vụ cụ thể và có giao diện (interface) rõ ràng. Nhóm một số các hàm lại thành các mô-đun hoặc các thành phần (component). Tại sao phải lập trình hướng đối tượng … Nhược điểm của ngôn ngữ lập trình hướng cấu trúc: Hàm không hạn chế truy nhập tới các biến toàn cục. Hàm và dữ liệu không có quan hệ với nhau. Không thể xây dựng những kiểu dữ liệu phức tạp. Ví dụ: Kiểu Point gồm hai tọa độ x và y. Không thể thực hiện các phép tính trên kiểu Point. 2. Đặc điểm của lập trình hướng đối tượng Ngôn ngữ lập trình hướng đối tượng kết hợp dữ liệu và các hàm thao tác trên dữ liệu này; gọi là đối tượng. Các hàm của đối tượng - gọi là các hàm thành viên (member function), cung cấp phương thức để truy nhập dữ liệu của đối tượng. Các thành phần dữ liệu thường được gọi là các thuộc tính (attribute hoặc instance variable). Việc gọi hàm thành viên của một đối tượng được xem như việc gửi thông điệp tới đối tượng đó (sending a mesage). Trong một chương trình C++ thường chứa một số các đối tượng, chúng giao tiếp với nhau thông qua việc gửi thông điệp. Mô hình hướng đối tượng Đặc điểm của ngôn ngữ lập trình hướng đối tượng Lập trình hướng đối tượng chỉ quan tâm đến việc chương trình chứa những đối tượng nào. Đối tượng là thành viên của lớp (class). Lớp là một mô tả của các đối tượng tương tự nhau. Một lớp có thể được chia thành nhiều lớp con. Một lớp có thể kế thừa từ nhiều lớp khác. Lớp gốc được gọi là lớp cơ sở (base class) Llớp thừa kế từ lớp cơ sở gọi là lớp dẫn xuất (derived class). Lập trình hướng đối tượng cho phép ta tạo ra kiểu dữ liệu mới và thực hiện các thao tác trên chúng một cách dễ dàng. 3. C và C++ C++ thừa kế từ ngôn ngữ C. Những câu lệnh trong C có thể được áp dụng trong C++. Những thành phần được bổ sung vào C để trở thành C++ bao gồm: Lớp Đối tượng Lập trình hướng đối tượng CHƯƠNG 2: NHỮNG VẤN ĐỀ CƠ BẢN TRONG C++ Nội dung chính Cấu trúc chương trình Biến Toán tử Các câu lệnh Structure Hàm 1. Cấu trúc chương trình Xét ví dụ sau: #include using namespace std; int main() { cout (i)/j; // ép i thành kiểu float trước khi chia Biến hằng (Constant Variables) Giá trị của hằng không thay đổi. Có 2 cách khai báo hằng: const float PI=3.14; #define PI 3.14 Định nghĩa ở đầu chương trình Không xác định kiểu của PI Sử dụng cout cout là một đối tượng được định nghĩa trước trong C++, tương ứng với luồng ra chuẩn (standard output stream). Toán tử > đưa nội dung từ luồng vào chuẩn vào biến. Ví dụ: int temperature; cout > temperature; Header File và Library File Một số nhiệm vụ được thực hiện bởi Library Function. Header File chứa khai báo các hàm mà ta cần sử dụng trong chương trình. Ví dụ: #include #include ”myprog.h” : yêu cầu chương trình dịch tìm từ thư mục chuẩn. ” ” : yêu cầu chương trình dịch tìm từ thư mục hiện tại. Nếu không include Header File thích hợp thì chương trình dịch sẽ báo lỗi. 3. Toán tử Toán tử toán học Toán tử quan hệ Toán tử logic Toán tử toán học +, -, *, /, % Ví dụ: int i = 1/3; // kết quả i = 0 float x = 1.0/3; // kết quả x=0.3333 int j = 7 % 3; // kết quả j=1 ++, -- Ví dụ: int i=3; int j=7; cout , =, = y) // true (x != y) // true Toán tử logic logical and : && (x >= 5) && ( x ? : Ví dụ: min = (alpha> c; } Vòng lặp while ... Vòng lặp do Trong vòng lặp do, biểu thức kiểm tra được đánh giá ở cuối vòng lặp. Thân vòng lặp được thực hiện ít nhất một lần. Ví dụ: char c; do { cout > c; } while ( c != ’y’); Vòng lặp do ... If … Else Phụ thuộc vào điều kiện kiểm tra là true hay false để quyết định nhánh thực hiện Ví dụ: int x; cout > x; if ( x > 100) cout ) { case : … break; case : … break; default : … } Lệnh switch ... char c; cout > c; switch (c) { case ’a’: cout double logn (double x, double base=10) // mặc định gán base = 10 { return log(x)/log(base); } double y=5.6; cout f; Kết quả: f của lớp Parent sẽ được viện dẫn Nếu f được khai báo là hàm ảo trong lớp Parent thì f của lớp Child sẽ được viện dẫn. Ví dụ: Không sử dụng hàm ảo class Mammal { public: void Move() {cout Move(); // “Mammal moves 1 step” } Ví dụ: Sử dụng hàm ảo class Mammal { public: void virtual Move() {cout Move(); // “Dog moves 1 step” } Hàm thuần ảo Lớp cơ sở trừu tượng là lớp cơ sở không có đối tượng nào và chỉ sử dụng để cho các lớp khác kế thừa. Hàm thuần ảo được khai báo trong lớp sẽ làm cho lớp đó trở thành lớp cơ sở trừu tượng. virtual kiểu_trả_về tên_hàm(danh sách tham số) = 0; Tất cả các lớp dẫn xuất đều phải định nghĩa hàm thuần ảo. Ví dụ: hàm thuần ảo class Mammal { public: virtual void Move() = 0; }; class Dog : public Mammal { public: void Move() {cout kind max(kind d1, kind d2) { if (d1 > d2) return (d1); return (d2); } Cấu trúc báo cho C++ biết rằng kind có thể được thay thế bởi bất kỳ kiểu nào, kể cả là class hoặc một kiểu bất kỳ. Định nghĩa khuôn hình… Ví dụ: Sử dụng template main() { float f = max(3.5, 8.7); int i = max(100, 800); char ch = max('A', 'Q'); int i2 = max(600, 200); } Khi C++ phát hiện câu lệnh: float f = max(3.5, 8.7); nó sẽ kiểm tra để xác định xem hàm max(float, float) đã có mã lệnh hay chưa và nó sẽ tự động tạo ra mã lệnh nếu chưa có. Chú ý rằng khi gặp câu lệnh int i2 = max(600, 200); thì nó sẽ không tạo ra mã lệnh cho hàm max(int,int) nữa vì nó đã tạo ra từ trước. Chuyên biệt hoá hàm Khi sử dụng hàm max để so sánh hai string như sau: main() { char *namel = "Able"; char *name2 = "Baker"; cout d2) sẽ so sánh giá trị của con trỏ chứ không phải nội dụng của con trỏ. Mà ta muốn C++ vẫn sử dụng cách so sánh với những dữ liệu thông thường, riêng đối với string thì phải sử dụng strcmp. Chuyên biệt hoá hàm … Thực hiện quá trình chuyên biết hoá (specialization). Ta định nghĩa lại hàm max chỉ dùng cho kiểu string. char *max(char *dl, char *d2) { if (strcmp(dl, d2) #include const int STACK_SIZE = 100; // số lượng phần tử lớn nhất trong Stack template class stack { private: int count; // Số lượng phần tử của Stack kind data[STACK_SIZE]; // Mảng chứa các phần tử của Stack public: stack (void) {count = 0; // Stack ban đầu rỗng} void push(const kind item) { data[count] = item; ++count; } kind pop(void) { --count; return (data[count]); } }; Khuôn hình lớp … Nếu ta khai báo: stack a_stack; // câu lệnh không hợp lệ Stack là một template chung. Khi C++ nhìn thấy lệnh khai báo này, nó sẽ hỏi “stack nào?”. Phải xác định một kiểu dữ liệu cụ thể được lưu trong stack. Phải khai báo: stack a_stack; // Các phần tử trong stack là kiểu số nguyên Sau đó, ta có thể sử dụng stack như sau: a_stack.push(l); x = a_stack.pop(); Khuôn hình lớp … Định nghĩa các hàm thành phần bên ngoài định nghĩa lớp như sau: template inline void stack::push(const kind item) { data[count] = item; ++count; } Chuyên biệt hoá lớp template stack { ...} Giới thiệu cho C++ biết cách tạo ra một tập các lớp có tên là stack, stack, stack,… C++ sẽ tự động tạo ra các hàm thành viên như: stack: :push, stack: :push, và stack::push. Nếu khai báo một hàm thành viên một cách tường minh thì C++ sẽ sử dụng định nghĩa này trước. inline void stack::push(const char * item) { data[count] = strdup(item); ++count; } Từ khoá template nói cho C++ biết ”Đây là class chung, hãy tạo ra một phiên bản tương ứng với nó”. Nếu không có từ khoá template, C++ sẽ hiểu đây là hàm chính thức và phải sử dụng nó trước. Chương 8: Quản lý bộ nhớ Nội dung chính Con trỏ (Pointer) Tham chiếu (Reference) Sử dụng tham chiếu hay con trỏ 1. Con trỏ Con trỏ được sử dụng để: Truy nhập vào các thành phần của mảng Truyền tham số cho hàm theo kiểu truyền biến Truyền mảng và xâu ký tự cho hàm Lấy thông tin từ bộ nhớ của hệ thống Tạo ra những cấu trúc dữ liệu như: danh sách liên kết Con trỏ … Mỗi biến trong chương trình chiếm một vùng nhớ, ví dụ biến kiểu int chiếm 4 byte nhớ. Vị trí của vùng nhớ được gọi là địa chỉ của biến Biến con trỏ Biến con trỏ là biến lưu giá trị của địa chỉ vùng nhớ. Mỗi kiểu dữ liệu có một biến con trỏ riêng: con trỏ kiểu int, con trỏ kiểu char… C++ sử dụng: Toán tử & để lấy địa chỉ của biến Toán tử * để lấy nội dung của biến được trỏ. Ví dụ: int i=17; int* ptr; // khai báo biến trỏ kiểu int ptr= &i; // gán địa chỉ của biến i cho con trỏ ptr cout *(ptr+k)) swap(ptr+j,ptr+k); } double array[6] = { 2.3, 4.5, 1.2, 6.8, 0.8, 4.9 }; bsort(array,n); Quản lý bộ nhớ Toán tử new được sử dụng để tạo ra các đối tượng trên vùng nhớ heap. Date* CreateDate() { int day, month, year; char dummy; cout > day >> dummy >> month >> dummy >> year; Date* tmpptr = new Date date(day, month, year); return tmpptr; } Date* ptr; ptr=CreateDate(); cout char* str =”This is an old C-style string”; int len=strlen(str); // xác định kích thước của str char* ptr; ptr = new char[len+1]; // cấp phát vùng nhớ strcpy(ptr,str); cout Date date; date.Set(12,3,1996); date.Display(); Date* dateptr; dateptr=new Date; dateptr->Set(9,12,1999); dateptr->Display(); (*dateptr).Display(); Ví dụ: Linked List struct link // định nghĩa một phần tử trong danh sách { int data; // data item link* next; // con trỏ trỏ tới phần tử kế tiếp }; class linklist { private: link* first; // con trỏ trỏ tới phần tử đầu tiên trong danh sách public: linklist() { first = NULL;} // hàm tạo không có tham số void additem(int d); // bổ sung thêm một phần tử vào danh sách void display(); // hiển thị danh sách } ; Ví dụ: Linked List … void linklist::additem(int d) { link* newlink = new link; // tạo ra một phần tử mới trong danh sách newlink->data = d; // gán dữ liệu newlink->next=first; // trỏ vào phần tử đầu tiên first = newlink; // thay đổi con trỏ first } void linklist::display() { link* current=first; // bắt đầu từ phần tử đầu tiên while(current != NULL) // đến khi kết thúc danh sách { cout data next; // di chuyển đến phần tử kế tiếp } } Cấu trúc tự trỏ Một lớp có thể chứa con trỏ trỏ tới đối tượng của chính lớp đó, nhưng không thể chứa đối tượng của lớp đó. class someclass { someclass* ptr; // đúng }; class someclass { someclass obj; //sai }; Ví dụ: Sử dụng cấu trúc tự trỏ class LinkList { private: int data; LinkList *next, *first; public: LinkList(); void Insert(int d); void Show(); }; Ví dụ: Sử dụng cấu trúc tự trỏ … LinkList :: LinkList(){f=NULL;} void LinkList :: Insert(int d) { LinkList* n = new LinkList(); n->data=d; n->next=first; first=next; } void LinkList :: Show() { LinkList* p=first; while (p!=NULL) { coutdatanext; } } 2. Tham chiếu Tham chiếu là một bí danh (alias) Khi ta tạo ra một tham chiếu, khởi tạo nó bằng tên của một đối tượng khác (đối tượng đích)thì tham chiếu đóng vai trò là một cái tên khác của đích. Bất kỳ việc gì được thực hiện trên tham chiếu cũng tức là được thực hiện trên đích. Khai báo tham chiếu: int& rSomeRef = someInt; Tham chiếu … Nếu ta yêu cầu một tham chiếu tham chiếu tới địa chỉ của nó, thì tham chiếu sẽ trả về địa chỉ của đối tượng đích của nó. Ví dụ: int intOne; int& rSomeRef = intOne; cout SetAge(10); // gán Family[0] bằng 10 pCat++; // trỏ vào Family[1] pCat->SetAge(20); // gán Family[1] bằng 20 Phân biệt con trỏ trỏ tới mảng và mảng các con trỏ Cat FamilyOne[500] CAT * FamilyTwo[500]; CAT * FamilyThree = new CAT[500]; FamilyOne là một mảng có 500 phần tử CAT FamilyTwo là một mảng có 500 con trỏ trỏ tới CAT FamilyThree là một con trỏ trỏ tới một mảng có 500 phần tử CAT. 4. Xâu ký tự Trong C++, xâu ký tự là một mảng các phần tử kiểu char và kết thúc bằng ký tự null. Có hai cách khai báo: char Greeting[] = { `H', `e', `l', `l', `o', ` `, `W','o','r','l','d', `\0' }; char Greeting[] = "Hello World"; Sử dụng cin.get() #include int main() { char buffer[80]; cout > Hàm getline(cin, string str): lưu thông tin từ luồng vào chuẩn đưa vào str. Ví dụ: string full_name; coutlength() thì báo lỗi Ví dụ: string s=“Hello world”; char c; c=s[1]; //c=‘e’ Chương 10: Bắt ngoại lệ Ngoại lệ (Exception) Exception gồm: Mô tả lỗi có thể xảy ra. Đoạn mã lệnh trong đó xảy ra exception, nằm trong khối try{} Những thứ gây ra exception nằm trong lệnh throw Mã lệnh bắt lỗi trong catch Ví dụ Trong ví dụ về stack, lỗi có thể xảy ra khi: Đẩy nhiều phần tử vào stack Lấy phần tử ra khỏi stack rỗng Lỗi có thể xảy ra là dạng out-of-bound Ví dụ … #include #include const int STACK_SIZE = 100; class stack { protected: int count; private: int data[STACK_SIZE]; public: stack(void); void push(const int item); int pop(void); }; Ví dụ … inline stack::stack(void) { count = 0; } inline void stack::push(const int item) { data[count] = item; count++; } inline int stack::pop(void) { count--; return (data[count]); } Ví dụ … const int WHAT_MAX = 80; class bound_err { public: char what[WHAT_MAX]; bound_err(char *_what) { if (strlen(_what) = STACK_SIZE) { bound_err overflow("Push overflows stack"); throw overflow; } stack::push(item); } inline int b_stack::pop(void) throw(bound_err) { if (count . Chú ý: ifstream thừa kế từ istream ofstream thừa kế từ ostream fstream thừa kế từ iostream Input File Để đọc 100 số từ file numbers.dat, phải khai báo biến có kiểu input file. ifstream data_file; Xác định tên file cần đọc, bằng cách sử dụng hàm open. data_file.open("numbers.dat" ); Đọc dữ liệu từ file ra: for (i = 0; i > data_array[i]; Sau khi sử dụng xong, đóng file lại. data_file.close(); Input File … C++ cho phép gọi open ngay trong hàm tạo: ifstream data_file("numbers.dat"); Khi mở file sử dụng chồng toán tử ! để kiểm tra file có được mở thành công hay không Khi đọc file, để kiểm tra lỗi sử dụng hàm bad. if (data_file.bad()) { cerr >name; cout>age; } void showdata() { cout<<"\n Name:"<<name; cout<<"\n Age:"<<age; } }; Ví dụ … int main(int argc, char* argv[]) { person p; fstream outfile("Person.txt",ios::out|ios::in| ios::binary|ios::app); for(int i=0;i<3;i++) { p.getdata(); outfile.write((char*)(&p),sizeof(p)); } outfile.seekg(0); do { outfile.read((char*)(&p),sizeof(p)); p.showdata(); } while(! outfile.eof()); return 0; }