Đóng gói và xây dựng lớp

Một phần của tài liệu Giáo trình lập trình hướng đối tượng (nghề lập trình viên máy tính cao đẳng) (Trang 40)

2.1. Đóng gói là gì?

Các lệnh (code) chương trình: Đây là phần chương trình mà thực hiện các

hành động và cúng được gọi là các hàm.

Dữ liệu chương trình: Dữ liệu là thông tin của chương trình mà tác động đến

các hàm chương trình.

Tính đóng gói (Encapsulation) là một khái niệm của lập trình hướng đối tượng mà ràng buộc dữ liệu và các hàm mà thao tác dữ liệu đó, và giữ chúng an toàn bởi ngăn cản sự gây trở ngại và sự lạm dụng từ bên ngoài. Tính đóng gói dẫn đến khái niệm OOP quan trọng làData Hiding.

Tính đóng gói - Data encapsulation là một kỹ thuật đóng gói dữ liệu, và các hàm mà

sử dụng chúng và trừu tượng hóa dữ liệu là một kỹ thuật chỉ trưng bày tới các Interface và ẩn Implementation Detail (chi tiết trình triển khai) tới người sử dụng.

C++ hỗ trợ các thuộc tính của đóng gói và ẩn dữ liệu thông qua việc tạo các kiểu tự định nghĩa (user-defined), gọi là classes. Chúng ta đã học rằng một lớp có thể chứa các thành viênprivate, protected vàpublic. Theo mặc định, tất cả thành phần được định

