PHẦN I: LÝ THUYẾTTìm hiểu kĩ thuật bắt lỗi vào/ ra: các lỗi error- status bít của lớp ios, các hàm đọc và thiết lập bít lỗi.. Các lỗi stream Từ trước đến nay chúng ta đã sử dụng các đối
Trang 1BÀI TẬP LỚN LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG
ĐỀ TÀI
“Tìm hiểu về kỹ thuật bắt lỗi vào/ra: Các bit lỗi (error-status bits) của lớp ios, các hàm đọc và thiết lập bit lỗi Các hàm chuyển đổi xâu thành số
Kỹ thuật bắt lỗi sử dụng Exception Viết chương trình nhập vào một phân
số, đưa phân số đã nhập ra màn hình theo dạng ts/ms đã rút gọn Chương trình cần kiểm tra sự hợp lệ của tử số và mẫu số, nếu không hợp lệ thì thông báo lý do sai và yêu cầu nhập lại Cho rằng tử số và mẫu số hợp lệ phải là các số nguyên kiểu int Các số nguyên phải được nhập vào dưới dạng xâu ký tự Trong chương trình có cài đặt Exception cho lớp phân
số”.
Giảng viên hướng dẫn: Ngô Công Thắng
Nhóm sinh viên thực hiện:
Nguyễn Thị Hồng Anh Đào Thị Thanh Dung
Lê Thị Dung Nguyễn Thị Mai Hoa
Hà Nội – 5/2010
Trang 2MỤC LỤC
PHẦN I: LÝ THUYẾT 3
I Giới thiệu 3
I.1 Lớp ios 3
I.2 Các lỗi stream 3
II Các bít trạng thái lỗi của lớp ios (error-status bits) 3
II.2 Các thao tác trên các bit lỗi 4
III Các hàm đọc và thiết lập các bít lỗi 5
III.1 Nhập vào các số 5
III.2 Nhập quá nhiều kí tự 6
III.3 Không nhập gì 7
IV Tìm hiểu các hàm chuyển đổi xâu thành số 8
V Kỹ thuật bắt lỗi sử dụng Exception 10
V.1 Thành phần Xử lý ngoại lệ 10
V.2 Stack Unwinding 11
PHẦN II CHƯƠNG TRÌNH 12
ĐỀ BÀI: 12
CHƯƠNG TRÌNH: 12
TÀI LIỆU THAM KHẢO 18
Trang 3PHẦN I: LÝ THUYẾT
Tìm hiểu kĩ thuật bắt lỗi vào/ ra: các lỗi (error- status bít) của lớp ios, các hàm đọc và thiết lập bít lỗi Tìm hiểu các hàm chuyển đổi xâu thành số.
I Giới thiệu
I.1 Lớp ios
Lớp ios là lớp cơ sở của tất cả các lớp stream và chứa các đặc điểm chính cần cho hoạt động của các stream C++ Ba đặc điểm quan trọng nhất là cờ định dạng, bit trạng thái lỗi và chế độ hoạt động file
I.2 Các lỗi stream
Từ trước đến nay chúng ta đã sử dụng các đối tượng cin và cout để
nhập vào và đưa ra mà không cần biết gì thêm, chẳng hạn:
Cout<<”hello”;
Và cin>>n;
Tuy nhiên, cách sử dụng này cho rằng không có lỗi gì xảy ra trong quá trình vào/ra Điều này không phải lúc nào cũng đúng Điều gì xảy ra nếu người sử dụng nhập vào chuỗi “chin” thay vì một số nguyên 9, hoặc
ấn enter mà không nhập gì cả? Điều gì sẽ xảy ra nếu có một phần cứng bị
lỗi? Chúng ta sẽ khảo sát những vấn đề như vậy trong phần này Nhiều kĩ thuật cũng thích hợp với vào ra file
II Các bít trạng thái lỗi của lớp ios (error-status bits)
II.1 Các bit trạng thái lỗi
Các bít trạng thái lỗi là một số enum ios báo cáo các lỗi xảy ra
trong một hoạt động vào hoặc ra Chúng bao gồm các bít sau:
Trang 4
Các bit trạng thái-lỗi
Eofbit: Kết thúc tập tin; cờ này được kích hoạt nếu gặp dấu kết
thúc tập tin Nói cách khác khi kênh nhập không còn kí tự để đọc tiếp nữa
Failbit: bít này được bật khi thao tác vào ra tiếp theo không thể
tiến hành được
Badbit: Bít này được bật khi kênh ở trạng thái không thể khôi
phục được
Failbit và badbit chỉ khác nhau đối với các kênh nhập Khi failbit
được kích hoạt, các thông tin trước đó trong kênh nhập không bị mất; trong khi đó điều này không đúng với badbit
Có thể nói rằng một thao tác tiếp theo phải chờ cho đến khi: -Trạng thái lỗi được sửa chữa
-Các cờ lỗi được tắt
II.2 Các thao tác trên các bit lỗi
II.2.1 Đọc giá trị
Trong lớp ios có định nghĩa 5 hàm thành viên:
- eof(): Trả về 1 nếu gặp dấu kết thúc file, có nghĩa là eofbit được
kích hoạt
- bad(): Trả về 1 nếu badbit được bật.
- fail(): Trả về 1 nếu failbit được bật.
- good(): Trả về 1 nếu ba hàm trên cho giá trị 0.
- rdstate():
+Trả về một số nguyên dương ứng với tất cả các cờ lỗi
+ Có thể dùng để kiểm tra goodbit, badbit, v.v + Sử dụng good(), bad() thì hơn
II.2.2 Thay đổi các trạng thái lỗi
good
eofbit
failbit
badbit
hardbit
Không có lỗi( không có bít nào được thiết lập)
Đã tới cuối file Hoạt động bị lỗi ( lỗi người sử dụng, EOF sớm) Hoạt động không hợp lệ
Lỗi không thể khôi phục
Trang 5Trong istream/ostream có hàm thành phần:
void clear(int i=0);
để bật các bit lỗi tương ứng với giá trị được sử dụng làm tham số Thông thường, ta xác định giá trị đó dựa trên các hằng số của các cờ lỗi Chẳng hạn, nếu f1 biểu thị 1 kênh, chỉ thị:
f1.clear(ios::badbit); sẽ bật cờ lỗi badbit và tắt tất cả các cờ
còn lại
Nếu ta không muốn bật cờ này đồng thời không muốn thay đổi giá trị các cờ khác, sử dụng chỉ thị sau:
f1.clear(ios::badbit|f1.rdstate());
II.2.3 Định nghĩa các toán tử () và !
Có thể kiểm tra một kênh bằng cách xem nó như 1 giá trị logic Điều này được thực hiện nhờ việc định nghĩa chồng trong lớp ios các tóan tử () và !
Chi tiết hơn, toán tử ()được định nghĩa chồng dưới dạng (trong đó f1 biểu thị 1 dòng):
(f1)
-trả về 1 giá trị khác 0 nếu các cờ lỗi được tắt, có nghĩa là hàm
good() có giá trị bằng 1.
-trả về () trong trường hợp ngược lại, có nghĩa là khi good() có giá
trị 0
III Các hàm đọc và thiết lập các bít lỗi
Nhiều hàm ios có thể dùng để đọc thậm chí thiết lập các bít lỗi này III.1 Nhập vào các số
Chúng ta cùng xem cách kiểm soát lỗi khi nhập vào các số Cách này áp dụng cho các đối tượng đọc vào từ bàn phím và đĩa
Các hàm cho các bít lỗi:
Int=eof()
Int=fail()
Int= bad()
Int=good()
Trả lại true nếu EOF được thiết lập Trả lại true nếu failbit, hoặc badbit hoặc hardfail được
thiết lập
Trả lại true nếu badbit hoạc hardfail được thiết lập Trả lại true nếu mọi thứ đều tốt, không có bit nào được
thiết lập
Trang 6Clear(int=0) Không có đối số, xóa tất cả các bit lỗi; nếu có đối số sẽ
thiết lập các bit xác định, như trong clear(ios::failbit)
Ví dụ 1: Ý tưởng là kiểm tra giá trị của goodbit, báo hiệu một lỗi
nếu nó không phải là true và cho người sử dụng một cơ hội khác để nhập vào cho đúng
#include<iostream>
using namespace std;
int main()
{ int var;
while (1) {
cout<<"\n\t Nhap vao mot so nguyen:";
cin>>var;
if(cin.good()) //neu khong co loi nao {
cin.ignore(10,'\n');
break;
} cin.clear();
cout<<"\n\t nhap sai yeu cau nhap lai";
cin.ignore(10,'\n');
} cout<<"\n\t so nguyen var la:"<<var<<endl<<"\t";
return 0;
}
Lỗi thông dụng nhất mà hệ thống này tìm kiếm được khi đọc vào
từ bàn phím là người sử dụng gõ vào không phải chữ số (chẳng hạn như
“chin” thay vì 9) Điều này làm cho failbit được thiết lập Tuy nhiên nó
cũng tìm kiếm những hư hỏng liên quan đến hệ thống mà thông dụng là file đĩa
Các số dấu phẩy động (float, double, longdouble ) có thể được phân tích để tìm lỗi như với các số nguyên(int).
Trang 7III.2 Nhập quá nhiều kí tự
Các kí tự phụ có thể là một vấn đề khi đọc vào từ các stream vào Điều này đặc biệt đúng khi có lỗi Điển hình là các kí tự phụ để lại trong stream sau khi việc nhập vào được coi như là hoàn tất Lúc đó chúng được truyền tới hoạt động vào tiếp theo mặc dù chúng không được dùng cho họat động tiếp theo này Thường thì một ‘\n’ vẫn còn ở lại Nhưng dôi khi các kí tự khác cũng được để lại Để giũ sạch những kí tự phụ này,
hàm thành viên ignore(max,delim) của istream được sử dụng Nó đọc
và ném đi lên tới max kí tự, bao gồm cả kí tự giới hạn được chỉ rõ ở đối
số thứ hai
Trong ví dụ trên, dòng lệnh:
cin.ignore(10,’\n’)
Làm cho cin đọc tới 10 kí tự, bao gồm cả kí tự ‘\n’ và hủy bỏ chúng khỏi stream vào.
III.3 Không nhập gì
Các kí tự trắng như TAB, SPACE và “\n” thường được bỏ qua khi
nhập vào các số Điều này có thể gây ra một vài tác động phụ không mong muốn
Ví dụ, người sử dụng được thông báo nhập vào 1 số, có thể chỉ ấn ENTER mà ko gõ bất kì chữ số nào (có thể họ sẽ nghĩ rằng như thế sẽ nhập vào 0 hoặc có thể họ bối rối ko biết nên làm gì) Trong chương trình trên, câu lệnh đơn giản:
cin>>var;
nếu ấn Enter cũng làm cho con trỏ rơi xuống dòng tiếp theo trong khi stream vẫn đợi nhập vào một số
Có vấn đề gì với việc con trỏ rơi xuống dòng tiếp theo?
+ Thứ nhất, những người lập trình không có kinh nghiệm, thấy
chương trình không chấp nhận khi ấn Enter có thể cho rằng máy tính bị hỏng
+ Thứ hai, ấn Enter lặp đi lặp lại làm cho con trỏ càng ngày càng
rơi xuống thấp cho đến khi toàn bộ màn hình bắt đầu cuốn lên Việc này
chẳng có vấn đề gì trong tương tác kiểu teletype, ở đó chương trình và
người sử dụng chỉ đơn giản gõ phím với nhau Tuy nhiên, trong các
Trang 8chương trình đồ họa văn bản (text-base graphics), việc cuốn màn hình
làm đảo lộn màn hình và cuối cùng phá hủy hoàn toàn màn hình
Bởi vậy, có thể bảo stream vào không bỏ qua các kí tự trắng
(whitespace) là rất quan trọng Điều này được thực hiện bằng cách xóa cờ
skipws:
Ví dụ 2:
#include<iostream.h>
void main()
{
int var;
cout<<"\n\tNhap vao mot so:";
cin.unsetf(ios::skipws);//khong bo qua dau phan cach cin>>var;
if (cin.good())
{
cout<<"\n\tKhong co loi\n";
} else cout<<"\n\tCo loi.\n";
}
Bây giờ nếu người sử dụng chỉ ấn Enter mà ko gõ bất kì chữ số
nào, failbit sẽ được thiết lập và một lỗi sẽ được tạo ra Lúc đó chương
trình có thể bảo người sử dụng phải làm gì hoặc di chuyển con trỏ để màn hình không bị cuốn
IV Tìm hiểu các hàm chuyển đổi xâu thành số.
Có hiệu lực khi có bao hàm: #include<ctype.h> và
#include<stdlib.h>
Gồm các hàm sau:
atoi(<xâu kí tự số nguyên>)
Biến <xâu kí tự số nguyên> đó thành kiểu nguyên (2B)
Vd: ‘123’->123
atol(<xâu kí tự số nguyên dài>)
Biến <xâu kí tự số nguyên dài> thành kiểu số nguyên dài
(4B)
Trang 9Vd: “123456789”->123456789
atof(<xâu kí tự số thực>)
Biến <xâu kí tự số thực> thành kiểu số thực độ chính xác
cao double (8B)
Vd: “1.23”->1.23 Tất cả các hàm này nhận một tham số và trả về giá trị số (int, long hoặc float)
Tuy nhiên float 4B là kiểu thực độ chính xác không cao, giá trị tuyệt đối của nó thuộc khoảng 3.4*10-38 đến 3.4*1038 mà thôi, những số dưới khoảng đó được cho là bằng 0.0; còn kiểu double 8B thì từ 1.7*10
-308 đến 1.7*10308, nên gọi là số thực độ chính xác kép
Ví dụ 3: Dùng hàm chuyển đổi kiểu để nhập dữ liệu:
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<conio.h>
#include<string.h>
using namespace std;
int main()
{
char phanso[30],tu[7],*mau;
int tuso,mauso;
cout<<"\n\tNhap xau dang a/b : ";
gets(phanso);
cin.unsetf(ios::skipws);//khong bo qua ki tu trang!
mau=strstr(phanso,"/");
int i=strlen(phanso)-strlen(mau);
strncpy(tu,phanso,i);
tuso=atoi(tu);
mauso=atoi(mau+1);
cout<<"\n\txau da nhap : "<<phanso;
int t,m;
Trang 10m=mauso*2;
cout<<"\n\ttuso : "<<tuso<<endl
<<"\t2*tuso : "<<t
<<"\n\n\tmauso : "<<mauso
<<"\n\tmauso*2 : "<<m<<endl;
getch();
return 0;
}
V Kỹ thuật bắt lỗi sử dụng Exception
Exception là một công cụ rất mạnh mẽ và linh hoạt để xử lý lỗi thời gian chạy hiệu quả Một ngoại lệ(exception) có thể là một loại cơ bản như int hay char Nó khắc phục những hạn chế các phương pháp xử
lý lỗi truyền thống của C++ Tuy nhiên, ngoại lệ xử lý, giống như tính năng ngôn ngữ khác, có thể dễ dàng bị lạm dụng Để sử dụng tính năng này một cách hiệu quả, điều quan trọng là làm thế nào để máy móc hiểu
và thực hiện
V.1 Thành phần Xử lý ngoại lệ
Xử lý ngoại lệ là một cơ chế để chuyển điều khiển từ một điểm trong một chương trình có phép toán ngoại lệ xảy ra được xử lý Trường hợp ngoại lệ là các biến được xây dựng bên trong các loại dữ liệu hoặc
các đối tượng lớp Việc xử lý ngoại lệ bao gồm bốn thành phần: một try block, một chuỗi của một hay nhiều handlers kết hợp với một try block, một biểu thức try, và throw expression Những try blog chặn có chứa mã
mà có thể ném một ngoại lệ Ví dụ:
try
{
int * p = new int[1000000]; //có thể ném std::bad_alloc
}
Một handler được gọi chỉ bằng một biểu thức ném(a throw
expression) đó là thực hiện trong xử lý thử chặn hoặc trong chức năng đó
được gọi là các từ khóa của handler Một ném biểu hiện(A throw
Trang 11Ví dụ:
try
{
throw 5; // 5 is assigned to n in the following catch statement
}
catch(int n)
{
}
Một ném biểu hiện tương tự như một tuyên bố trở lại, là một sản phẩm nào vứt bỏ một tuyên bố mà không có toán hạng Ví dụ:
throw;
Một ném rỗng bên trong xử lý một chỉ ra một rethrow Nếu không,
có ngoại lệ là đang được xử lý, thực hiện một cuộc gọi chấm dứt ném trống ()
V.2 Stack Unwinding
Khi một ngoại lệ được ném, cơ chế thời gian chạy tìm kiếm đầu tiên cho một xử lý thích hợp trong phạm vi hiện hành Nếu như một xử lý không tồn tại, phạm vi hiện tại là đã thoát và chặn đó là cao hơn trong chuỗi gọi được nhập vào phạm vi Quá trình này lặp đi lặp lại: Nó tiếp tục cho đến khi xử lý thích hợp đã được tìm thấy Một ngoại lệ được coi là xử
lý khi nhập cảnh của nó để xử lý một Tại thời điểm này, các stack đã được unwound và tất cả các đối tượng địa phương đã được xây dựng trên đường đi từ một thử chặn ném vào một biểu thức có được tiêu huỷ Trong
sự vắng mặt của một xử lý thích hợp, chương trình chấm dứt Lưu ý, tuy nhiên, C + +, đảm bảo đúng tiêu huỷ của các đối tượng địa phương chỉ khi ném ngoại lệ được xử lý Cho dù là một ngoại lệ còn tự do nguyên nhân sự hủy diệt của các đối tượng địa phương trong stack ươm là triển khai thực hiện phụ thuộc Để đảm bảo rằng destructors của các đối tượng địa phương được viện dẫn trong trường hợp ngoại lệ còn tự do, bạn có thể thêm tất cả trong main () Ví dụ:
int main()
{
try
{
//
Trang 12}
catch(std::exception& stdexc) // handle expected exceptions {
//
}
catch( ) // ensure proper cleanup in the case of an uncaught exception
{
}
return 0;
}
Quá trình stack unwinding rất giống với một chuỗi các báo cáo lại, mỗi trở về cùng một đối tượng để gọi nó
Liên tục sao chép các đối tượng được thông qua giá trị rất tốn kém
vì các đối tượng ngoại lệ có thể được xây dựng và phá hủy nhiều lần trước khi xử lý phù hợp đã được tìm thấy Tuy nhiên, nó chỉ xảy ra khi một ngoại lệ được ném, mà chỉ xảy ra bất thường và những tình huống hiếm hoi Trong hoàn cảnh này, xem xét hiệu suất để duy trì tính toàn vẹn của một ứng dụng
PHẦN II CHƯƠNG TRÌNH
ĐỀ BÀI:
Viết chương trình nhập vào một phân số, đưa phân số đã nhập ra màn hình theo dạng ts/ms đã rút gọn Chương trình cần kiểm tra sự hợp
lệ của tử số và mẫu số, nếu không hợp lệ thì thông báo lý do sai và yêu cầu nhập lại Cho rằng tử số và mẫu số hợp lệ phải là các số nguyên kiểu int Các số nguyên phải được nhập vào dưới dạng xâu ký tự.
Trang 13CHƯƠNG TRÌNH:
#include<iostream.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
//Lop phan so
class phanso
{
private:
int tu,mau;
public:
void nhap();
void hien();
void rutgon();
};
//Khai bao chuong trinh con
void tennhom();
int uscln(int tu, int mau);
void ctcps();
void menu();
int ktint(char * str);
int nhap_tu_so();
int nhap_mau_so();
//Chuong trinh chinh
int main()
{ int lc;
tennhom();
do
{
menu();
cin>>lc;
cin.ignore();
switch (lc)
Trang 14case 1:
ctcps();
break;
} }
while ((lc<2)&&(lc>=1));
return 0;
}
//Chuong trinh con
void menu()
{
cout<<"\n\n\t\t===CHUONG TRINH NHAP VA HIEN MOT PHAN SO===";
cout<<"\n\t\t ===SU DUNG CHUYEN DOI XAU THANH SO===\n";
cout<<"\n\t\t\t1 Phan so";
cout<<"\n\t\t\t2 Thoat\n";
cout<<"\n\t\t\tNhap vao lua chon: ";
}
void ctcps()
{
phanso a;
cout<<"\nNhap vao phan so:\n";
a.nhap();
a.rutgon();
cout<<"\nPhan so toi gian la: ";
a.hien();
}
int uscln(int tu, int mau)
{
if (tu<mau)
{
if (tu==0) return mau;
return uscln(mau%tu,tu);
}
if (tu>mau) return uscln(tu%mau,mau);
}