Bài 40.1: Ôn tập kiến thức khóa học trong C++

Chào mừng trở lại series về C++! Sau một hành trình khá dài cùng nhau khám phá ngôn ngữ mạnh mẽ này, chúng ta đã đi qua rất nhiều khái niệm, từ những viên gạch cơ bản nhất cho đến những cấu trúc phức tạp hơn. Trước khi dấn thân vào các chủ đề nâng cao tiếp theo, đây là thời điểm vàng để chúng ta cùng nhau ngồi lại, xâu chuỗi và củng cố lại những kiến thức cốt lõi đã học.

Bài viết ôn tập này không chỉ là một danh sách các chủ đề, mà là một chuyến tàu lượn tốc hành qua những trạm quan trọng nhất trên bản đồ C++ của chúng ta. Hãy cùng nhau nhìn lại và đảm bảo rằng nền móng của bạn thật vững chắc nhé!

1. Biến, Kiểu Dữ Liệu và Toán Tử: Những Khối Xây Dựng Đầu Tiên

  • Biến là trái tim của mọi chương trình, là nơi chúng ta lưu trữ dữ liệu. Mỗi biến cần có một kiểu dữ liệu xác định để C++ biết cách xử lý nó và phân bổ bộ nhớ phù hợp.
  • Chúng ta đã làm quen với các kiểu dữ liệu nguyên thủy phổ biến như:
    • int: Số nguyên.
    • float, double: Số thực (số có phần thập phân). double thường cung cấp độ chính xác cao hơn.
    • char: Ký tự đơn.
    • bool: Giá trị logic (true hoặc false).
    • Và cả string cho chuỗi ký tự (mặc dù không phải nguyên thủy, nhưng cực kỳ thông dụng).
  • Các toán tử giúp chúng ta thực hiện các phép tính và thao tác trên dữ liệu:
    • Toán tử số học: +, -, *, /, % (chia lấy dư).
    • Toán tử so sánh: == (bằng), != (khác), >, <, >=, <=.
    • Toán tử logic: && (AND), || (OR), ! (NOT).
    • Toán tử gán: =, +=, -=, *=, /=, % =.

Ví dụ minh họa:

#include <iostream>
#include <string> // Cần include để dùng string

int main() {
    // Khai báo và gán giá trị cho biến
    int soNguyen = 10;
    double soThuc = 3.14;
    char kyTu = 'A';
    bool laDung = true;
    string ten = "C++";

    // Sử dụng toán tử
    int tong = soNguyen + 5; // Toán tử số học
    bool lonHon = soNguyen > soThuc; // Toán tử so sánh
    bool dieuKien = (soNguyen > 0) && laDung; // Toán tử logic

    // In ra kết quả
    cout << "So nguyen: " << soNguyen << endl;
    cout << "Tong: " << tong << endl;
    cout << "Lon hon: " << lonHon << endl; // In ra 1 (true) hoac 0 (false)
    cout << "Dieu kien: " << dieuKien << endl;
    cout << "Ten ngon ngu: " << ten << endl;

    return 0;
}

Giải thích:

  • Chúng ta khai báo các biến với kiểu dữ liệu khác nhau (int, double, char, bool, string).
  • Thực hiện các phép toán và so sánh đơn giản.
  • Sử dụng cout để in giá trị của biến và kết quả các phép toán ra màn hình. endl dùng để xuống dòng.

2. Cấu Trúc Điều Khiển: Chỉ Đường Cho Chương Trình

Chương trình của chúng ta không chỉ chạy tuần tự từ trên xuống dưới. Chúng ta cần các cấu trúc để đưa ra quyết định (chọn đường nào đi) và lặp lại các hành động (đi đường đó bao nhiêu lần).

  • Câu lệnh điều kiện (if, else if, else): Giúp chương trình thực thi một khối code chỉ khi một điều kiện nhất định đúng. else ifelse cung cấp các lựa chọn thay thế.
  • Câu lệnh lựa chọn (switch): Là một cách gọn gàng để xử lý nhiều trường hợp lựa chọn dựa trên giá trị của một biến.
  • Vòng lặp (for, while, do-while): Cho phép chúng ta lặp lại một khối code nhiều lần.
    • for: Thường dùng khi biết trước số lần lặp hoặc lặp qua các phần tử của một dãy.
    • while: Lặp chừng nào điều kiện còn đúng (kiểm tra điều kiện trước khi lặp).
    • do-while: Lặp ít nhất một lần, sau đó kiểm tra điều kiện để quyết định lặp tiếp hay không (kiểm tra điều kiện sau khi lặp).