nghĩa trong một lớp là private. Ví dụ: ? 1 2 3 4 5 6 7 8 9 10 11 12 classBox { public: doubletinhTheTich(void) {

returnchieudai * chieurong * chieucao; }

private:

doublechieudai; // Chieu dai cua mot box doublechieurong; // Chieu rong cua mot box doublechieucao; // Chieu cao cua mot box };

Các biếnchieudai, chieurong, và chieucao làprivate.Nghĩa là chúng chỉ có thể được

truy cập bởi các thành viên khác của lớp Box, và không thể bởi bất kỳ phần khác trong chương trình của bạn.Đây là một cách thực hiện tính đóng gói trong C++.

Để làm cho các phần của lớp làpublic (ví dụ: có thể truy cập tới các phần khác trong

chương trình của bạn), bạn phải khai báo chúng sau từ khóapublic. Tất cả biến và hàm

được định nghĩa sau từ khóa public là có thể truy cập cho tất cả các hàm trong chương trình của bạn.

2.2. Xây dựng lớp

Lớp (Class) là cách phân loại (classify) các đối tượng dựa trên đặc điểm chung của các đối tượng đó.

• Lớp có thể coi là khuôn mẫu để tạo các đối tượng − Ví dụ: Người, Sinh Vật, Màu sắc…

• Lớp chính là kết quả của quá trình trừu tượng hóa dữ liệu

2.3. Che giấu dữ liệu

Đóng gói là một khái niệm trong lập trình hướng đối tượng, kết hợp giữa dữ liệu và các hàm thao tác dữ liệu với nhau, đồng thời đảm bảo sự an toàn và tránh khỏi sự can thiệp và sử dụng sai cách. Nó liên quan đến việc đóng gói các thành viên dữ liệu và các chức năng bên trong một lớp duy nhất.Đóng gói dữ liệu liên quan tới một khái niệm khác đó là che giấu dữ liệu.

Nói chung, đóng gói là một quá trình gói các đoạn mã tương tự vào cùng một nơi. Tính năng đóng gói dữ liệu là một cơ chế đóng gói dữ liệu và các hàm sử dụng dữ liệu.Trừu tượng hóa dữ liệu là cơ chế chỉ để cho phép hiện ra các giao diện và ẩn dấu đi các chi tiết bên trong khỏi người dùng.

C ++ hỗ trợ các thuộc tính đóng gói và che giấu dữ liệu thông qua việc tạo ra các kiểu dữ liệu do người dùng tự định nghĩa, được gọi là các lớp hay Class. Một lớp có thể chứa các thành viên trong phạm vi Private, Protected và Public. Theo mặc định, tất cả các thành viên sẽ được xác định trong phạm vi là Private.

2.4. Phương thức set/get

3. Tạo và sử dụng đối tượng

3.1. Khởi tạo dữ liệu

Đây là cách khai báo các biến có cùng kiểu dữ liệu trên cùng một dòng đồng thời khởi tạo giá trị ban đầu cho mỗi biến.Khi khai báo nhiều biến trên cùng 1 dòng, mỗi biến được khai báo sẽ cách nhau bằng 1 dấu phẩy. Điều này hoàn toàn được cho phép

trong C++. (adsbygoogle = window.adsbygoogle || []).push({});

int number1 =4, number2 = 5;

3.2.Phương thức khởi tạo

Hiện tại C++11 trở về sau hỗ trợ 3 loại constructor với mục đích sử dụng khác nhau nhằm tối ưu hóa cho từng trường hợp sử dụng cụ thể:

• Constructor: hàm tạo khởi tạo mới. • Copy constructor: hàm tạo sao chép. • Move constructor: hàm tạo dịch chuyển.

Ứng với 3 tên gọi này là mục đích sử dụng của nó.Cả 3 phương thức đều tự động gọi khi đối tượng được tạo ra.

3.3.Khai báo và khởi tạo đối tượng

Hàm khởi tạo (constructor) là một phương thức đặc biệt được gọi tự động tại thời

điểm đối tượng được tạo.Mục đích của hàm khởi tạo là để khởi tạo các thành viên dữ

liệu của đối tượng.

Mộthàm khởi tạo sẽ khác những hàm thông thường ở những điểm sau:

• Có tên trùng với tên lớp

• Không có kiểu dữ liệu trả về ( kể cả kiểu void)

• Tự động được gọi khi một đối tượng thuộc lớp được tạo ra

• Nếu chúng ta không khai báo mộthàm khởi tạo, trình biên dịch C++ sẽ tự động

tạo mộthàm khởi tạo mặc định cho chúng ta (sẽ là hàm không có tham số nào

và có phần thân trống).

3.4.Sử dụng đối tượng

Xét ví dụ sau #include <string.h> class Matrix4x4

{ private: double* data; public: Matrix4x4() {

data = new double[4 * 4]; }

Matrix4x4(const Matrix4x4& matrix) {

data = new double[4 * 4];

memcpy(data, matrix.data, sizeof(double) * 4 * 4); } Matrix4x4(Matrix4x4&& matrix) { data = matrix.data; matrix.data = nullptr; } ~Matrix4x4() { if (data != nullptr) delete[] data; }

Matrix4x4& operator=(const Matrix4x4& matrix) {

memcpy(data, matrix.data, sizeof(double) * 4 * 4); return *this;

}

Copy constructor được sử dụng trong trường hợp tạo các giá trị mới nhưng cần sao chép ngay các dữ liệu của đối tượng có sẵn thay vì gọi constructor tốn kém hiệu năng và rồi gọi phương thức sao chép tốn kém thêm 1 lần hiệu năng nữa.

1 trường hợp điển hình sử dụng copy constructor thuận tiện là hoán đổi giá trị của 2 Matrix4x4 khi cần khai báo 1 Matrix4x4 temp.

Matrix4x4 temp = m1; m1 = m2;

m2 = temp; (adsbygoogle = window.adsbygoogle || []).push({});

Dòng Matrix4x4 temp = m1 gọi copy constructor khi khởi tạo temp, trường hợp này dùng vậy rất thuật tiện và đỡ tốn hiệu năng.

Matrix4x4(Matrix4x4 && matrix)

Move constructor được gọi trong trường hợp khởi tạo đối tượng truyền vào 1 rvalue. Matrix4x4 m1, m2;

Matrix4x4 m3(m1 + m2);

Dòng Matrix4x4 m3(m1 + m2) trong đó m1 + m2 sẽ trả về 1 đối tượng tạm, sau dòng code này đối tượng đó sẽ hủy, trước C++11 chỉ có thể gọi copy constructor, m3 sẽ phải khởi khởi tạo vùng nhớ và sao chép dữ liệu từ đối tượng m1 + m2 dù biết rằng m1 + m2 sẽ không dùng nữa nhưng vẫn không thể "chiếm dụng" kết quả này, nhưng với C++11 thì khác, có thể tiến hành gọi move constructor vì biết m1 + m2 là rvalue, như phần hiện thực của move constructor có thể thấy dữ liệu tạm được "sang nhượng" cho m3. với chi phí thấp hơn copy constructor.

Matrix4x4 operator+(const Matrix4x4& matrix) {

Matrix4x4 result = *this; for (int i = 0; i < 4 * 4; i++)

result.data[i] += matrix.data[i]; return result; } }; int main() { Matrix4x4 m1, m2; Matrix4x4 m3 = m1 + m2; 4. Từ khóa this

Mỗi đối tượng trong C++ có sự truy cập tới vị trí riêng của nó thông qua một con trỏ quan trọng gọi là con trỏ this.Con trỏ this trong C++ là một từ khóa đề cập đến thể hiện hiện tại của lớp, là một tham số ẩn với tất cả hàm thành viên. Vì thế, bên trong

một hàm thành viên, con trỏ this có thể tham chiếu tới đối tượng đang gọi.

Các hàm friend không có con trỏ this, bởi vì friend không phải là các thành viên của một lớp. Chỉ có các hàm thành viên trong C++ là có con trỏ this.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <iostream> using namespace std; class Employee { public:

int id; //data member (bien instance) string name; //data member(bien instance) float salary;

Employee(int id, string name, float salary) { this->id = id;

this->name = name; this->salary = salary; }

void display() {

cout << id << " " << name << " " << salary << endl; }

18 };

int main(void) {

Employee e1 = Employee(101, "Tran Thi Vinh", 500); // tao doi tuong Employee Employee e2 = Employee(102, "Dao Van Hoa", 1000); // tao doi tuong Employee e1.display();

e2.display(); return 0; }

101 Tran Thi Vinh 500 102 Dao Van Hoa 1000

5. Truyền tham số cho phương thức

5.1. Truyền tham số kiểu dữ liệu tham trị

Ngôn ngữ lập trình C++ cho phép bạn truyền một con trỏ tới một hàm. Đểtruyền con trỏ tới hàm trong C++ bạn chỉ cần khai báo tham số hàm có kiểu con trỏ.

Ví dụ sau chúng ta truyền một con trỏ unsigned long tới một hàm và thay đổi giá trị của nó bên trong hàm, truyền tham chiếu khi gọi hàm:

#include <iostream> #include <ctime> usingnamespacestd; voidlaySoGiay(unsigned long*par); intmain() { unsigned longseconds;

laySoGiay(&seconds) // in gia tri

cout << "So giay la: "<< seconds << endl; return0;

} (adsbygoogle = window.adsbygoogle || []).push({});

voidlaySoGiay(unsigned long*par) { // Lay so giay hien tai *par = time( NULL );

return; }

Kết quả:

So giay la: 1542429527

5.2. Truyền tham số kiểu dữ liệu tham chiếu

Với hàm có tham số là một con trỏ, thì bạn cũng có thể truyền một mảng vào, ví dụ: #include <iostream>

usingnamespacestd;

// khai bao prototype ham: doublegiaTriTB(int*arr, intsize); intmain() {

// khai bao mang so nguyen arr co 5 phan tu. intarr[5] = {10, 20, 100, 30, 60};

trungbinh = giaTriTB(arr, 5); // hien thi ket qua cout << "Gia tri trung binh la: "<< trungbinh << endl; return0;

}doublegiaTriTB(int*arr, intsize) { int i, sum = 0;

doubletrungbinh;

for(i = 0; i < size; ++i) { sum += arr[i];

}

trungbinh = double(sum) / size; returntrungbinh;

}

