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++.
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ủahà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ểuvoid)
• 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;
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;
}
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) {
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 private> public:
<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{ }
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 lớp: là tên lớp đối tượng đã được định nghĩa trước khi khai báo biến.
• 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>;
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 dữ liệu phức tạp do người dùng tự định nghĩa như struct, hoặc kiểu là một lớp đã được định nghĩa trước đó.
• Tên thuộc tính: là tên thuộc tính của lớp, có tính chất như một biến thông thường. Tên thuộc tính phải tuân theo quy tắc đặt tên biến của C++.
Ví dụ, khai báo:
class Car{ private: int speed; public:
string mark; };
Khai báo một lớp xe ô tô (Car), có hai thuộc tính: thuộc tính tốc độ (speed) có tính chất private, thuộc tính nhãn hiệu xe (mark) có tính chất public.
Lưu ý:
• Không được khởi tạo giá trị ban đầu cho các thuộc tính ngay trong lớp. Vì các thuộc tính chỉ có giá trị khi nó gắn với một đối tượng cụ thể, là một thể hiện (biến) của lớp.
Ví dụ:
class Car{ private:
int speed; // đúng int weight = 500; // lỗi };
• Khả năng truy nhập thuộc tính của lớp là phụ thuộc vào thuộc tính ấy được khai báo trong phạm vi của từ khóa nào: private, protected hay public.
• các hàm public truy nhập (get / set) đến thuộc tính đó. Thông thường, do yêu cầu đóng gói dữ liệu của hướng đối tượng, ta nên khai báo các thuộc tính có tính chất riêng tư (ptivate). Nếu muốn các đối tượng khác truy nhập được vào các thuộc tính này, ta xây dựng
Sử dụng thuộc tính
Thuộc tính có thể được sử dụng cho các chương trình nằm ngoài lớp thông qua tên biến lớp hoặc sử dụng ngay trong lớp bởi các phương thức của lớp.
• Nếu thuộc tính được dùng bên ngoài phạm vi lớp, cú pháp phải thông qua tên biến lớp (cách này chỉ sử dụng được với các biến có tính chất public):
<Tên biến lớp>.<tên thuộc tính>;
• Nếu thuộc tính được dùng bên trong lớp, cú pháp đơn giản hơn:<Tên thuộc tính>; Ví dụ, với định nghĩa lớp: class Car{ private: int speed; public: string mark; };
//ta khai báo một biến lớp: Car myCar;
Thì có thể sử dụng thuộc tính nhãn hiệu xe khi in ra màn hình như sau: cout << myCar.mark;
Lưu ý:
• Khi dùng thuộc tính bên trong các phương thức của lớp, mà tên thuộc tính lại bị trùng với tên biến toàn cục (tự do) của chương trình, ta phải chỉ rõ việc dùng tên thuộc tính của lớp (mà không phải tên biến toàn cục) bằng cách dùng chỉ thị phạm vi lớp “::”
cú pháp:
Phương thức của lớp
Khai báo khuôn mẫu phương thức
Một phương thức là một thao tác thực hiện một số hành động đặc trưng của lớp đối tượng.
Phương thức được khai báo tương tự như các hàm trong C++: <Kiểu trả về><Tên phương thức>([<Các tham số>]);
Trong đó:
• Kiểu trả về: là kiểu dữ liệu trả về của phương thức. Kiểu có thể là các kiểu dữ liệu cơ bản của C++, cũng có thể là kiểu do người dùng định nghĩa, hoặc kiểu lớp đã được định nghĩa.
• Tên phương thức: do người dùng tự đặt tên, tuân theo quy tắc đặt tên biến của C++.
• Các tham số: Các tham số đầu vào của phương thức, được biểu diễn bằng kiểu dữ liệu tương ứng. Các tham số được phân cách bởi dấu phẩy “,”. Các tham số là tùy chọn (Phần trong dấu ngoặc vuông “[]” là tùy chọn).
Ví dụ, khai báo: class Car{ private: int speed; string mark; public: void show(); };
Định nghĩa một lớp Car có hai thuộc tính cục bộ là speed và mark, và khai báo một phương thức show() để mô tả đối tượng xe tương ứng.
Show() là một phương thức không cần tham số và kiểu trả về là void.
Lưu ý:
Khả năng truy nhập phương thức từ bên ngoài là phụ thuộc vào phương thức được khai báo trong phạm vi của từ khóa nào: private, protected hay public.