Ví dụ minh họa:

#include <iostream>

int main() {
    int diem = 75;

    // if-else if-else
    if (diem >= 90) {
        cout << "Diem A" << endl;
    } else if (diem >= 80) {
        cout << "Diem B" << endl;
    } else if (diem >= 70) {
        cout << "Diem C" << endl;
    } else {
        cout << "Can co gang hon" << endl;
    }

    // Vòng lặp for
    cout << "Cac so chan tu 0 den 10:" << endl;
    for (int i = 0; i <= 10; i += 2) {
        cout << i << " ";
    }
    cout << endl; // Xuong dong

    // Vòng lặp while
    int dem = 5;
    cout << "Dem nguoc:" << endl;
    while (dem > 0) {
        cout << dem << "... ";
        dem--;
    }
    cout << "Bat dau!" << endl;

    return 0;
}

Giải thích:

  • Phần if-else if-else kiểm tra điểm và in ra xếp loại tương ứng.
  • Vòng lặp for bắt đầu với i = 0, lặp chừng nào i <= 10, mỗi lần lặp tăng i thêm 2, in ra các số chẵn.
  • Vòng lặp while bắt đầu với dem = 5, lặp chừng nào dem > 0, in ra giá trị hiện tại của dem và giảm nó đi 1 sau mỗi lần lặp.

3. Hàm (Functions): Phân Chia Công Việc

Khi chương trình trở nên lớn hơn, việc gom các đoạn code thực hiện một nhiệm vụ cụ thể vào thành hàm là cực kỳ quan trọng. Hàm giúp:

  • Tái sử dụng code: Viết một lần, dùng nhiều nơi.
  • Tổ chức code: Chia chương trình thành các khối nhỏ, dễ quản lý và đọc hiểu hơn.
  • Trừu tượng hóa: Che giấu chi tiết triển khai phức tạp, chỉ cần biết hàm làm gì và dùng nó như thế nào.

Một hàm có thể nhận các tham số (input) và trả về một giá trị (output) thông qua câu lệnh return.

Ví dụ minh họa:

#include <iostream>

// Khai báo (prototype) của hàm
int tinhTong(int a, int b);

int main() {
    int so1 = 5;
    int so2 = 7;

    // Gọi hàm
    int ketQua = tinhTong(so1, so2);

    cout << "Tong cua " << so1 << " va " << so2 << " la: " << ketQua << endl;

    // Gọi hàm với giá trị khác
    cout << "Tong cua 10 va 20 la: " << tinhTong(10, 20) << endl;

    return 0;
}

// Định nghĩa (implementation) của hàm
int tinhTong(int a, int b) {
    return a + b; // Trả về tổng của a và b
}

Giải thích:

  • Chúng ta định nghĩa một hàm tên là tinhTong nhận hai tham số kiểu intab, và trả về một giá trị kiểu int.
  • Hàm này thực hiện phép cộng đơn giản.
  • Trong hàm main, chúng ta gọi hàm tinhTong hai lần với các đối số khác nhau và lưu kết quả vào biến ketQua hoặc in trực tiếp.

4. Mảng (Arrays) & vector: Lưu Trữ Bộ Sưu Tập Dữ Liệu

Khi cần lưu trữ một tập hợp các giá trị có cùng kiểu, chúng ta sử dụng các cấu trúc dữ liệu tập hợp.

  • Mảng (Arrays): Là một dãy các phần tử cùng kiểu được lưu trữ liền kề trong bộ nhớ. Kích thước của mảng phải được xác định tại thời điểm biên dịch và không thể thay đổi sau đó.
  • vector: Là một phần tử của Thư viện Chuẩn C++ (STL). Nó giống như một mảng, nhưng có kích thước động, có thể thay đổi trong quá trình chạy chương trình. vector cung cấp nhiều tiện ích hơn mảng thô.

Ví dụ minh họa:

#include <iostream>
#include <vector> // Cần include để dùng vector

