Đây là bài thực hành thực tế tiên phong về lập trình hướng đối tượng người dùng, vậy nên tất cả chúng ta sẽ tập trung chuyên sâu vào những kỹ năng và kiến thức cơ bản nhất. Đã đến lúc những bạn dừng việc theo dõi hướng dẫn trong bài học kinh nghiệm và cố tự mình triển khai bài tập nho nhỏ sau đây. Đây sẽ là 1 thời cơ tốt để kiểm nghiệm lại những kiến thức và kỹ năng đã được học và bổ khuyết những lỗ hổng mà những bạn chưa nắm vững .
Trong bài thực hành này, các bạn sẽ phải viết 1 lớp mô tả khái niệm phân số. Trong C++ thì chúng ta có kiểu dữ liệu int
cho số nguyên và double
cho số thực thế nhưng không có kiểu nào dành cho phân số cả. Nhiệm vụ của các bạn hôm nay sẽ là bổ sung thêm kiểu dữ liệu đó.
Chuẩn bị trước khi lập trình – Những lời khuyên
Lớp mà tất cả chúng ta chuẩn bị sẵn sàng tạo ra không quá phức tạp và không yên cầu nhiều sức lực lao động cũng hoàn toàn có thể nghĩ ra những phương pháp và phép toán mà tất cả chúng ta cần sử dụng .
Bài tập này dùng để kiểm tra kỹ năng và kiến thức của những bạn về những khái niệm sau :
- Thuộc tính và quyền truy cập
- Phương thức khởi tạo
- Ghi đè các phép toán
Hãy xem lại chúng 1 lần ở đầu cuối rồi tất cả chúng ta cùng khởi đầu !
Đặc điểm của lớp PhanSoX
Bởi vì lớp của tất cả chúng ta dùng để miêu tả khái niệm « phân số » nên PhanSo là 1 lựa chọn không tồi cho tên lớp. Tôi thêm vào chữ « X » mang ý nghĩa là mới, tân tiến ( kiểu như RockmanX so với Rockman vậy ). Vậy là tất cả chúng ta có tên của lớp : PhanSoX .
Sử dụng kiểu dữ liệu int
và double
thật là vô cùng dễ dàng. Chúng ta chỉ cần khai báo, khởi tạo giá trị ban đầu và rồi đã có thể sử dụng chúng trong các phép toán. Để làm được điều tương tự với PhanSoX, các bạn sẽ cần bỏ ra thêm không ít công sức. Tiếp theo đó, chúng ta cũng cần có thể so sánh các PhanSoX với nhau thì mới coi như cơ bản hoàn thiện.
Mục tiêu ở đầu cuối là đoạn mã sau đây hoàn toàn có thể được biên dịch thành công xuất sắc và hoạt động giải trí đúng chuẩn .
#include#include "PhanSoX.h" using namespace std; int main(){ PhanSoX a(4,5); //Khai bao phan so 4/5 PhanSoX b(2); // Khai bao phan so 2/1 (= 2) PhanSoX c,d; // Khai bao 2 phan so bang 0 c = a+b; //Tinh 4/5 + 2/1 = 14/5 d = a*b; //Tinh 4/5 * 2/1 = 8/5 cout << a << " + " << b << " = " << c << endl; cout << a << " * " << b << " = " << d << endl; if(a > b) cout << "a lon hon b." << endl; else if(a==b) cout << "a bang b." << endl; else cout << "a nho hon b." << endl; return 0; }
Để làm được như vậy, tất cả chúng ta cần phải :
- Tạo ra lớp cùng những thuộc tính
- Tạo ra 1 số phương pháp khởi tạo
-
Ghi đè các phép toán, ít nhất là
+
,*
,<<
,<
và==
Ngoài ra thì PhanSoX còn phải bảo vệ giá trị được lưu trong đối tượng người tiêu dùng là 1 phân số đã được rút gọn, nghĩa là 4/5 thay vì 8/10 hay 12/15 .
Đây là tổng thể những nhu yếu cơ bản cần quan tâm khi tạo ra lớp PhanSoX. Tôi nghĩ hoàn toàn có thể một số ít kỹ năng và kiến thức toán học sẽ hữu dụng trong bài thực hành thực tế này. Nếu những bạn đã sẵn sàng chuẩn bị thì tất cả chúng ta hãy bắt tay vào làm thôi !
Tôi sẽ không bỏ rơi những bạn ngay đâu. Một vài hướng dẫn sau đây sẽ rất hữu dụng để những bạn hoàn toàn có thể khởi đầu .
Tạo 1 dự án mới
Để khởi đầu bài thực hành thực tế này, những bạn cần tạo 1 dự án Bất Động Sản mới. Hãy tự do sử dụng IDE nếu những bạn muốn. Chúng ta biết rằng trong dự án Bất Động Sản này sẽ có tối thiểu 3 tệp .
-
main.cpp : tệp này chứa hàm
main()
. Trong hàmmain()
, chúng ta sẽ tạo ra các đối tượng của lớp PhanSoX và kiểm tra các tính năng mà chúng ta đã liệt kê bên trên. - PhanSoX. h : chứa nguyên mẫu của lớp PhanSoX với list những thuộc tính và phương pháp của nó .
- PhanSoX. cpp : chứa mã giải quyết và xử lý của những phương pháp được nêu bên trên .
! Cần chú ý quan tâm là tên những tệp. cpp và. h phải giống với tên của lớp nếu không chương trình hoàn toàn có thể không chạy .
Khung mã nguồn của các tệp
Để mở màn, tất cả chúng ta xác lập khung cơ bản nhất của những đoạn mã trong những tệp .
main.cpp
Ở bên trên, tôi thậm chí đã trình bày cho các bạn mã hoàn chỉnh khi chúng ta viết xong chương trình. Thế nhưng hãy quên nó đi và bắt đầu 1 cách từ từ. Chúng ta sẽ dần dần thêm các dòng lệnh vào main()
tùy theo mức độ phát triển của lớp.
#include#include " PhanSoX.h" using namespace std; int main(){ PhanSoX a(1,5); // Tao ra phan so 1/5 return 0; }
Hãy mở màn với chương trình chỉ gồm có 1 câu lệnh khai báo biến .
PhanSoX.h
Tệp này chứa khai báo của lớp PhanSoX. Chúng ta sẽ bao gồm gói
vì tiếp theo sẽ cần đến, ít nhất là khi chúng ta muốn sử dụng cout
để in ra màn hình khi muốn tìm lỗi trong đoạn mã.
#ifndef DEF_PHANSOX #define DEF_ PHANSOX #includeclass PhanSoX{ public: private: }; #endif
Trước mắt thì lớp này vẫn còn trống và trách nhiệm của những bạn chính là hoàn thành xong nó. Đấy mới là mục tiêu chính của bài thực hành thực tế. Dù vậy thì tôi cũng đã phân ra 2 phần rõ ràng với quyền truy vấn khác nhau. Tôi rất kỳ vọng những bạn sẽ tuân thủ tốt tính đóng gói của lập trình hướng đối tượng người dùng .
! 2 dòng đầu tiên cũng như dòng cuối cùng được IDE tự động thêm vào các tệp .h
với mục đích tránh cho mỗi đoạn mã không bị khai báo nhiều lần. Vậy nên nếu bạn không sử dụng IDE, đừng quên tự tay thêm những dòng này vào tệp tiêu đề.
PhanSoX.cpp
Chính là trong tệp này mà chúng ta sẽ viết mã xử lý của các phương thức. Bởi vì lớp của chúng ta vẫn còn trống nên chưa có đoạn mã nào được viết ở đây. 1 chú ý khác là đừng quên bao gồm tệp tiêu đề PhanSoX.h
.
#include " PhanSoX.h "
Vậy thế là phần khung của chương trình đã cơ bản triển khai xong .
Xác định thuộc tính của lớp
Bước tiên phong khi muốn tạo ra 1 lớp là phải xác lập những thuộc tính của lớp đấy. Tôi nghĩ là sẽ không quá khó khăn vất vả với toàn bộ mọi người .
Tất cả các phân số thì được tạo thành từ 2 thành phần là tử số và mẫu số. 2 thành phần này đều là 2 số nguyên nên chúng ta sẽ thêm vào 2 thuộc tính kiểu int
cho lớp.
#ifndef DEF_PHANSOX #define DEF_ PHANSOX #includeclass PhanSoX{ public: private: int m_tuSo ; int m_mauSo ; }; #endif
Trong tên của thuộc tính, tôi luôn khởi đầu bằng « m_ ». Tôi đã từng nói điều này với những bạn từ những bài học kinh nghiệm trước. Cách đặt tên này giúp tất cả chúng ta thuận tiện phân biệt xem là trong đoạn mã tất cả chúng ta đang thao tác với 1 thuộc tính của lớp hay chỉ là 1 biến địa phương thông thường trong phương pháp .
Các phương thức khởi tạo
Tôi cũng sẽ không hỗ trợ quá nhiều ở đây. Tuy nhiên nếu các bạn tinh ý thì sẽ thấy là trong đoạn mã xử lý của hàm main()
mà tôi trình bày ở đầu bài thực hành, chúng ta có thể có đến 3 phương thức khởi tạo khác nhau.
- Phương thức đầu tiên nhận vào 2 tham số nguyên, lần lượt là tử số và mẫu số. Đây là phương thức mà mọi người hay nghĩ đến đầu tiên khi muốn tạo phân số.
- Phương thức thứ 2 chỉ nhận 1 tham số nguyên và tạo ra 1 phân số có giá trị bằng số nguyên đó. Điều này tương đương với mẫu số của phân số này sẽ bằng 1.
- Phương thức cuối cùng không nhân tham số, là phương thức khởi tạo mặc định, tạo ra 1 phân số có giá trị bằng 0.
Tôi sẽ không lý giải thêm gì nữa. Các bạn hoàn toàn có thể mở màn viết, tối thiểu là phương pháp tiên phong. Một khi đã viết xong nó thì tôi chắc là những phương pháp khởi tạo bên dưới sẽ trở nên hiển nhiên hơn .
Các phép toán
1 phần quan trọng của bài thực hành là viết mã xử lý cho các phép toán. Các bạn cần phải suy nghĩ kỹ trước khi viết mã cũng như áp dụng những gì chúng ta đã nói khi thao tác với lớp ThoiGian trong bài trước để có được 1 hệ thống phép toán kết hợp chặt chẽ. Ví dụ như chú ý kỹ thuật dùng phép toán ==
để định nghĩa !=
hay phép +=
để tạo ra phép +
.
Rút gọn phân số
1 điểm rất quan trọng là những phân số cần phải được rút gọn, nghĩa là 2/5 thay vì 4/10, để dễ thao tác. Tốt nhất là lớp mà tất cả chúng ta tạo ra hoàn toàn có thể tự rút gọn phân số mà nó đang đại diện thay mặt .
Để triển khai giải quyết và xử lý này, tất cả chúng ta sẽ cần sử dụng 1 chiêu thức toán học để rút gọn phân số rồi dịch nó sang ngôn từ C + + .
Nếu muốn rút gọn 1 phân số, trước hết tất cả chúng ta cần tìm ra ước số chung lớn nhất của tử số và mẫu số. Sau đó tất cả chúng ta chia cả tử và mẫu cho ước chung lơn nhất này. Ví dụ như 4/10 thì 4 và 10 có ước chung lớn nhất là 2. Vậy nên tất cả chúng ta sẽ chia cả 4 và 10 cho 2 được 2 và 5 và hiệu quả của phân số rút gọn sẽ là 2/5 .
Để tính ước chung lớn nhất cũng không phải là đơn giản. Sau đây tôi sẽ trình bày với các bạn hàm số để tìm ước chung lớn nhất này dùng giải thuật Euclid. Các bạn có thể thêm nó vào trong tệp PhanSoX.cpp
để sử dụng.
int ucln(int a, int b){ while (b != 0){ const int t = b; b = a % b; a = t; } return a; }
Chúng ta cũng cần thêm nguyên mẫu của nó vào PhanSoX.h
nữa.
#ifndef DEF_PHANSOX #define DEF_ PHANSOX #includeclass PhanSoX{ //Khai bao lop… }; int ucln(int a, int b); #endif
Bây giờ các bạn có thể thoải mái sử dụng hàm này trong các phương thức của lớp.
Vậy là đủ rồi, hãy khởi đầu viết mã thôi .
Đáp án
Đừng đọc phần này nếu những bạn chưa thử tự mình triển khai bài thực hành thực tế. Trái lại, nếu những bạn đã thao tác 1 cách trang nghiêm thì sau đây sẽ là đáp án dùng để những bạn so sánh với những gì mà những bạn đã viết .
Chắc hẳn những bạn cũng đã tốn tương đối thời hạn để nghĩ xem tất cả chúng ta sẽ cần viết những phương pháp và phép toán nào. Nếu những bạn không hề tự làm được hết bài thì cũng không sao cả, bài chữa dưới đây sẽ chỉ ra 1 số ít điểm chính cần quan tâm hoàn toàn có thể giúp ích cho những bạn để triển khai xong chương trình của mình. Sau đấy, những bạn hoàn toàn có thể liên tục rèn luyện với những sáng tạo độc đáo để nâng cấp cải tiến được đề xuất kiến nghị ở cuối bài học kinh nghiệm này .
Chúng ta hãy cùng xem 1 lượt những bước tạo ra chương trình .
Các phương thức khởi tạo
Tôi đã đề xuất kiến nghị là tất cả chúng ta sẽ khởi đầu với phương pháp khởi tạo nhu yếu 2 tham số nguyên là tử số và mẫu số. Đây là phiên bản của tôi .
PhanSoX:: PhanSoX(int tuSo, int mauSo) : m_tuSo(tuSo), m_mauSo(mauSo){ }
Chúng ta sử dụng list giá trị để triển khai khởi tạo. Đến đây thì mọi thứ vẫn thông thường .
Tiếp theo là 2 phương pháp khởi tạo khác lần lượt nhận 1 tham số và không nhận tham số .
PhanSoX:: PhanSoX(int giaTri) : m_tuSo(giaTri), m_mauSo(1){ } PhanSoX:: PhanSoX() : m_tuSo(0), m_mauSo(1){ }
Cần quan tâm là ở đây, số 5 sẽ được trình diễn là 5/1 hay số 0 là 0/1 .
Đến lúc này thì chúng ta vẫn đáp ứng đủ yêu cầu của bài tập. Trước khi chuyển sang viết những thứ phức tạp hơn, hãy viết phép toán <<
cho phép chúng ta hiển thị phân số của chúng ta ra màn hình. Ngoài ra nhờ vậy chúng ta cũng sẽ dễ dàng phát hiện các lỗi của chương trình nếu có.
Hiển thị 1 phân số
Theo như những gì chúng ta đã xem trong bài học trước, giải pháp tốt nhất lúc này là tạo ra 1 phương thức hienThi()
cho lớp và hàm operator<<
sẽ gọi phương thức này để hiển thị đối tượng. Trong trường hợp này, tôi đề nghị chúng ta bắt chước mã nguồn đã viết ở bài trước.
ostream& operator<<(ostream& luong, PhanSoX const& phanSo){ phanSo.hienThi(luong); return luong; }
Và đoạn mã của hienThi()
của tôi như sau :
void PhanSoX::hienThi(ostream& luong) const { if(m_mauSo == 1){ luong << m_tuSo; }else{ luong << m_tuSo << '/' << m_mauSo; } }
! Chú ý từ khóa const
được sử dụng bởi vì phương thức này chỉ hiển thị các thuộc tính của lớp chứ không thay đổi chúng.
Chắc tối thiểu những bạn cũng viết được 1 đoạn mã gần giống như trên. Ở đây tôi phân ra 2 trường hợp khi mẫu số bằng 1 hoặc khác 1. Trong trường hợp mẫu số bằng 1 thì việc hiển thị mẫu số là không thiết yếu .
Phép cộng
Tương tự như phép toán <<
, chúng ta cũng sẽ áp dụng giải pháp chúng ta đã sử dụng ở bài học trước : tạo ra phương thức operator+=()
cho lớp và sử dụng nó trong mã xử lý của operator+()
.
PhanSoX operator+( PhanSoX const& a, PhanSoX const& b){ PhanSoX saoChep(a); saoChep += b; return saoChep; }
Phần khó khăn vất vả nằm ở mã giải quyết và xử lý của phép toán rút gọn .
Đề phòng có bạn nào đó không nhớ thì sau đây là công thức cộng 2 phân số .
Nếu chuyển sang ngôn từ C + + thì tất cả chúng ta sẽ có phương pháp sau :
PhanSoX& PhanSoX::operator+=( PhanSoX const& phanSoKhac){ m_tuSo= phanSoKhac.m_mauSo * m_tuSo+ m_mauSo * phanSoKhac.m_tuSo; m_mauSo = m_mauSo * phanSoKhac.m_mauSo; return *this; }
! Giống như tất cả các phép toán rút gọn khác, operator+=
sẽ trả về *this
. Đây là quy ước.
Phép nhân
Phép nhân của 2 phân số thậm chí còn còn dễ hơn so với phép cộng .
Chắc các bạn sẽ không ngạc nhiên nếu tôi sử dụng đến phương thức operator*=()
chứ. Tôi nghĩ hẳn tất cả mọi người đều đã nắm được nguyên lý.
PhanSoX operator*( PhanSoX const& a, PhanSoX const& b){ PhanSoX saoChep(a); saoChep *= b; return saoChep; } PhanSoX& PhanSoX::operator*=( PhanSoX const& phanSoKhac){ m_tuSo *= phanSoKhac.m_tuSo; m_mauSo *= phanSoKhac.m_mauSo; return *this; }
Các phép toán so sánh bằng
Hai phân số đã rút gọn sẽ bằng nhau nếu cả tử số và mẫu số của chúng đều lần lượt bằng nhau. Thuật toán ở đây sẽ khá đơn giản. Lần này chúng ta cũng vẫn cần 1 phương thức bang()
ở trong lớp và được sử dụng trong các hàm phép toán bên ngoài.
bool PhanSoX::bang(PhanSoX const& phanSoKhac) const { if(m_tuSo == phanSoKhac.m_tuSo && m_mauSo == phanSoKhac.m_mauSo) return true; else return false; } bool operator==( PhanSoX const& a, PhanSoX const& b){ if(a.bang(b)) return true; else return false; } bool operator!=( PhanSoX const& a, PhanSoX const& b){ if(a.bang(b)) return false; else return true; }
Hoặc ngắn gọn hơn :
bool PhanSoX::bang(PhanSoX const& phanSoKhac) const { return (m_tuSo == phanSoKhac.m_tuSo && m_mauSo == phanSoKhac.m_mauSo); } bool operator==( PhanSoX const& a, PhanSoX const& b){ return (a.bang(b)); } bool operator!=( PhanSoX const& a, PhanSoX const& b){ return !(a.bang(b)); }
Một khi phương thức bang()
hoàn thành thì 2 hàm phép toán trở nên quá đơn giản. Hơn nữa phép toán !=
còn sử dụng phép toán ==
, mọi việc trở nên càng đơn giản.
Các phép toán so sánh hơn kém
Chỉ còn sót lại phép toán so sánh hơn kém là tất cả chúng ta sẽ triển khai xong cơ bản chương trình. Có rất nhiều cách để so sánh 2 phân số nhưng dưới đây tôi sẽ dùng 1 công thức có sẵn trong sách toán trung học cơ sở .
Công thức này màn biểu diễn ở trong C + + sẽ như sau .
bool PhanSoX::nhoHon(PhanSoX const& phanSoKhac) const{ if(m_tuSo * phanSoKhac.m_mauSo < m_mauSo * phanSoKhac.m_tuSo) return true; else return false; }
Lần này có đến 4 phép toán hoàn toàn có thể sử dụng phương pháp tất cả chúng ta vừa tạo .
bool operator<( PhanSoX const& a, PhanSoX const& b) { return a.nhoHon(b); } bool operator>( PhanSoX const& a, PhanSoX const& b){ return b.nhoHon(a); } bool operator<=( PhanSoX const& a, PhanSoX const& b){ return !(b.nhoHon(a)); } bool operator>=( PhanSoX const& a, PhanSoX const& b){ return !(a.nhoHon(b)); }
Hoàn thành 4 phép toán này là tất cả chúng ta triển khai xong hết những phép toán cần sử dụng đến trong nhu yếu của đề bài. Chỉ còn lại yếu tố phức tạp nhât, đấy là rút gọn phân số .
Rút gọn phân số
Bên trên tôi đã lý giải là để rút gọn phân số, thứ nhất tất cả chúng ta cần tìm ra ước số chung lớn nhất của tử số và mẫu số, sau đó chia cả tử và mẫu cho ước chung vừa tìm được .
Bởi vì phép toán này sẽ được sử dụng ở nhiều chỗ khác nhau trong chương trình, tôi đề nghĩ tất cả chúng ta sẽ tạo ra 1 phương pháp riêng cho lớp. Thậm chí tất cả chúng ta hoàn toàn có thể chỉ được cho phép sử dụng phương pháp này bên trong lớp bằng cách trao quyền truy vấn private cho nó chính do người dùng bên ngoài cũng không thật sự cần đến nó .
void PhanSoX::rutGon(){ int uocSo = ucln(m_tuSo, m_mauSo); //Tim uoc chung lon nhat m_tuSo /= uocSo; //Roi rut gon phan so m_mauSo /= uocSo; }
? Lúc nào thì cần sử dụng phương thức này ?
Đơn giản là mỗi khi có một phân số mới được tạo ra hoặc bị biến hóa, nghĩa là trong phương pháp khởi tạo bởi hoàn toàn có thể người dùng đưa ra giá trị những tham số tạo thành 1 phân số chưa rút gọn .
PhanSoX::PhanSoX(int tuSo, int mauSo) : m_tuSo(tuSo), m_mauSo(mauSo){ rutGon(); }
Một phân số bị thay đổi khi là kết quả của 1 phép toán như operator+=
hoặc operator*=
.
PhanSoX& PhanSoX::operator+=( PhanSoX const& phanSoKhac){ m_tuSo= phanSoKhac.m_mauSo * m_tuSo+ m_mauSo * phanSoKhac.m_tuSo; m_mauSo = m_mauSo * phanSoKhac.m_mauSo; rutGon() ; return *this; } PhanSoX& PhanSoX::operator*=( PhanSoX const& phanSoKhac){ m_tuSo *= phanSoKhac.m_tuSo; m_mauSo *= phanSoKhac.m_mauSo; rutGon() ; return *this; }
Cuối cùng thì lớp của tất cả chúng ta đã triển khai xong và cung ứng tổng thể những nhu yếu của đầu bài .
Cải tiến chương trình
Chương trình của tất cả chúng ta dù đã phân phối được nhu yếu của bài thực hành thực tế nhưng vẫn hoàn toàn có thể có thêm rất nhiều nâng cấp cải tiến khác chính do có quá nhiều thứ tất cả chúng ta hoàn toàn có thể làm với 1 phân số .
Tôi ý kiến đề nghị những bạn tải mã nguồn của bài thực hành thực tế bên trên trước khi bắt tay vào nâng cấp cải tiến nó .
Tải mã nguồn
Sau đây là 1 vài ý tưởng sáng tạo của tôi mà tất cả chúng ta hoàn toàn có thể làm với những phân số .
- Thêm các phương thức lấy cho phép chúng ta trích xuất giá trị của tử số và mẫu số.
- Thêm phương thức trả về số thực đại diện bởi phân số.
- Đơn giản hóa các phương thức khởi tạo bằng cách sử dụng các giá trị mặc định cho tham số. Chúng ta có thể rút gọn còn 1 phương thức khởi tạo.
- Thêm vào các phép toán khác như phép trừ và phép chia.
- Cải tiến lớp để có thể tạo ra các phân số âm vì hiện giờ lớp của chúng ta mới đáp ứng được các phân số dương. Các bạn có thể sẽ phải đặt ra thêm các quy tắc ví dụ như nếu là phân số âm thì dấu trừ phải ở trên tử số (nghĩa là -1/4 chứ không chấp nhận 1/-4), vv…
- Thêm vào phép toán lấy số đối của 1 số.
- Thêm vào các hàm toán học như
abs()
,sqrt()
haypow()
.
Tôi nghĩ là từng này nâng cấp cải tiến cũng khiến những bạn bộn bề được trong 1 khoảng chừng thời hạn nhất định rồi. Những thực hành thực tế này rất quan trọng để giúp những bạn hình thành lối tâm lý theo hướng đối tượng người tiêu dùng. Và cách tâm lý này chỉ hoàn toàn có thể được hình thành trải qua không ngừng rèn luyên và rèn luyện .
Nếu những bạn có những sáng tạo độc đáo khác, đừng ngại san sẻ và tất cả chúng ta sẽ cùng đàm đạo !
Source: https://final-blade.com
Category: Kiến thức Internet