Bài giảng lập trình hướng đối tượng - Thầy Cường Học viện bưu chính viễn thông TP HCM
Trang 1Chương 7
Hệ thống Nhập/Xuất C++
• Cơ sở Nhập/Xuất C++
• Nhập/Xuất có định dạng
• Sử dụng width(), precision() và fill()
• Sử dụng bộ thao tác Nhập/Xuất
• Tạo bộ chèn
• Tạo bộ chiết
Trang 3I/ Cơ sở Nhập/Xuất C++
Hệ thống Nhập/Xuất của C++ cũng điều khiển các stream
• Stream là một thiết bị logic có chức năng tạo ra hoặc sử dụng thông tin Nhờ hệ thống Nhập/Xuất của C++, mỗi stream được liên kết với thiết bị vật lý Cho dù có nhiều loại thiết bị vật lý khác nhau, nhưng các stream đều được xử lý như nhau
Chính vì vậy mà hệ thống Nhập/Xuất có thể vận hành trên bất kỳ loại thiết bị nào
Khi một chương trình C++ được thực thi sẽ có bốn stream được mở một cách tự động
Stream Ý nghiã Thiết bị mặc định
cin thiết bị nhập chuẩn Bàn phím
cout thiết bị xuất chuẩn Màn hình
cerr thiết bị báo lỗi chuẩn Màn hình
clog Phiên bản của cerr Màn hình
Có thể định lại cho các stream để liên kết gắn với các thiết bị xuất nhập khác
• Trong chương I, C++ cung cấp các hổ trợ cho hệ thống Nhập/Xuất trong file iostream.h Nội dung của file này là các phân cấp lớp hổ trợ các thao tác Nhập/Xuất C++ có hai phân cấp lớp Nhập/Xuất, chúng có liên hệ với nhau nhưng chúng không giống nhau
Phân cấp lớp Nhập/Xuất thứ nhất được suy dẫn từ lớp Nhập/Xuất mức thấp tên là
streambuf Lớp này cung cấp các thao tác cơ bản của Nhập/Xuất cấp thấp, cũng như
các hổ trợ cơ bản cho toàn bộ hệ thống Nhập/Xuất của C++ Lập trình viên trình độ
cao nên sử dụng trực tiếp lớp streambuf này
Phân cấp lớp Nhập/Xuất thứ hai có tên ios , là lớp Nhập/Xuất cấp cao Nó cung cấp
các thao tác về định dạng, kiểm lỗi, chứa nhiều hàm và biến dùng để điều khiển và kiểm soát các thao tác cơ bản của các stream Nhập/Xuất
Lớp ios là lớp cơ sở để suy dẫn ra các lớp istream, ostream và iostream.h Ba lớp
dẫn xuất này được sử dụng để tạo ra các stream Nhập, Xuất và Nhập/Xuất
Trang 4II/ Nhập/Xuất có định dạng
1/ Mỗi stream của C++ được đi kèm với các cờ định dạng (format flags)
Các cờ định dạng này xác định cách thức thể hiện của dữ liệu, các cờ được mã hoá dưới dạng số nguyên kiểu long int Các cờ định dạng được đặt tên và giá trị trong lớp
ios qua bảng liệt kê như sau :
// ios format flags
2/ Mô tả các cờ định dạng
• Khi cờ skipws được thiết lập, các ký tự cách (gồm các ký tự khoảng cách, tab
và xuống dòng) được bỏ đi khi đọc một stream Nếu cờ này bị xoá, các ký tự cách sẽ không bị bỏ đi Thường áp dụng cho một số kiểu file trên diã
Trang 5• Khi cờ left được thiết lập, kết xuất sẽ được canh biên trái
• Khi cờ right được thiết lập, kết xuất sẽ được canh biên phải
• Khi cờ internal được thiết lập, dấu của trị số được canh biên trái trong khi con
số được canh biên trái Nếu cả ba cờ đều bị xoá, theo mặc định, kết xuất sẽ được canh biên phải
• Theo mặc định, giá trị nguyên được trình bày dưới dạng số thập phân Tuy
nhiên có thể thay đổi cơ số của kết xuất Thiết lập cờ oct sẽ làm cho kết xuất được trình bày dưới dạng số hệ bát phân Thiết lập cờ hex kết xuất là số hệ thập lục Để trả lại kiểu số thập phân, cần thiết lập cờ dec
• Khi đặt cờ showbase, cơ số của giá trị số được trình bày
• Khi đặt cờ showpoint cho phép in dấu chấm thập phân và các số không đi sau
kèm theo các giá trị kiểu dấu chấm động
• Theo mặc định, khi một con số kiểu số mũ được in ra, chữ "e" được trình bày ở kiểu chữ thường Tương tự, khi in một giá trị số ở hệ thập lục, ký tự " dùng chỉ
hệ thập lục được trình bày ở kiểu chữ thường Khi cờ upcase được thiết lập, các
ký tự nói trên sẽ được trình bày bằng kiểu chữ in hoa
• Khi đặt cờ showpos làm cho xuất hiện dấu cộng phiá trước các giá trị số dương
• Khi đặt cờ scientific làm các giá trị số kiểu dấu chấm động được trình bày dươí dạng mũ Nhưng khi đặt cờ fixed, các giá trị số kiểu dấu chấm động được trình
bày dươí dạng thường có 6 số lẽ
• Khi đặt cờ unitbuf, hệ thống Nhập/Xuất của C++ đẩy stream xuất ra hết sau
mỗi thao tác xuất
• Khi đặt cờ stdio, thiết bị xuất chuẩn stdout và thiết bị nhập chuẩn stdin sẽ được
đẩy ra sau mỗi thao tác xuất Tuy nhiên cờ này không được định nghiã trong chuẩn trù định ANSI C++, và có thể không áp dụng trong một số trình biên dịch
3/ Các hàm liên quan đến cờ định dạng
Trang 6• Hàm setf() dùng thiết lập cờ định dạng Hàm này thuộc lớp ios Dạng tổng quát
:
long setf(long flags) ;
Hàm trả về giá trị được thiết lập trước đó của cờ và thiết lập các cờ có tên trong hàm Ví dụ thiết lập cờ showpos :
stream.setf(ios::showpos) ;
stream là tên stream cần để thiết lập cờ showpos
Lưu yù, trong C++, không thể đặt cờ định dạng ở phạm vi toàn cục chung cho tất cả các stream, mỗi stream sẽ mang một thông tin trạng thái các định dạng riêng của nó và chúng độc lập với nhau
Có thể gọi hàm setf() để lập nhiều cờ cùng lúc, bằng cách dùng toán tử OR Ví dụ
cout.setf(ios::showbase | ios::hex) ;
• Hàm xoá cờ unsetf(), dùng để xoá một hay nhiều cờ định dạng Dạng tổng quát
:
long unsetf(long flags) ;
Hàm trả về giá trị trước đó của cờ
• Hàm flags() cho biết trạng thái của cờ định dạng Hàm này thuộc lớp ios Dạng
thường gọi : long flags() ;
Dạng có kèm đối số long flags(long f) ;
cho phép thiết lập tất cả các cờ định dạng của stream dựa theo giá trị của đối số khi
gọi hàm Từng bit của giá trị f sẽ được gán cho biến lưu trị cờ định dạng, và sẽ xoá tình trạng cờ định dạng trước đó Hàm sẽ trả về giá trị tình trạng các cờ định dạng trước đó
#include <iostream.h>
Trang 7// now, change formats
cout.unsetf(ios::dec); // not required by all compilers
Trang 9if(f & ios::skipws) cout << "skipws on\n";
else cout << "skipws off\n";
if(f & ios::left) cout << "left on\n";
else cout << "left off\n";
if(f & ios::right) cout << "right on\n";
else cout << "right off\n";
if(f & ios::internal) cout << "internal on\n";
else cout << "internal off\n";
if(f & ios::dec) cout << "dec on\n";
else cout << "dec off\n";
if(f & ios::oct) cout << "oct on\n";
else cout << "oct off\n";
if(f & ios::hex) cout << "hex on\n";
else cout << "hex off\n";
if(f & ios::showbase) cout << "showbase on\n";
else cout << "showbase off\n";
if(f & ios::showpoint) cout << "showpiont on\n";
else cout << "showpoint off\n";
if(f & ios::showpos) cout << "showpos on\n";
else cout << "showpos off\n";
if(f & ios::uppercase) cout << "uppercase on\n";
else cout << "uppercase off\n";
if(f & ios::scientific) cout << "scientific on\n";
else cout << "scientific off\n";
if(f & ios::fixed) cout << "fixed on\n";
else cout << "fixed off\n";
Trang 10if(f & ios::unitbuf) cout << "unitbuf on\n";
else cout << "unitbuf off\n";
if(f & ios::stdio) cout << "stdio on\n";
else cout << "stdio off\n";
oct is on
hex is off
showpoint is off uppercase is off showpos is off scientific is off
unitbuf is off stdio is off
Ví dụ 2.4 Minh hoạ hàm flags(), sử dụng mặt nạ cờ (flag mask) dùng để thiết lập
các cờ showpos, showbase, oct và right Bốn cờ này mang giá trị tương ứng là 0x0400, 0x0080, 0x0020, 0x0004 Cộng bốn giá trị này ta có giá trị 0x04A4
#include <iostream.h>
void showflags() ;
int main()
Trang 112 Hãy viết chương trình để thiết lập trạng thái các cờ định dạng của stream cout sao cho các giá trị số kiểu dấu chấm động được trình bày với dấu chấm thập phân Ngoài
ra, các giá trị số kiểu dấu chấm động được in ra dưới dạng số mũ, với ký tự "E" chỉ phần mũ được trình bày bằng kiểu chữ in hoa
3 Hãy viết chương trình thực hiện chức năng lưu lại trạng thái hiện tại của các cờ định dạng, tiếp đến thiết lập hai cờ showbase và hex, rồi xuất ra giá trị 100, cuối cùng lập lại trạng thái ban đầu của các cờ trạng thái
III/ Sử dụng các hàm width(), precision() và fill()
Trang 12Các hàm này thiết lập các tham số định dạng gồm : độ rộng của trường, độ chính xác và ký tự lấp đầy
Hàm int width(int w) ; với w là độ rộng của trường
Hàm trả về độ rộng của trường trước đó
Hàm int precision(int p); với p là chính xác cần thiết lập
Hàm trả về giá trị là chính xác trước đó
Hàm char fill (char ch); với ch là ký tự lấp đầy mới
Hàm trả về ký tự lấp đầy trước đó
Ví dụ 3.1
#include <iostream.h>
int main()
{
cout.width(10); // set minimum field width
cout << "hello" << '\n'; // right justify by default
cout.fill('%'); // set fill character
cout.width(12); // set width
cout << "hello" << '\n'; // right justify by default
cout.setf(ios::left); // left justify
cout.width(9); // set width
cout << "hello" << '\n'; // output left justified
cout.width(12); // set width
cout.precision(10); // set 10 digits of precision
cout << 123.234567 << '\n';
cout.width(10); // set width
cout.precision(3); // set 3 digits of precision
cout << 123.234567 << '\n';
Trang 13// Create a table of square roots and squares
Bài tập III
Kết quả của chương trình
20 4.4721 400
Trang 141 Hãy viết chương trình thiết in ra logarit cơ số tự nhiên và logarit cơ số thập phân của các con số từ 2 đến 100 Định dạng bảng số sao cho các con số được canh biên phải với độ rộng trường là 10 Sử dụng độ chính xác là 5
2 Tạo một hàm mang tên là : void center(char *s) ;
Chức năng của hàm là canh giữa một chuỗi ký tự trên màn hình Để thực hiện hàm này, hãy sử dụng hàm width() Giả sử màn hình có độ rộng 80 cột (và để cho đơn giản, giả sử chuỗi ký tự có chiều dài < 80 ký tự)
IV/ Sử dụng bộ thao tác Nhập/Xuất (I/O Manipulator)
Bộ thao tác Nhập/Xuất là các hàm đặc biệt sử dụng trong các mệnh đề Nhập/Xuất
Cần phải nạp file tiêu đề iomanip.h vào chương trình Các bộ thao tác Nhập/Xuất
chuẩn được trình bày trong bảng sau :
dec Định dạng dữ liệu kiểu số ở dạng thập phân Xuất
endl Xuất một ký tự xuống dòng và
đẩy stream ra thiết bị vật lý Xuất ends Xuất một ký tự rỗng Xuất
flush Đẩy stream ra thiết bị vật lý Xuất
hex Định dạng dữ liệu kiểu số ở dạng thập lục phân Xuất
oct Định dạng dữ liệu kiểu số ở dạng bát phân Xuất
resetiosflags(long f) Xóa các cờ xác định trong f Nhập/Xuất setbase(int base) Đặt cơ số thành base Xuất
setfill(int ch) Đặt ch làm ký tự lấp đầy Xuất
setiosflags(long f) Thiết lập các cờ xác định trong f Nhập/Xuất setprecision(int p) Đặt độ chính xác là p Xuất
setw(int w) Đặt độ rộng của trường là w Xuất
ws Bỏ qua các ký tự cách (khoảng cách, tab, xuống
dòng)
Nhập
Các bộ thao tác có thể xuất hiện trong các mệnh đề Nhập/Xuất Ví dụ
cout << oct << 100 << hex << 100;
Trang 15cout << setw(10) << 100;
Lưu ý các bộ thao tác Nhập/Xuất chỉ có ảnh hưởng đến stream có tên trong biểu thức Nhập/Xuất, các bộ thao tác này không ảnh hưởng đến tất cả các stream đang mở
• Sử dụng bộ thao tác Nhập/Xuất là cách thứ hai để định dạng thông tin trong hệ thống Nhập/Xuất của C++ Trong một số trường hợp, sử dụng bộ thao tác Nhập/Xuất dễ dàng hơn là dùng các cờ và hàm định dạng
Ưu điểm so với các hàm định dạng khác của ios là
+ tính dễ sử dụng
+ mã chương trình sẽ ngắn gọn hơn
Nếu muốn thiết lập một số cờ định dạng bằng bộ thao tác, hãy dùng setiosflags() Bộ
thao tác này có chức năng giống như hàm setf()
Nếu muốn xoá một số cờ định dạng bằng bộ thao tác, hãy dùng resetiosflags() Bộ
thao tác này có chức năng giống như hàm unsetf()
Ví dụ 4.1 Sử dụng bộ thao tác Nhập/Xuất
#include <iostream.h>
#include <iomanip.h>
int main()
{
cout << hex << 100 << endl; // 64
cout << oct << 10 << endl; // 12
cout << setfill('X') << setw(10);
cout << 100 << " hi " << endl; // XXXXXXX144 hi
return 0;
}
Ví dụ 4.2
Trang 16// This version uses I/O manipulators to display the table of squares and square roots
V Tạo bộ chèn vào (inserter)
Kết quả chương trình
Trang 17Quá tải toán tử Nhập/Xuất cho các lớp tự tạo là một trong những lý do quyết định sử
dụng mệnh đề Nhập/Xuất của C++ thay cho việc sử dụng các hàm Nhập/Xuất theo kiểu C
• Theo C++, thao tác xuất còn được gọi là chèn (insert), toán tử << còn gọi là
toán tử chèn Khi quá tải toán tử này, nghiã là tạo một hàm chèn, hay còn gọi là
bộ chèn (inserter) , do toán tử << thực hiện chức năng chèn thông tin vào trong một stream
Cú pháp các bộ chèn
ostream &operator << (ostream &stream, class_name ob)
{
// body of inserter }
stream : tham số thứ nhất tham chiếu đến một đối tượng thuộc kiểu ostream
Stream stream phải là một stream xuất
ob : tham số thứ hai là đối tượng cần xuất (nó có thể là một tham chiếu)
Bộ chèn sẽ trả về một tham chiếu đến stream stream kiểu ostream Điều này cần
thiết khi sử dụng toán tử << trong các biểu thức Nhập/Xuất phức tạp, ví dụ
cout << ob1 << ob2 << ob2;
Nội dung của bộ chèn có thể thực hiện bất kỳ thủ tục nào mà lập trình viên muốn Kinh nghiệm cho biết, nên giới hạn chức năng của nó trong phạm vi kết xuất thông tin ra một stream
• Một bộ chèn không được phép là thành phần của lớp mà bộ chèn này thao tác
Lý do, khi một hàm toán tử kiểu bất kỳ là một thành phần của một lớp nào đó, toán hạng bên trái vốn được tham chiếu không tường minh bằng con trỏ, lại là một đối
tượng phát sinh ra việc gọi hàm toán tử Có nghiã là, toán hạng bên trái là một đối
tượng của chính lớp đoù
Đối với bộ chèn, toán hạng bên trái (của <<) là một stream thay vì một đối tượng,
và toán hạng bên phải là một đối tượng cần được kết xuất
Trang 18@ C++ cho phép bộ chèn là hàm friend của lớp mà nó thao tác
Ví dụ 5.1 Lớp coord chứa bộ chèn là hàm friend
// Use a friend inserter for objects of type coord
@ Lập trình viên nên tránh cách xây dựng bộ chèn bị hạn chế bởi việc kết xuất
thông tin ra thiết bị xuất chuẩn gắn với stream cout
ostream &operator<<(ostream &stream, coord ob)
{
Kết quả chương trình
1, 1
10, 23
Trang 19cout << ob.x << ", " << ob.y << '\n';
return stream;
}
Ví dụ 5.2 Lớp coord chứa bộ chèn không phải là hàm friend
// Create an inserter for objects of type coord, using a non-friend inserter
// An inserter for the coord class
ostream &operator<<(ostream &stream, coord ob)
• Một bộ chèn không chỉ giởi hạn ở việc xuất thông tin dạng văn bản Nó có thể
thực hiện các thao tác hay chuyển đổi thông tin cần thiết để kết xuất thông tin ra
thiết bị riêng biệt hay một tình huống riêng biệt nào đó
Ví dụ 5.3 Bộ chèn xuất thông tin ra máy vẽ (plotter)