Tải bản đầy đủ (.doc) (17 trang)

BÁO CÁO LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG Tìm hiểu về kỹ thuật bắt lỗi vàora

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (171.22 KB, 17 trang )

BÀ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
Lớp: Tin52c
Hà Nội – 5/2010
1
MỤC LỤC
V. Kỹ thuật bắt lỗi sử dụng Exception 9
V.1. Thành phần Xử lý ngoại lệ 10
V.2. Stack Unwinding 10
PHẦ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.
2
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:
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.
Tên Ý nghĩa
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
3
 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

Trong 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):
4
(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:
Hàm Chức năng
Int=eof()

Int=fail()
Int= bad()
Int=good()
Clear(int=0)
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
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;
5
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).
III.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ì

6
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
chươ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";
}
7
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)
Vd: “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*10
38
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*10
308
, 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>
8
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;
t=tuso*2;
m=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
9
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
expression) bao gồm từ khóa và ném một biểu thức chuyển nhượng.
Ví 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
10
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
{
//
}
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
11
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ự.
CHƯƠ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();
};
12
//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)
{
case 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";
13
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);
}
int nhap_tu_so()
{
int gttu;
char ts[10];
while(1)
{

cout<<"\nNhap tu so:";
cin.unsetf(ios::skipws);//ko bo qua cac ki tu trang
cin>>ts;
if (ktint(ts)!=1)
{
cout<<"\nLoi: Tu so khong phai so nguyen.";
cin.clear(ios::failbit);//thiet lap failbit
}
if (cin.good())
{
cin.ignore(10,'\n');
14
gttu=atoi(ts);
break;
}
cin.clear();
cin.ignore(10,'\n');
}
return gttu;
}
int nhap_mau_so()
{
int gtmau;
char ms[10];
do
{
while(1)
{
cout<<"\nNhap mau so:";
cin.unsetf(ios::skipws);//ko bo qua cac ki tu trang

cin>>ms;
if (ktint(ms)!=1)
{
cout<<"\nLoi: Mau so khong phai so nguyen.";
cin.clear(ios::failbit);//thiet lap failbit
}
if (cin.good())
{
cin.ignore(10,'\n');
gtmau=atoi(ms);
break;
}
cin.clear();
cin.ignore(10,'\n');
}
if (gtmau==0) cout<<"\nLoi: Mau so bang 0. Nhap lai.\n";
}while (gtmau==0);
return gtmau;
}
int ktint(char * str) //Ham tra ve true neu chuoi bieu dien so nguyen
15
{
int len=strlen(str);
if (len==0||len>5) return 0;
for (int j=0;j<len;j++)
if ((str[j]<'0'||str[j]>'9') && str[j]!='-') return 0;
double n=atof(str);
if (n<-32768||n>32767) return 0;
return 1;
}

void tennhom()
{
cout<<"\n\t\tBAI TAP LON MON LAP TRINH HUONG DOI
TUONG\n";
cout<<"\n\tDe tai: Nhap vao 1 phan so voi tu va mau la nhung ";
cout<<"so nguyen\n\t\t(co kiem tra dieu kien cua tu so va mau
so),";
cout<<"\n\t\tdua phan so da nhap ra man hinh duoi dang rut gon.";
cout<<"\n\tGiang vien huong dan:\t\tNgo Cong Thang";
cout<<"\n\tNhom sinh vien thuc hien:";
cout<<"\n\t\t\t\t\tNguyen Thi Hong Anh";
cout<<"\n\t\t\t\t\tDao Thi Thanh Dung.";
cout<<"\n\t\t\t\t\tLe Thi Dung";
cout<<"\n\t\t\t\t\tNguyen Thi Mai Hoa";
}
//Dinh nghia cac ham cua lop
void phanso::nhap()
{
tu=nhap_tu_so();
mau=nhap_mau_so();
}
void phanso::rutgon()
{
//chi co tu so la co the mang dau -
if (mau<0)
{
tu=-tu;
mau=-mau;
16
}

//tim uscln
int usc=(tu>=0) ? uscln(tu,mau) : uscln(-tu,mau);
tu/=usc;
mau/=usc;
}
void phanso::hien()
{
if (tu==0)
cout<<0;
else
if (tu==mau)
cout<<1;
else
if (tu==-mau)
cout<<-1;
else
if (mau==0) cout<<tu;
else
{
cout<<tu<<"/"<<mau;
}
}
TÀI LIỆU THAM KHẢO
1. Lập trình hướng đối tượng với C++. Nguyễn Thanh Thuỷ(chủ
biên). Nxb Khoa học và kỹ thuật. Hà Nội 2009.
2. Ngôn ngữ lập trình C++ và Cấu trúc dữ liệu. TS. Nguyễn Việt
Hương. Nxb Giáo dục.
3. Bài giảng Lập trình hướng đối tượng. GV Ngô Công Thắng.
Trường ĐH Nông Nghiệp Hà Nội.
4. ANSI/ISO C++ Professional Programmer's Handbook

5.Tài liệu tham khảo trên mạng.
17

×