6. Chồng phương thức

Nạp chồng toán tử và Nạp chồng hàm trong C++

Nạp chồng hàm (function overloading) vàNạp chồng toán tử

(operatoroverloading) trong C++ cho phép bạn xác định nhiều hơn một định nghĩa

cho một tên hàm hoặc một toán tử trong cùng phạm vi (scope), được gọi tương ứng. Một khai báo nạp chồng là một khai báo mà đã được khai báo với cùng tên như một khai báo được khai báo trước đó trong cùng phạm vi, ngoại trừ rằng: cả hai khai báo có các tham số khác nhau và định nghĩa khác nhau.

Khi bạn gọi một hàm nạp chồng hoặc một toán tử nạp chồng, thì compiler quyết định định nghĩa thích hợp nhất để sử dụng bằng việc so sánh các kiểu tham số bạn đã sử dụng để gọi hàm hoặc toán tử với các kiểu tham số đã được xác định trong các định nghĩa. Tiến trình lựa chọn hàm nạp chồng hoặc toán tử nạp chồng thích hợp nhất này được gọi làoverload resolution - phân giải nạp chồng.

Nạp chồng hàm trong C++

Bạn có thể có nhiều định nghĩa cho cùng tên hàm trong cùng phạm vi. Định nghĩa của hàm phải khác lẫn nhau về kiểu và/hoặc số tham số trong danh sách tham số.Bạn không thể nạp chồng các khai báo hàm mà chỉ khác nhau ở kiểu trả về.

