Professional Documents
Culture Documents
Câu 1: Nêu khái niệm về sự kế thừa và những ưu nhược điểm của kế thừa trong việc l ập
trình. Trường hợp nào thì có thể vi phạm tính kế thừa? Cho ví dụ minh họa.
- Khái niệm: Kế thừa trong lập trình là cách 1 lớp có thể thừa hưởng lại những thuộc
tính, phương thức từ 1 lớp khác và sử dụng chúng như của bản thân mình.
- Một định nghĩa trừu tượng hơn về kế thừa: là một đặc điểm của ngôn ngữ hướng đối
tượng dùng để biểu diễn mối quan hệ đặc biệt hoá – tổng quát hoá giữa các lớp.
- Lớp cơ sở thường được xử lí như 1 thành phần kiểu đối tượng của lớp dẫn xuất
Ưu điểm:
+ Cho phép xây dựng một lớp mới từ lớp đã có.
Lớp mới gọi là lớp con(subclass) hay lớp dẫn xuất(derived class).
Lớp đã có gọi là lớp cha(superclass) hay lớp cơ sở(base class).
+ Cho phép chia sẻ các thông tin chung nhằm tái sử dụng và đồng thời giúp ta dễ
dàng nâng cấp hay bảo trì.
+ Lớp con theo 1 tiêu chuẩn nhất định
+ cho phép tổ chức code theo 1 cách hợp lý và có cấu trúc
+cho phép tạo nên những đối tượng ở các dạng khác nhau dựa vào những lớp chúng
thừa kế
+cho phép ghi đè những phương thức ở lớp thừa kế, ta có thể thay đổi cách 1 phương
thức làm việc mà ko cần thay đổi cả code gốc
+ cho phép tạo lớp cơ sở trừu tượng để định nghĩa những hành vi cơ bản và để lại
nhưng cài đặt ứng dụng chi tiết hơn cho các lớp sau
+ Định nghĩa sự tương thích giữa các lớp, nhờ đó ta có thể chuyển kiểu tự động.
Nhược điểm:
+Khi sử dụng quá nhiều kế thừa, mã có thể trở nên khó hiểu và khó bảo trì. Kế thừa có
thể dẫn đến việc xung đột giữa các phương thức và thuộc tính.
b. Trình bày khái niệm đa hình trong lập trình hướng đối tượng. Cho ví dụ minh họa
- Tính đa hình là hiện tượng các đối tượng thuộc các lớp khác nhau có thể hiểu cùng
một thông điệp theo các cách khác nhau.
- Ví dụ: có 3 con vật: chó, mèo, lợn. Khi ta bảo “kêu” thì con chó sẽ kêu gâu gâu, con
mèo sẽ kêu meo meo và con heo sẽ kêu ẹt ẹt. Cả 3 con vật có thể hiểu cùng một thông
điệp là “kêu” nhưng thực hiện theo các cách khác nhau.
Câu 7: Phân biệt các kiểu kế thừa private, protected, public.
Phạm vi truy từ khoá dẫn Private Protected Public
cập xuất
Private X X X
Protected Private Protected Protected
Public Private Protected Public
- Thành phần private ở lớp cha thì không truy xuất được ở lớp con.
- Kế thừa public: Lớp con kế thừa public từ lớp cha thì các thành phân protected của lớp cha trở
thành protected của lớp con, các thành public của lớp cha trở thành public của lớp con.
- Kế thừa protected: Lớp con kế thừa protected từ lớp cha thì các thành phần protected và public
của lớp cha trở thành protected của lớp con.
- Kế thừa private: Lớp con kế thừa private từ lớp cha thì các thành phần protected và public của
lớp cha thành private của lớp con.
Câu 8: Trình bày các đặc điểm quan trọng của lập trình hướng đối tượng.
• Trừu tượng hóa – Abstraction: Trừu tượng là quá trình tạo ra các lớp trừu tượng để mô
hình hóa các đối tượng trong thế giới thực. Nó giúp tập trung vào các khía cạnh quan
trọng và ẩn đi các chi tiết không cần thiết.
• Đóng gói – Encapsulation: Đóng gói là quá trình kết hợp dữ liệu và các phương thức hoạt
động trên dữ liệu thành một đơn vị độc lập. Điều này giúp bảo vệ dữ liệu và ẩn thông tin
chi tiết về cách hoạt động bên trong của đối tượng.
• Thừa kế - Inheritance: Kế thừa cho phép lớp con kế thừa các thuộc tính và phương thức
từ lớp cha. Điều này giúp tái sử dụng mã nguồn và xây dựng các mối quan hệ phân cấp
giữa các lớp.
• Đa hình – Polymorphism: Đa hình cho phép sử dụng các phương thức cùng tên nhưng
với cách thức thực hiện khác nhau trong các lớp khác nhau. Điều này cho phép gọi cùng
một phương thức và có kết quả khác nhau tuỳ thuộc vào đối tượng gọi phương thức đó.
Câu 9: Trình bày khái niệm của lớp cơ sở trừu tượng (abstract class). Lớp cơ sở trừu tượng được
cài đặt trong C++ như thế nào?
- Lớp cơ sở trừu tượng(Abstract Class) là lớp có ít nhất một phương thức thuần
ảo(pure virtual), hàm thuần ảo được xác định là một hàm virtual có kết thúc khai báo
hàm là “=0”. VD: virtual voi Nhap() = 0;
- Trong trường hợp lớp cơ sở trừu tượng có tất cả các phương thức là thuần ảo thì được
gọi là interface( giao diện).
- VD cài đặt:
Class Vehicle {
private:
string id;
double price;
public:
virtual void Input() = 0;
virtual void Output() = 0;
};
Câu 10:
a. Hàm thuần ảo là gì? Lớp trừu tượng là gì? Cho ví dụ minh họa.
- Hàm thuần ảo( Phương thức ảo thuần tuý) có ý nghĩa cho việc tổ chức sơ đồ phân
cấp các lớp, nó đóng vai trò chừa sẵn chỗ trống cho các lớp con điền vào với phiên
bản phù hợp. Phương thức ảo thuần tuý là phương thức ảo không có nội dung, được
khai báo với từ kháo virtual và được gán giá trị = 0.
- Khi lớp có phương thức ảo thuần tuý, lớp trở thành lớp cơ sở trừu tượng. Lớp cơ sở
trừu tượng không có đối tượng nào thuộc về chính nó.
- VD:
class Shape{ //Abstract
public:
virtual void Draw() = 0; //pure virtual function
}
Trong ví dụ trên, các hàm thành phần trong lớp Shape là phương thức ảo thuần tuý và
lớp Shape là lớp cơ sở trừu tượng. Nó bảo đảm không thể tạo được đối tượng thuộc
lớp Shape.
b. Hãy nêu các đặc điểm quan trọng của lập trình hướng đối tượng. (trùng câu 8)
Câu 11: Phân biệt khái niệm overload (tải chồng) và override (ghi đè) trong lập trình hướng đối
tượng.
Override Overload
Khái Là 1 tính năng cho phép một hoặc nhiều Nạp chồng phương thức là có vài
niệm lớp con cung cấp một triển khai cụ thể phương thức trùng tên nhưng khác
của 1 phương thức đã được cung cấp nhau về đối số. Cài chồng phương
bởi lớp cha của nó. Nói cách khác, nếu thức cho phép ta tạo nhiều phiên bản
lớp con cung cấp trình triển khai cụ thể của 1 phương thức, mỗi phiên bản
của phương thức mà đã được cung cấp chấp nhận một danh sách đối số khác
bởi 1 trong các lớp cha của nó thì gọi là nhau, nhằm tạo thuận lợi cho việc
ghi đè phương thức gọi phương thức.
Hành Thay đổi hành vi hiện tại của phương Thêm hoặc mở rộng cho hành vi của
vi thức phương thức
Đa Thể hiện tính đa hình tại run time Thể hiện tính đa hình tại compile
hình
Danh Danh sách tham số phải giống nhau Danh sách tham số khác nhau(số
sách lượng, thứ tự, kiểu dữ liệu)
tham
số
Giá trị Kiểu trả về bắt buộc phải giống nhau Kiểu trả về có thể khác nhau
trả về
Phạm Xảy ra giữa 2 class có quan hệ kế thừa Xảy ra trong phạm vi cùng 1 class
vi
Câu 12: Trình bày khái niệm Hàm bạn, lớp bạn. Ưu nhược điểm. Cho ví dụ minh họa.
- Hàm bạn là hàm tự do, không thuộc lớp. Tuy nhiên hàm bạn trong có quyền truy cập
các thành viên private của lớp. Một lớp có thể có nhiều hàm bạn, và chúng phải nằm
bên ngoài class.
- Lớp bạn là một lớp được khai báo bên ngoài lớp khác nhưng được cấp quyền truy cập
vào các thành viên private và protected của lớp đó. Lớp bạn có thể được khai báo bên
trong một lớp khác hoặc bên ngoài lớp đó.
- Ưu điểm của hàm bạn và lớp bạn là giúp tăng tính đóng gói và bảo mật của dữ liệu
trong lập trình hướng đối tượng. Tuy nhiên, việc sử dụng quá nhiều hàm bạn và lớp
bạn có thể dẫn đến việc giảm tính kế thừa và đa hình của chương trình.
- VD:
class MyClass {
private:
int x = 10;
public:
friend void myFriendFunction(MyClass obj);
friend class MyFriendClass;
};
class MyFriendClass {
public:
void showPrivate(MyClass obj) {
cout << "x is: " << obj.x << endl;
}
}
Trong ví dụ trên, myFriendFunction() được khai báo là một hàm bạn của lớp
MyClass. MyFriendClass được khai báo là một lớp bạn của lớp MyClass.
Câu 13: Nêu vai trò của hàm tạo (Constructor), hàm hủy (destructor) trong định nghĩa lớp.
- 3 constructor: constructor có tham số, default constructor, hàm tạo sao chép.
- Constructor là hàm dùng để khởi tạo giá trị cho đối tượng, đáp ứng tính sẵn sàng.
Được khai báo như một phương thức, tên phương thức trùng với tên lớp nhưng không
có kiểu dữ liệu trả về. Một class có thể có nhiều constructor. Constructor phải có
thuộc tính public.
- Destructor là hàm dùng để hủy đối tượng khi hết phạm vi sử dụng. Được khai báo
như một phương thức, tên phương thức trùng với tên lớp và có dấu ~ ở trước. Không
có kiểu dữ liệu trả về. Destructor phải được khai báo ở nhãn public và chỉ có duy nhất
1 destructor trong class.
Câu 14: Trình bày phép gán trong lập trình hướng đối tượng. Tại sao phải xây dựng phép gán
cho lớp? Cho ví dụ minh họa.
+ toán tử gán không tạo ra đối tượng mới ,chỉ thực hiện phép gán giữa 2 đối tượng đã tồn tại
+ trong đa số các trường hợp khi lớp không có thành phần con trỏ hay tham chiếu thì toán tử gán
mặc định là đủ dùng và không cần định nghĩa một phương thức toán tử gán cho lớp
- Phép gán trong lập trình hướng đối tượng là một phép toán dùng để gán giá trị của
một đối tượng cho một đối tượng khác. Phép gán được xây dựng bằng cách sử dụng
toán tử =. Khi sử dụng phép gán, giá trị của đối tượng bên phải được gán cho đối
tượng bên trái.
- Việc xây dựng phép gán cho lớp là cần thiết vì khi sử dụng một con trỏ đối tượng
thuộc
lớp cơ sở, chúng ta không biết chắc được đối tượng mà con trỏ đó sẽ giữ có kiểu dữ
liệu gì nó có thể là một đối tượng thuộc lớp cơ sở hoặc lớp dẫn xuất.
- VD:
#include <iostream>
using namespace std;
class MyClass {
public:
int x;
MyClass(int a) { x = a; }
MyClass operator = (MyClass obj) {
x = obj.x;
return *this;
}
};
int main() {
MyClass obj1(10), obj2(20);
cout << "Before assignment: " << endl;
cout << "obj1.x = " << obj1.x << endl;
cout << "obj2.x = " << obj2.x << endl;
obj1 = obj2;
return 0;
}
Trong ví dụ này, chúng ta đã xây dựng phép gán cho lớp MyClass. Phép gán này
được thực hiện bằng cách sử dụng toán tử =. Khi thực hiện phép gán obj1 = obj2, giá
trị của obj2 được sao chép vào obj1.
Câu 15: Trình bày kỹ thuật nạp chồng (overloading) trong các tình huống không lập trình hướng
đối tượng, trong lập trình hướng đối tượng và hàm bạn. Cho ví dụ minh họa?
- Nạp chồng (overloading) là một kỹ thuật trong lập trình cho phép bạn định nghĩa
nhiều hàm hoặc toán tử có cùng tên nhưng khác nhau về số lượng tham số hoặc kiểu
dữ liệu của các tham số.
- Ví dụ, trong lập trình hướng đối tượng, bạn có thể nạp chồng phương thức để cho
phép một lớp có nhiều phương thức cùng tên nhưng khác nhau về số lượng tham số
hoặc kiểu dữ liệu của các tham số.
- Trong lập trình không hướng đối tượng, bạn có thể sử dụng nạp chồng hàm để định
nghĩa nhiều hàm có cùng tên nhưng khác nhau về số lượng tham số hoặc kiểu dữ liệu
của các tham số.
- Trong hàm bạn, bạn có thể sử dụng nạp chồng hàm để định nghĩa nhiều hàm có cùng
tên nhưng khác nhau về số lượng tham số hoặc kiểu dữ liệu của các tham số.
- VD:
#include <iostream>
using namespace std;
int add(int x, int y) {
return x + y;
}
double add(double x, double y) {
return x + y;
}
int main() {
int a = 1, b = 2;
double c = 1.5, d = 2.5;
cout << add(a, b) << endl; // Output: 3
cout << add(c, d) << endl; // Output: 4
return 0;
}
Trong ví dụ này, chúng ta đã định nghĩa hai hàm add() với hai kiểu dữ liệu khác nhau:
int và double. Khi gọi hàm add() với các giá trị tương ứng, chương trình sẽ gọi hàm
add() tương ứng với kiểu dữ liệu của các giá trị được truyền vào.
Câu 16: Trình bày kỹ thuật đa năng hóa toán tử (nạp chồng toán tử) trong xây dựng một lớp. So
sánh với cách xây dựng hàm tính toán tương ứng với toán tử. Cho ví dụ minh họa.
- Nạp chồng toán tử (operator overloading) là một kỹ thuật trong lập trình hướng đối
tượng cho phép ta định nghĩa lại các toán tử có sẵn trong ngôn ngữ lập trình để thực
hiện các phép tính trên các đối tượng của lớp mà ta định nghĩa. Kỹ thuật này cho
phép ta sử dụng các toán tử như +, -, *, /, %,… để thực hiện các phép tính trên các đối
tượng của lớp mà ta định nghĩa.
- Cách xây dựng hàm tính toán tương ứng với toán tử là một cách khác để thực hiện
việc này. Tuy nhiên, việc sử dụng nạp chồng toán tử sẽ giúp cho mã nguồn của chúng
ta trở nên ngắn gọn và dễ đọc hơn.
- VD:
#include <iostream>
using namespace std;
class PhanSo {
private:
int tuSo;
int mauSo;
public:
PhanSo(int tu = 0, int mau = 1) {
tuSo = tu;
mauSo = mau;
}
PhanSo operator+(PhanSo const &obj) {
PhanSo res;
res.tuSo = tuSo * obj.mauSo + obj.tuSo * mauSo;
res.mauSo = mauSo * obj.mauSo;
return res;
}
void display() { cout << tuSo << "/" << mauSo << endl; }
};
int main() {
PhanSo a(1, 2), b(2, 3);
PhanSo ketQua = a + b; // Tương đương với PhanSo ketQua = a.cong(b);
ketQua.display();
return 0;
}
Câu 17: Trình bày kỹ thuật liên kết động. Cho ví dụ minh họa
- Kỹ thuật liên kết động (Dynamic binding) là một trong những kỹ thuật lập trình
hướng đối tượng (OOP) cho phép chương trình quyết định phương thức nào sẽ được
gọi tại thời điểm chạy chương trình.
- Một lời gọi xuất phát từ con trỏ tới phương thức ảo không liên kết với một phương
thức cố định , mà tùy thuộc vào nội dung con trỏ. Đó là sự liên kết động và phương
thức được liên kết thay đổi mỗi khi có sự thay đổi nội dung con trỏ trong quá trình
chạy chương trình.
- Ví dụ minh họa về kỹ thuật liên kết động trong ngôn ngữ C++:
#include <iostream>
using namespace std;
class Animal {
public:
virtual void sound() {
cout << "Animal sound" << endl;
}
};
class Dog : public Animal {
public:
void sound() {
cout << "Woof" << endl;
}
};
class Cat : public Animal {
public:
void sound() {
cout << "Meow" << endl;
}
};
int main() {
Animal *animal;
Dog dog;
Cat cat;
animal = &dog;
animal->sound();
animal = &cat;
animal->sound();
return 0;
}
Trong ví dụ trên, ta có 3 lớp: Animal, Dog và Cat. Lớp Animal là lớp cha của lớp Dog
và lớp Cat. Lớp cha này có một phương thức ảo (virtual method) là sound(). Lớp con
Dog và Cat đều override lại phương thức này.
Trong hàm main(), ta khai báo một con trỏ kiểu Animal và khởi tạo nó bằng một đối
tượng kiểu Dog. Sau đó, ta gọi phương thức sound() thông qua con trỏ này. Khi chạy
chương trình, phương thức sound() của lớp Dog sẽ được gọi.
Tiếp theo, ta khởi tạo con trỏ kiểu Animal bằng một đối tượng kiểu Cat và gọi
phương thức sound(). Lần này, phương thức sound() của lớp Cat sẽ được gọi.
Câu 18: Trình bày toán tử (). Cho ví dụ minh họa.
- Toán tử () trong C++ được gọi là toán tử hàm. Nó cho phép định nghĩa một hàm để
thực hiện một phép tính như một toán tử.
- Ví dụ:
#include <iostream>
using namespace std;
class Box {
protected:
double length;
public:
void setLength(double len) { length = len; }
double getLength() {return length;}
Box operator() (double len) {
Box box;
box.length = len;
return box;
}
};
int main() {
Box Box1;
Box Box2;
Box1.setLength(6.0);
Box2.setLength(8.0);
cout << "Volume of Box1 : " << Box1.getLength() <<endl;
cout << "Volume of Box2 : " << Box2.getLength() <<endl;
return 0;
}
Trong ví dụ trên, lớp Box có một toán tử hàm được định nghĩa bằng cách sử dụng từ
khóa operator() để tạo ra một hộp mới với các kích thước được chỉ định.
Câu 19: Trình bày con trỏ hàm. Cho ví dụ minh họa.
- Con trỏ hàm trong C++ là một biến lưu trữ địa chỉ của một hàm. Con trỏ hàm được
sử dụng để gán địa chỉ của hàm cho con trỏ hàm, gọi một hàm bằng con trỏ hàm,
truyền con trỏ hàm vào hàm dưới dạng đối số.
- VD:
#include <iostream>
using namespace std;
int add(int a, int b) {
return a + b;
}
int main() {
int (*p)(int, int);
p = add;
cout << p(2, 3) << endl;
return 0;
}
Trong ví dụ này, chúng ta khai báo một con trỏ hàm tên là p và có kiểu là int (*)(int, int).
Sau đó, chúng ta gán địa chỉ của hàm add cho con trỏ p. Cuối cùng, chúng ta gọi hàm add
thông qua con trỏ p.
Câu 20: So sánh hàm tạo sao chép và phép gán. Cho ví dụ minh hoạ.
- Hàm tạo sao chép được sử dụng để khởi tạo một đối tượng mới từ một đối tượng đã
có.
- Toán tử gán không tạo ra đối tượng mới, chỉ thực hiện phép gán giữa 2 đối tượng đã
tồn taị
+ Hàm tạo sao chép sẽ khởi tạo một đối tượng mới và cấp phát bộ nhớ mới cho nó,
trong khi phép gán sẽ sao chép giá trị của các thành viên từ đối tượng nguồn sang đối
tượng đích.
Nếu đã xây dựng toán tử gán mà lại dùng hàm tạo sao chép mặc định thì lại chưa đủ .
Vì việc khởi gán trong câu lệnh khởi báo sẽ không gọi tới toán tử gán mà lại gọi tới
hàm tạo sao chép
Câu lệnh new(chứa dấu bằng sẽ gọi đến hàm tạo)
+ Hàm tạo sao chép không thể được gọi trực tiếp, trong khi phép gán có thể được gọi
trực tiếp.
+ Ví dụ minh họa về hàm tạo sao chép và phép gán trong lập trình C++:
#include <iostream>
using namespace std;
class MyClass {
public:
int x;
MyClass() { cout << "Constructor called." << endl; }
MyClass(const MyClass& obj) {
x = obj.x;
cout << "Copy constructor called." << endl;
}
MyClass& operator=(const MyClass& obj) {
x = obj.x;
cout << "Assignment operator called." << endl;
return *this;
}
};
int main() {
MyClass obj1;
obj1.x = 10;
MyClass obj2 = obj1; // Copy constructor called.
cout << "obj2.x = " << obj2.x << endl;
MyClass obj3;
obj3 = obj1; // Assignment operator called.
cout << "obj3.x = " << obj3.x << endl;
return 0;
}
Trong ví dụ này, hàm tạo sao chép và phép gán của lớp MyClass được định nghĩa để
hiển thị thông báo khi chúng được gọi. Trong hàm main(), hai đối tượng obj2 và obj3
được khởi tạo từ đối tượng obj1 bằng cách sử dụng hàm tạo sao chép và phép gán.