Bài 1:
- Mã nguồn:
(1) ClassX.h:
#pragma once
#include <string>
// Lớp ClassX: khai báo
class ClassX {
// Thuộc tính, phương thức
public:
ClassX(int x = 0, int y = 0); // Hàm tạo thông thường
ClassX(const ClassX &obj); // Hàm tạo sao chép
ClassX(ClassX &&obj); // Hàm tạo di chuyển
~ClassX(); // Hàm hủy
void scale(int factor); // Điều chỉnh dữ liệu theo hệ số
void show() const; // Hiển thị thông tin đối tượng dưới dạng (x, y)
private:
int x, y;
// Thuộc tính, phương thức lớp
public:
static int get_count(); // Hiển thị số lượng đối tượng hiện tại của lớp
public:
static const std::string description; // Mô tả lớp ClassX
static const int max_objects; // Giới hạn đối tượng tối đa
private:
static int count; // Số lượng đối tượng hiện tại của lớp
// Khai báo hàm bạn của lớp
friend void process();
};
// Khai báo hàm thông thường
void process();
(2) ClassX.cpp:
#include "ClassX.h"
#include <iostream>
#include <string>
// Triển khai lớp ClassX
// Khởi tạo biến tĩnh bên ngoài lớp
const std::string ClassX::description{"Mẫu lớp đơn giản"};
const int ClassX::max_objects = 999;
int ClassX::count = 0;
// Phương thức lớp
int ClassX::get_count() {
return count;
}
// Phương thức đối tượng
ClassX::ClassX(int x, int y): x{x}, y{y} {
++count;
std::cout << "Hàm tạo ClassX được gọi.\n";
}
ClassX::ClassX(const ClassX &obj): x{obj.x}, y{obj.y} {
++count;
std::cout << "Hàm tạo sao chép ClassX được gọi.\n";
}
ClassX::ClassX(ClassX &&obj): x{obj.x}, y{obj.y} {
++count;
std::cout << "Hàm tạo di chuyển ClassX được gọi.\n";
}
ClassX::~ClassX() {
--count;
std::cout << "Hàm hủy ClassX được gọi.\n";
}
void ClassX::scale(int factor) {
x *= factor;
y *= factor;
}
void ClassX::show() const {
std::cout << "(" << x << ", " << y << ")" ;
}
// Triển khai hàm thông thường
void process() {
ClassX obj5(42);
obj5.y = 2049;
std::cout << "obj5 = "; obj5.show(); std::cout << '\n';
std::cout << "process: Số lượng đối tượng ClassX hiện tại: " << ClassX::get_count() << std::endl;
}
(3) test1.cpp:
#include "ClassX.h"
#include <iostream>
void test_ClassX();
int main() {
std::cout << "Kiểm tra lớp ClassX: \n";
test_ClassX();
std::cout << "\nKiểm tra hàm bạn: \n";
process();
}
void test_ClassX() {
using std::cout;
using std::endl;
cout << "ClassX info: " << ClassX::description << endl;
cout << "Số lượng tối đa đối tượng ClassX: " << ClassX::max_objects << endl;
cout << "Số lượng đối tượng ClassX hiện tại: " << ClassX::get_count() << endl << endl;
ClassX obj1;
cout << "obj1 = "; obj1.show(); cout << endl;
ClassX obj2(3, 4);
cout << "obj2 = "; obj2.show(); cout << endl;
ClassX obj3(obj2);
obj3.scale(2);
cout << "obj3 = "; obj3.show(); cout << endl;
ClassX obj4(std::move(obj2));
cout << "obj4 = "; obj4.show(); cout << endl;
cout << "Kiểm tra: Số lượng đối tượng ClassX hiện tại: " << ClassX::get_count() << endl;
}
-
Kết quả chạy chương trình thử nghiệm:
-
Câu trả lời:
(1) CÓ;
(2) Hàm tạo thông thường (dòng 9) dùng để tạo đối tượng lớp từ hai giá trị mặc định là 0; hàm tạo sao chép (dòng 10) sao chép thuộc tính của đối tượng hiện có sang đối tượng mới, tương tự như phép gán; hàm tạo di chuyển (dòng 11) chuyển toàn bộ tài nguyên của tham số đối tượng sang đối tượng mới, đối tượng gốc trở thành rỗng, tương tự như thao tác cắt; hàm hủy (dòng 12) xóa đối tượng đã tạo. Về thời điểm gọi, hàm tạo phụ thuộc vào tham số được cung cấp, trong khi hàm hủy được gọi tự động trước khi giải phóng không gian bộ nhớ;
(3) CÓ, chương trình chạy bình thường và kết quả không thay đổi.
Bài 2:
- Mã nguồn:
(1) Complex.h:
#pragma once
#include <string>
class Complex{
public:
Complex(double a = 0,double b = 0);
Complex(const Complex &c);
~Complex();
double get_real() const;
double get_imag() const;
void add(Complex c);
private:
double real,imag;
friend void output(Complex c);
friend double abs(Complex c);
friend Complex add(Complex a,Complex b);
friend bool is_equal(Complex a,Complex b);
friend bool is_not_equal(Complex a,Complex b);
public:
static const std::string description;
};
(2) Complex.cpp:
#include "Complex.h"
#include <iostream>
#include <cmath>
const std::string Complex::description{"Mẫu lớp đơn giản"};
Complex::Complex(double a,double b): real{a},imag{b}{}
Complex::Complex(const Complex &c): real{c.real},imag{c.imag}{}
Complex::~Complex(){
}
double Complex::get_real() const{
return real;
}
double Complex::get_imag() const{
return imag;
}
void Complex::add(Complex c){
real += c.get_real();
imag += c.get_imag();
}
void output(Complex c){
if(c.get_imag() >= 0)
std::cout << c.get_real() << " + " << c.get_imag() << "i";
else
std::cout << c.get_real() << " - " << (0-c.get_imag()) << "i";
}
double abs(Complex c){
double magnitude = sqrt(c.get_real() * c.get_real() + c.get_imag() * c.get_imag());
return magnitude;
}
Complex add(Complex a,Complex b){
double c_r = a.get_real() + b.get_real();
double c_i = a.get_imag() + b.get_imag();
Complex c(c_r,c_i);
return c;
}
bool is_equal(Complex a,Complex b){
if(a.get_real() == b.get_real() && a.get_imag() == b.get_imag())
return true;
return false;
}
bool is_not_equal(Complex a,Complex b){
if(is_equal(a,b))
return false;
return true;
}
(3) test2.cpp:
// Bổ sung tiêu đề cần thiết
// xxx
#include "Complex.h"
#include <iostream>
#include <iomanip>
#include <complex>
#include <string>
void test_Complex();
void test_std_complex();
int main() {
std::cout << "*******Kiểm tra 1: Lớp Complex tự định nghĩa*******\n";
test_Complex();
std::cout << "\n*******Kiểm tra 2: Lớp complex chuẩn thư viện*******\n";
test_std_complex();
}
void test_Complex() {
using std::cout;
using std::endl;
using std::boolalpha;
cout << "Kiểm tra thành viên lớp: " << endl;
cout << Complex::description << endl << endl;
cout << "Kiểm tra đối tượng Complex: " << endl;
Complex c1;
Complex c2(3, -4);
Complex c3(c2);
Complex c4 = c2;
const Complex c5(3.5);
cout << "c1 = "; output(c1); cout << endl;
cout << "c2 = "; output(c2); cout << endl;
cout << "c3 = "; output(c3); cout << endl;
cout << "c4 = "; output(c4); cout << endl;
cout << "c5.real = " << c5.get_real()
<< ", c5.imag = " << c5.get_imag() << endl << endl;
cout << "Kiểm tra phép toán phức: " << endl;
cout << "abs(c2) = " << abs(c2) << endl;
c1.add(c2);
cout << "c1 += c2, c1 = "; output(c1); cout << endl;
cout << boolalpha;
cout << "c1 == c2 : " << is_equal(c1, c2) << endl;
cout << "c1 != c2 : " << is_not_equal(c1, c2) << endl;
c4 = add(c2, c3);
cout << "c4 = c2 + c3, c4 = "; output(c4); cout << endl;
}
void test_std_complex() {
using std::cout;
using std::endl;
using std::boolalpha;
cout << "Kiểm tra std::complex<double>: " << endl;
std::complex<double> c1;
std::complex<double> c2(3, -4);
std::complex<double> c3(c2);
std::complex<double> c4 = c2;
const std::complex<double> c5(3.5);
cout << "c1 = " << c1 << endl;
cout << "c2 = " << c2 << endl;
cout << "c3 = " << c3 << endl;
cout << "c4 = " << c4 << endl;
cout << "c5.real = " << c5.real()
<< ", c5.imag = " << c5.imag() << endl << endl;
cout << "Kiểm tra phép toán phức: " << endl;
cout << "abs(c2) = " << abs(c2) << endl;
c1 += c2;
cout << "c1 += c2, c1 = " << c1 << endl;
cout << boolalpha;
cout << "c1 == c2 : " << (c1 == c2)<< endl;
cout << "c1 != c2 : " << (c1 != c2) << endl;
c4 = c2 + c3;
cout << "c4 = c2 + c3, c4 = " << c4 << endl;
}
-
Kết quả chạy chương trình thử nghiệm:
-
Câu trả lời:
Câu hỏi 1:
Lớp mẫu chuẩn thư viện gọn gàng hơn; các hàm thực hiện phép toán nhưng không thể biểu diễn trực quan, chỉ có thể hiểu qua tên hàm; tuy nhiên, biểu diễn phép toán của thư viện chuẩn phù hợp hơn với ngôn ngữ toán học;
Câu hỏi 2:
(1) Không. Vì những hàm này có thể thực hiện được trong phạm vi public.
(2) Không.
(3) Theo cá nhân, khi cần truy cập nhiều thuộc tính riêng tư, có thể sử dụng hàm bạn hoặc khi mối quan hệ giữa các hàm bạn mật thiết; khi chức năng có thể thực hiện qua giao diện công khai, nên tránh dùng hàm bạn.
Câu hỏi 3:
Có thể thêm từ khóa explicit trước hàm tạo sao chép để thực hiện.
Bài 3:
- Mã nguồn:
(1) PlayerControl.h:
#pragma once
#include <string>
enum class ControlType {Play, Pause, Next, Prev, Stop, Unknown};
class PlayerControl {
public:
PlayerControl();
ControlType parse(const std::string& control_str); // Chuyển đổi std::string --> ControlType
void execute(ControlType cmd) const; // Thực hiện thao tác điều khiển (mô phỏng bằng in ra)
static int get_count();
private:
static int total_count;
};
(2) PlayerControl.cpp:
#include "PlayerControl.h"
#include <iostream>
#include <algorithm>
int PlayerControl::total_count = 0;
PlayerControl::PlayerControl() {}
// Bổ sung
// 1. Chuyển chuỗi đầu vào thành chữ thường, hỗ trợ không phân biệt hoa thường
// 2. Phân tích "play"/"pause"/"next"/"prev"/"stop" và trả về giá trị enum tương ứng
// 3. Chuỗi không khớp trả về ControlType::Unknown
// 4. Mỗi lần gọi thành công parse, tăng total_count
ControlType PlayerControl::parse(const std::string& control_str) {
// xxx
total_count++;
std::string s = control_str;
for(auto &a : s){
a = std::tolower(a);
}
if(s == "play")
return ControlType::Play;
else if(s == "pause")
return ControlType::Pause;
else if(s == "next")
return ControlType::Next;
else if(s == "prev")
return ControlType::Prev;
else if(s == "stop")
return ControlType::Stop;
else
return ControlType::Unknown;
}
void PlayerControl::execute(ControlType cmd) const {
switch (cmd) {
case ControlType::Play: std::cout << "[play] Phát nhạc...\n"; break;
case ControlType::Pause: std::cout << "[Pause] Dừng nhạc\n"; break;
case ControlType::Next: std::cout << "[Next] Chuyển bài tiếp theo\n"; break;
case ControlType::Prev: std::cout << "[Prev] Quay lại bài trước\n"; break;
case ControlType::Stop: std::cout << "[Stop] Dừng nhạc\n"; break;
default: std::cout << "[Error] điều khiển không xác định\n"; break;
}
}
int PlayerControl::get_count() {
return total_count;
}
(3) test3.cpp:
#include "PlayerControl.h"
#include <iostream>
void test() {
PlayerControl controller;
std::string control_str;
std::cout << "Nhập lệnh điều khiển: (play/pause/next/prev/stop/quit):\n";
while(std::cin >> control_str) {
if(control_str == "quit")
break;
ControlType cmd = controller.parse(control_str);
controller.execute(cmd);
std::cout << "Số lần điều khiển hiện tại: " << PlayerControl::get_count() << "\n\n";
}
}
int main() {
test();
}
- Kết quả chạy chương trình thử nghiệm:
Bài 4:
- Mã nguồn:
(1) Fraction.h:
#pragma once
#include <string>
int find_gcd(int a,int b);
class Fraction{
public:
Fraction(int numerator,int denominator = 1);
Fraction(const Fraction &f);
~Fraction();
int get_numerator() const;
int get_denominator() const;
Fraction negative() const;
static const std::string description;
private:
int numerator,denominator;
friend void output(Fraction f);
friend Fraction add(Fraction a,Fraction b);
friend Fraction sub(Fraction a,Fraction b);
friend Fraction mul(Fraction a,Fraction b);
friend Fraction div(Fraction a,Fraction b);
};
(2) Fraction.cpp:
#include "Fraction.h"
#include <string>
#include <iostream>
Fraction::Fraction(int numerator,int denominator ){
int gcd;
gcd = find_gcd(numerator,denominator);
this->numerator = numerator / gcd;
this->denominator = denominator / gcd;
}
Fraction::Fraction(const Fraction &f): numerator{f.numerator},denominator{f.denominator}{}
Fraction::~Fraction(){
}
const std::string Fraction::description{"Lớp Fraction v 0.01.\nHiện tại chỉ hỗ trợ tạo đối tượng phân số, xuất, phép cộng/trừ/nhân/chia."};
int Fraction::get_numerator() const{
return numerator;
}
int Fraction::get_denominator() const{
return denominator;
}
Fraction Fraction::negative() const{
return Fraction(-numerator,denominator);
}
void output(Fraction f){
if(f.denominator == 0){
std::cout << "Mẫu số không thể bằng 0";
return;
}
if(f.numerator % f.denominator == 0){
std::cout << f.numerator/f.denominator;
return;
}
if(f.denominator < 0){
std::cout << -f.numerator << "/" << -f.denominator;
}else{
std::cout << f.numerator << "/" << f.denominator;
}
}
Fraction add(Fraction a,Fraction b){
int denominator = a.denominator * b.denominator;
int numerator = a.numerator * b.denominator + b.numerator * a.denominator;
return Fraction(numerator,denominator);
}
Fraction sub(Fraction a,Fraction b){
int denominator = a.denominator * b.denominator;
int numerator = a.numerator * b.denominator - b.numerator * a.denominator;
return Fraction(numerator,denominator);
}
Fraction mul(Fraction a,Fraction b){
int denominator = a.denominator * b.denominator;
int numerator = a.numerator *b.numerator;
return Fraction(numerator,denominator);
}
Fraction div(Fraction a,Fraction b){
int denominator = a.denominator * b.numerator;
int numerator = a.numerator *b.denominator;
return Fraction(numerator,denominator);
}
int find_gcd(int a,int b){
while(b != 0){
int temp = b;
b = a % b;
a = temp;
}
return a;
}
(3) test4.cpp:
#include "Fraction.h"
#include <iostream>
void test1();
void test2();
int main() {
std::cout << "Kiểm tra 1: Chức năng cơ bản lớp Fraction\n";
test1();
std::cout << "\nKiểm tra 2: Mẫu số bằng 0: \n";
test2();
}
void test1() {
using std::cout;
using std::endl;
cout << "Kiểm tra lớp Fraction: " << endl;
cout << Fraction::description << endl << endl;
Fraction f1(5);
Fraction f2(3, -4), f3(-18, 12);
Fraction f4(f3);
cout << "f1 = "; output(f1); cout << endl;
cout << "f2 = "; output(f2); cout << endl;
cout << "f3 = "; output(f3); cout << endl;
cout << "f4 = "; output(f4); cout << endl;
const Fraction f5(f4.negative());
cout << "f5 = "; output(f5); cout << endl;
cout << "f5.get_numerator() = " << f5.get_numerator()
<< ", f5.get_denominator() = " << f5.get_denominator() << endl;
cout << "f1 + f2 = "; output(add(f1, f2)); cout << endl;
cout << "f1 - f2 = "; output(sub(f1, f2)); cout << endl;
cout << "f1 * f2 = "; output(mul(f1, f2)); cout << endl;
cout << "f1 / f2 = "; output(div(f1, f2)); cout << endl;
cout << "f4 + f5 = "; output(add(f4, f5)); cout << endl;
}
void test2() {
using std::cout;
using std::endl;
Fraction f6(42, 55), f7(0, 3);
cout << "f6 = "; output(f6); cout << endl;
cout << "f7 = "; output(f7); cout << endl;
cout << "f6 / f7 = "; output(div(f6, f7)); cout << endl;
}
-
Kết quả chạy chương trình thử nghiệm:
-
Câu trả lời:
Tôi chọn phương án hàm bạn. Vì các phép tính chủ yếu được thực hiện giữa các đối tượng cùng lớp và chủ yếu sử dụng thuộc tính riêng tư, giúp viết code dễ dàng và cải thiện hiệu suất; tuy nhiên điều này làm tăng mức độ liên kết, nếu hàm bạn có lỗi sẽ phải sửa nhiều phần code, khó bảo trì hơn. Tuy nhiên do dự án đơn giản và lượng code ít nên có thể chọn phương pháp này.
Tổng kết thí nghiệm:
Việc thực hiện chức năng của đối tượng lớp có nhiều cách, cần sử dụng hợp lý ưu nhược điểm của các phương pháp theo tình huống cụ thể để tối ưu code, tăng tính đọc hiểu và dễ bảo trì sau này.