Trong ví dụ sau, cùng một hàmhamIn được sử dụng để in các kiểu dữ liệu khác nhau:

#include <iostream> usingnamespacestd; classinDuLieu { public: voidhamIn(inti) {

cout << "In so nguyen: "<< i << endl; }

voidhamIn(double f) {

cout << "In so thuc: "<< f << endl; }

voidhamIn(char* c) { (adsbygoogle = window.adsbygoogle || []).push({});

cout << "In ky tu: "<< c << endl; }

};

intmain(void) {

inDuLieu idl;

// Goi ham hamIn de in so nguyen idl.hamIn(12345);

// Goi ham hamIn de in so thuc idl.hamIn(6677.02);

// Goi ham hamIn de in ky tu

idl.hamIn("Hoc C++ co ban va nang cao."); return0;

}

Biên dịch và chạy chương trình C++ trên sẽ cho kết quả sau:

7. Xây dựng lớp đơn giản, tạo và sử dụng các đối tượng

Để quản lý một hoặc nhiều đối tượng, trong lập trình hướng đối tượng người ta tạo ra một khung gọi là lớp.Trong lớp nó sẽ có các biến mà biến này ta gọi là các thuộc tính (properties), và lớp nó có thể chứa các hàm mà các hàm này chúng ta gọi nó là phương thức (method).

Hình trên ta thấy có 1 class Car và 3 đối tượng Object1, Object1,Object3

Cú pháp khai báo lớp:

class<Ten lop> {

private:

<Khai bao cac thanh phan public> protected:

<Khai bao cac thanh phan protected> };

Trong đó:

class : là từ khóa bắt buộc để định nghĩa một lớp đối tượng trong C++

Ten_lop: là do người dùng tự định nghĩa. Ten_lop có tính chất như định nghĩa kiểu dữ liệu để sử dụng sau này. Cách đặt tên lớp theo quy tắc đặt tên biến trong ngôn ngữ C++

Qui tắc khai báo lớp:

Một lớp được định nghĩa bắt đầu với từ khóa class, theo sau là tên của 1 lớp, và cặp dấu ngoặc tròn bên trong chứa các thuộc tính (properties) và các phương thức (methods) thuộc về lớp. Tên lớp phải bắt đầu bằng 1 ký tự hoặc dấu gạch chân (_), theo sau là bất kỳ ký tự số, chữ cái hoa thường, hay dấu gạch chân và không trùng tên với các từ khóa của PHP. Tên lớp có thể diễn giải bằng biểu thức chính quy như sau: ^[a-zA-Z_\x7f-\xff][a-zA- Z0-9_\x7f-\xff]*$.

Một lớp có thể chứa các hằng số, biến (hay gọi là thuộc tính) và các hàm (hay gọi là phương thức).