int main() {
    // Mảng cố định (kich thuoc 5 phan tu)
    int mangSoNguyen[5] = {10, 20, 30, 40, 50};

    // Truy cap phan tu mang (bat dau tu chi so 0)
    cout << "Phan tu thu 3 cua mang (chi so 2): " << mangSoNguyen[2] << endl; // In ra 30

    // Duyet mang
    cout << "Cac phan tu cua mang:" << endl;
    for (int i = 0; i < 5; ++i) {
        cout << mangSoNguyen[i] << " ";
    }
    cout << endl;

    // vector (bat dau rong)
    vector<int> vecSoNguyen;

    // Them phan tu vao vector
    vecSoNguyen.push_back(100);
    vecSoNguyen.push_back(200);
    vecSoNguyen.push_back(300);

    // Kich thuoc hien tai cua vector
    cout << "Kich thuoc cua vector: " << vecSoNguyen.size() << endl;

    // Truy cap phan tu vector (cung bat dau tu chi so 0)
    cout << "Phan tu dau tien cua vector: " << vecSoNguyen[0] << endl; // In ra 100

    // Duyet vector (dung range-based for loop - C++11 tro len)
    cout << "Cac phan tu cua vector:" << endl;
    for (int so : vecSoNguyen) {
        cout << so << " ";
    }
    cout << endl;

    return 0;
}

Giải thích:

  • Mảng mangSoNguyen được khai báo với kích thước cố định là 5 và khởi tạo giá trị. Chúng ta truy cập các phần tử bằng chỉ số [].
  • vector<int> vecSoNguyen được tạo ra lúc đầu rỗng. Phương thức push_back() được dùng để thêm các phần tử vào cuối vector, làm cho kích thước của nó tăng lên động.
  • Kích thước của vector được lấy bằng phương thức size().
  • Ví dụ cho thấy cách duyệt cả mảng truyền thống (dùng vòng lặp for với chỉ số) và vector (dùng range-based for loop tiện lợi).

5. Con Trỏ (Pointers) & Tham Chiếu (References): Làm Việc Trực Tiếp Với Bộ Nhớ

Đây là hai khái niệm đặc trưng và mạnh mẽ của C++, cho phép chúng ta làm việc ở cấp độ bộ nhớ.

  • Con trỏ (Pointer): Là một biến đặc biệt lưu trữ địa chỉ bộ nhớ của biến khác.
    • Sử dụng toán tử & (address-of) để lấy địa chỉ của một biến.
    • Sử dụng toán tử * (dereference) để truy cập giá trị tại địa chỉ mà con trỏ đang trỏ tới.
  • Tham chiếu (Reference): Là một bí danh (alias) cho một biến đã tồn tại. Một khi đã khởi tạo, tham chiếu không thể thay đổi để tham chiếu đến biến khác. Nó về cơ bản là một tên khác cho cùng một vị trí bộ nhớ.

Ví dụ minh họa:

#include <iostream>

int main() {
    int giaTri = 100;

    // Khai bao con tro va tro toi dia chi cua giaTri
    int* conTroGiaTri = &giaTri;

    // Khai bao tham chieu va tham chieu den giaTri
    int& thamChieuGiaTri = giaTri;

    // In ra gia tri goc, gia tri qua con tro, gia tri qua tham chieu
    cout << "Gia tri goc: " << giaTri << endl; // 100
    cout << "Gia tri qua con tro (*conTroGiaTri): " << *conTroGiaTri << endl; // 100
    cout << "Gia tri qua tham chieu (thamChieuGiaTri): " << thamChieuGiaTri << endl; // 100

    // In ra dia chi cua gia tri goc va dia chi ma con tro dang tro toi
    cout << "Dia chi cua giaTri (&giaTri): " << &giaTri << endl;
    cout << "Dia chi ma con tro tro toi (conTroGiaTri): " << conTroGiaTri << endl; // Giong dia chi cua giaTri

    // Thay doi gia tri qua con tro
    *conTroGiaTri = 200;
    cout << "Gia tri sau khi thay doi qua con tro: " << giaTri << endl; // 200

    // Thay doi gia tri qua tham chieu
    thamChieuGiaTri = 300;
    cout << "Gia tri sau khi thay doi qua tham chieu: " << giaTri << endl; // 300

    return 0;
}

