Lập trình C++ - Lớp và đối tượng
Lớp và Đối tượng trong lập trình C++
Để 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.
1. 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.
2. 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:
- 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
- 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.
3. 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:
<Tên lớp>::<Tên thuộc tính>;
4. 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.
Định nghĩa phương thức
Trong C++, việc cài đặt chi tiết nội dung của phương thức có thể tiến hành ngay trong phạm vi lớp hoặc bên ngoài phạm vi định nghĩa lớp. Cú pháp chỉ khác nhau ở dòng khai báo tên phương thức.
Nếu cài đặt phương thức ngay trong phạm vi định nghĩa lớp, cú pháp là:
<Kiểu trả về> <Tên phương thức>([<Các tham số>]){ … // Cài đặt chi tiết
}
Nếu cài đặt phương thức bên ngoài phạm vi định nghĩa lớp, ta phải dùng chỉ thị phạm vi “::” để chỉ ra rằng đấy là một phương thức của lớp mà không phải là một hàm tự do trong chương trình:
<Kiểu trả về> <Tên lớp>::<Tên phương thức>([<Các tham số>]){ … // Cài đặt chi tiết
}
Ví dụ, nếu cài đặt phương thức show() của lớp Car ngay trong phạm vi định nghĩa lớp, ta cài đặt như sau:
class Car{
private:
int speed; // Tốc độ
string mark; // Nhãn hiệu
public:
void show(){ // Khai báo phương thức ngay trong lớp
cout << "This is a " << mark << " having a speed of " << speed << "km/h!" << endl;
}
};
Nếu muốn cài đặt bên ngoài lớp, ta cài đặt như sau:
class Car{
private:
int speed; // Tốc độ
string mark; // Nhãn hiệu
public:
void show(); // Giới thiệu xe
};
/* Khai báo phương thức bên ngoài lớp */
void Car::show(){
cout << "This is a " << mark << " having a speed of "<< speed << "km/h!" << endl;
}
Lưu ý:
- Nếu phương thức được cài đặt ngay trong lớp thì các tham số phải tường minh, nghĩa là mỗi tham số phải được biểu diễn bằng một cặp <Kiểu dữ liệu> <Tên tham số> như khi cài đặt chi tiết một hàm tự do trong chương trình.
- Thông thường, chỉ các phương thức ngắn (trên một dòng) là nên cài đặt ngay trong lớp. Còn lại nên cài đặt các phương thức bên ngoài lớp để chương trình được sáng sủa, rõ ràng và dễ theo dõi.
Sử dụng phương thức
Cũng tương tự như các thuộc tính của lớp, các phương thức cũng có thể được sử dụng bên ngoài lớp thông qua tên biến lớp, hoặc có thể được dùng ngay trong lớp bởi các phương thức khác của lớp định nghĩa nó. Nếu phương thức đượ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 phương thức có tính chất public):
<Tên biến lớp>.<Tên phương thức>([<Các đối số>]);
Nếu thuộc tính được dùng bên trong lớp, cú pháp đơn giản hơn:
<Tên phương thức>([<Các đối số>]);
Ví dụ, với định nghĩa lớp:
class Car{
private:
int speed; // Tốc độ
string mark; // Nhãn hiệu
public:
void show(); // Giới thiệu xe
};
/* Khai báo phương thức bên ngoài lớp */
void Car::show(){
cout << "This is a " << mark << " having a speed of "<< speed << "km/h!" << endl;
}
ta khai báo một biến lớp:
Car myCar;
Thì có thể sử dụng phương thức giới thiệu xe như sau:
myCar.show();
Lưu ý:
- Khi dùng phương thức bên trong các phương thức khác của lớp, mà phương thức lại bị trùng với các phương thức tự do của chương trình, ta phải chỉ rõ việc dùng phương thức của lớp (mà không phải dùng phương thức tự do) bằng cách dùng chỉ thị phạm vi lớp “::”
Cú pháp:
<Tên lớp>::<Tên phương thức>([<Các đối số>]);
Chương trình cài đặt đầy đủ một lớp xe ô tô (Car) với các thuộc tính có tính chất cục bộ:
- Tốc độ xe (speed)
- Nhãn hiệu xe (mark)
- Giá xe (price)
Và các phương thức có tính chất public:
- Khởi tạo các tham số (init)
- Giới thiệu xe (show)
- Các phương thức truy nhập (get/set) các thuộc tính
Sau đó, chương trình main sẽ sử dụng lớp Car này để định nghĩa các đối tượng cụ thể và sử dụng các phương thức của lớp này.
#include<iostream>
#include<string>
using namespace std;
/* Định nghĩa lớp */
class Car {
private:
int speed; // Tốc độ
string mark; // Nhãn hiệu
float price; // Giá xe
public:
void setSpeed(int); // Gán tốc độ cho xe
int getSpeed(); // Lấy tốc độ xe
void setMark(string); // Gán nhãn cho xe
string getMark(); // Lấy nhãn xe
void setPrice(float); // Gán giá cho xe
float getPrice(); // Lấy giá xe
void init(int, string, float);// Khởi tạo thông tin về xe
void show(); // Hiển thị thông tin về xe
};
/* Khai báo phương thức bên ngoài lớp */
void Car::setSpeed(int speedIn) { // Gán tốc độ cho xe
speed = speedIn;
}
int Car::getSpeed() { // Lấy tốc độ xe
return speed;
}
void Car::setMark(string markIn) { // Gán nhãn cho xe
mark = markIn;
}
string Car::getMark() { // Lấy nhãn xe
return mark;
}
void Car::setPrice(float priceIn) { // Gán giá cho xe
price = priceIn;
}
float Car::getPrice() { // Lấy giá xe
return price;
}
void Car::init(int speedIn, string markIn, float priceIn) {
speed = speedIn;
mark = markIn;
price = priceIn;
return;
}
void Car::show() { // Phương thức hiển thị xe
cout << "This is a" << mark << "having a speed of " << speed << "km/h and its price is $" << price << endl;
return;
}
// Hàm main, chuong trình chính
int main() {
Car myCar; // Khai báo bie
// Kh?i t?o l?n th? nh?t
cout << "Xe thu nhat: " << endl;
myCar.init(100, "Ford", 3000);
cout << "Toc do (km/h): " << myCar.getSpeed() << endl;
cout << "Nhan hieu :" << myCar.getMark() << endl;
cout << "Gia ($):" << myCar.getPrice() << endl;
// Thay d?i thu?c tính xe
cout << "Xe thu hai:" << endl;
myCar.setSpeed(150);
myCar.setMark("Mercedes");
myCar.setPrice(5000);
myCar.show();
system("pause");
return 0;
}
Kết quả:
Xe thu nhat:
Toc do (km/h): 100
Nhan hieu: Ford
Gia ($): 3000
Xe thu hai:
This is a Mercedes having a speed of 150km/h and its price is $5000
5. Phạm vi truy nhập lớp
Trong C++, có một số khái niệm về phạm vi, xếp từ bé đến lớn như sau:
- Phạm vi khối lệnh: Trong phạm vi giữa hai dấu giới hạn “{}” của một khối lệnh. Ví dụ các lệnh trong khối lệnh lặp while(){} sẽ có cùng phạm vi khối lệnh.
- Phạm vi hàm: Các lệnh trong cùng một hàm có cùng mức phạm vi hàm.
- Phạm vi lớp: Các thành phần của cùng một lớp có cùng phạm vi lớp với nhau: các thuộc tính và các phương thức của cùng một lớp.
- Phạm vi chương trình (còn gọi là phạm vi tệp): Các lớp, các hàm, các biến được khai báo và định nghĩa trong cùng một tệp chương trình thì có cùng phạm vi chương trình.
- Trong phạm vi truy nhập lớp, ta chỉ quan tâm đến hai phạm vi lớn nhất, đó là phạm vi lớp và phạm vi chương trình. Trong C++, phạm vi truy nhập lớp được quy định bởi các từ khóa về thuộc tính truy nhập:
- private: Các thành phần của lớp có thuộc tính private thì chỉ có thể được truy nhập trong phạm vi lớp.
- protected: Trong cùng một lớp, thuộc tính protected cũng có ảnh hưởng tương tự như thuộc tính private: các thành phần lớp có thuộc tính protected chỉ có thể được truy nhập trong phạm vi lớp. Ngoài ra nó còn có thể được truy nhập trong các lớp con khi có kế thừa
- public: các thành phần lớp có thuộc tính public thì có thể được truy nhập trong phạm vi chương trình, có nghĩa là nó có thể được truy nhập trong các hàm tự do, các phương thức bên trong các lớp khác…
Ví dụ, thuộc tính price của lớp Car có tính chất private nên chỉ có thể truy nhập bởi các phương thức của lớp Car. Không thể truy nhập từ bên ngoài lớp (phạm vi chương trình), chẳng hạn trong một hàm tự do ngoài lớp Car.
void Car::setPrice(float priceIn){
price = priceIn; // Đúng, vì setPrice là một phương thức của lớp Car
}
nhưng:
void freeFunction(Car myCar){
myCar.price = 3000;// Lỗi, vì freeFunction là một hàm tự do
// nằm ngoài phạm vi lớp Car
}
Khi đó, hàm freeFunction phải truy nhập gián tiếp đến thuộc tính price thông qua phương thức truy nhập có tính chất public như sau:
void freeFunction(Car myCar){
myCar.setPrice(3000);// Đúng, vì setPrice là một phương thức của
// lớp Car có thuộc tính public
}
Tuy nhiên, C++ cho phép một cách đặc biệt để truy nhập đến các thành phần private và protected của một lớp bằng khái niệm hàm bạn và lớp bạn của một lớp: trong các hàm bạn và lớp bạn của một lớp, có thể truy nhập đến các thành phần private và protected như bên trong phạm vi lớp đó.
Sự khác nhau giữa lớp và đối tượng trong C++
Sự khác nhau giữa lớp và đối tượng trong C++ được thống kê trong bảng sau:
STT | Đối tượng | Lớp |
1. | Đối tượng là thể hiện của 1 lớp. | Lớp là một khuân mẫu hay thiết kế để tạo ra các đối tượng. |
2. | Đối tượng là 1 thực thể trong thế giới thực như Bút chì, Xe đạp, … | Lớp là một nhóm các đối tượng tương tự nhau. |
3. | Đối tượng là 1 thực thể vật lý | Lớp là 1 thực thể logic |
4. | Đối tượng được tạo ra chủ yếu từ từ khóa new. Ví dụ: Student s1=new Student(); |
Lớp được khai báo bằng việc sử dụng từ khóa class. Ví dụ: class Student{} |
5. | Đối tượng có thể được tạo nhiều lần. | Lớp được khai báo 1 lần duy nhất. |
6. | Đối tượng được cấp bộ nhớ khi nó được tạo ra. | Lớp không được cấp bộ nhớ khi nó được tạo ra. |
7. | Chỉ có một cách để tạo ra đối tượng trong C++ như từ khóa new | Chỉ có một cách để định nghĩa lớp trong C++ sử dụng từ khoá class. |