Ví dụ: về 1 lớp cơ bản như sau:

class Car{ };

Lưu ý:

• Từ khóa class là bắt buộc để định nghĩa một lớp đối tượng trong C++. Hơn nữa, C++ có phân biệt chữ hoa chữ thường trong khai báo cho nên chữ class phải được viết bằng chữ thường.

Ví dụ: khai báo đúng

class Car{ } (adsbygoogle = window.adsbygoogle || []).push({});

Khai báo sai

Class Car{// Lỗi từ khóa }

• Bắt buộc phải có dấu chấm phẩy “;” ở cuối định nghĩa lớp vì C++ coi định nghĩa một lớp như định nghĩa một kiểu dữ liệu, cho nên phải có dấu chấm phẩy cuối định nghĩa (tương tự định nghĩa kiểu dữ liệu kiểu cấu trúc).

• Để phân biệt với tên biến thông thường, ta nên (nhưng không bắt buộc) đặt tên lớp bắt đầu bằng một chữ in hoa và các tên biến bắt đầu bằng một chữ in thường.

Sử dụng lớp đối tượng

Lớp đối tượng được sử dụng khi ta khai báo các thể hiện của lớp đó.Một thể hiện của một lớp chính là một đối tượng cụ thể của lớp đó. Việc khai báo một thể hiện của một lớp được thực hiện như cú pháp khai báo một biến có kiểu lớp:

<Tên lớp><Tên biến lớp>;

Trong đó:

• Tên biến lớp: là tên đối tượng cụ thể. Tên biến lớp sẽ được sử dụng như các biến thông thường trong C++, ngoại trừ việc nó có kiểu lớp đối tượng.

Ví dụ, muốn khai báo một thể hiện (biến) của lớp Car

Car myCar;

Sau đó, ta có thể sử dụng biến per trong chương trình như các biến thông thường: truyền tham số cho hàm, gán cho biến khác … Lưu ý:

• Khi khai báo biến lớp, ta không dùng lại từ khóa class nữa. Từ khóa class chỉ được sử dụng khi định nghĩa lớp mà không dùng khi khai báo biến lớp.

Ví dụ, khai báo:

Car myCar; // đúng là đúng, nhưng khai báo: class Car myCar; ; // Lỗi cú pháp là sai cú pháp.

Các thành phần của lớp

Việc khai báo các thành phần của lớp có dạng như sau: class<Tên lớp>{

private:

<Khai báo các thành phần riêng> protected:

<Khai báo các thành phần được bảo vệ> public:

<Khai báo các thành phần công cộng> };

Trong đó:

• private: là từ khóa chỉ tính chất của C++ để chỉ ra rằng các thành phần được khai báo trong phạm vi từ khóa này là riêng tư đối với lớp đối tượng. Các đối tượng của các lớp khác không truy nhập được các thành phần này.

• protected: các thành phần được khai báo trong phạm vi từ khóa này đều được bảo vệ. Qui định loại đối tượng nào được truy nhập đến các thành phần được bảo vệ • public: các thành phần công cộng. Các đối tượng của các lớp khác đều có thể

truy nhập đến các thành phần công cộng của một đối tượng bất kì. Các thành phần của lớp được chia làm hai loại:

1. Các thành phần chỉ dữ liệu của lớp, được gọi là thuộc tính của lớp 2. Các thành phần chỉ hành động của lớp, được gọi là phương thức của lớp.

Thuộc tính của lớp

Khai báo thuộc tính

Thuộc tính của lớp là thành phần chứa dữ liệu, đặc trưng cho các tính chất của lớp. Thuộc tính của lớp được khai báo theo cú pháp sau:

<Kiểu dữ liệu><Tên thuộc tính>; (adsbygoogle = window.adsbygoogle || []).push({});

Trong đó:

• Kiểu dữ liệu: có thể là các kiểu dữ liệu cơ bản của C++, cũng có thể là các kiểu

Một phần của tài liệu Giáo trình lập trình hướng đối tượng (nghề lập trình viên máy tính cao đẳng) (Trang 40)