Giải thích:

  • Chúng ta có biến giaTri.
  • conTroGiaTri là một con trỏ (int*) lưu địa chỉ của giaTri (lấy bằng &giaTri). *conTroGiaTri truy cập giá trị tại địa chỉ đó.
  • thamChieuGiaTri là một tham chiếu (int&) đến giaTri. Bất kỳ thao tác nào trên thamChieuGiaTri cũng chính là thao tác trên giaTri.
  • Thay đổi giá trị thông qua con trỏ hoặc tham chiếu đều làm thay đổi giá trị của biến gốc giaTri, chứng tỏ chúng cùng "nhìn" vào một vị trí bộ nhớ.

6. Lập Trình Hướng Đối Tượng (OOP): Class & Object Cơ Bản

OOP là một mô hình lập trình mạnh mẽ giúp quản lý độ phức tạp của các dự án lớn bằng cách tổ chức code xoay quanh các đối tượng (objects).

  • Class (Lớp): Là một bản thiết kế hoặc khuôn mẫu để tạo ra các đối tượng. Nó định nghĩa các thuộc tính (dữ liệu - gọi là members) và hành vi (hàm - gọi là methods) mà các đối tượng thuộc lớp đó sẽ có.
  • Object (Đối tượng): Là một thể hiện cụ thể (instance) của một lớp. Mỗi đối tượng có dữ liệu riêng của nó theo định nghĩa của lớp.
  • Access Specifiers (public, private, protected): Kiểm soát khả năng truy cập vào các members và methods của lớp từ bên ngoài. public có thể truy cập từ mọi nơi; private chỉ có thể truy cập từ bên trong lớp đó.
  • Constructor (Hàm tạo): Là một phương thức đặc biệt trong lớp, có cùng tên với lớp, được gọi tự động khi một đối tượng của lớp đó được tạo ra. Nó thường dùng để khởi tạo dữ liệu cho đối tượng mới.

Ví dụ minh họa:

#include <iostream>
#include <string>

// Dinh nghia mot Class don gian
class ConVat {
public: // Cac thanh phan co the truy cap tu ben ngoai
    string ten;
    int tuoi;

    // Constructor (Ham tao)
    ConVat(string tenConVat, int tuoiConVat) {
        ten = tenConVat;
        tuoi = tuoiConVat;
        cout << "Mot con vat moi duoc tao ra: " << ten << endl;
    }

    // Method (Ham thanh vien)
    void keu() {
        cout << ten << " dang keu!" << endl;
    }

    // Destructor (Ham huy - don dep khi doi tuong bi huy)
    ~ConVat() {
        cout << ten << " bi huy." << endl;
    }
}; // Ket thuc dinh nghia Class phai co dau cham phay

int main() {
    // Tao cac Object (the hien) tu Class ConVat
    ConVat meo("Meo", 3); // Goi constructor
    ConVat cho("Cho", 5); // Goi constructor

    // Truy cap thuoc tinh va goi phuong thuc cua Object
    cout << meo.ten << " " << meo.tuoi << " tuoi." << endl;
    meo.keu();

    cout << cho.ten << " " << cho.tuoi << " tuoi." << endl;
    cho.keu();

    // Khi main ket thuc, cac object meo va cho se bi huy, goi destructor

    return 0;
}

Giải thích:

  • Chúng ta định nghĩa class ConVat với hai thuộc tính tentuoi (đều là public).
  • Constructor ConVat(string tenConVat, int tuoiConVat) nhận tên và tuổi khi tạo đối tượng và gán vào thuộc tính của đối tượng đó. Nó được gọi ngay khi chúng ta viết ConVat meo("Meo", 3);.
  • Method keu() chỉ đơn giản là in ra một thông báo.
  • Destructor ~ConVat() được gọi khi đối tượng bị hủy (ví dụ, khi nó ra khỏi phạm vi tồn tại, như cuối hàm main). Nó giúp thực hiện các tác vụ "dọn dẹp" nếu cần.
  • Trong main, chúng ta tạo hai đối tượng meocho từ lớp ConVat và sử dụng toán tử . để truy cập các thuộc tính (ten, tuoi) và gọi phương thức (keu()).

Comments

There are no comments at the moment.