Tải bản đầy đủ - 0 (trang)
12 Input/Output Operators for User-Defined Types

12 Input/Output Operators for User-Defined Types

Tải bản đầy đủ - 0trang

//io/frac1out.hpp

#include



inline

std::ostream&operator<<(std::ostream&strm,const

{

strm<
returnstrm;

}

Thefunctionwritesthenumeratorandthedenominator,separatedbythe

character'/',tothestreamthatispassedastheargument.The

streamcanbeafilestream,astringstream,orsomeotherstream.To

supportthechainingofwriteoperationsortheaccesstothestreams

stateinthesamestatement,thestreamisreturnedbythefunction.

Thissimpleformhastwodrawbacks:

1. Becauseostreamisusedinthesignature,thefunction

appliesonlytostreamswiththecharactertypechar.Ifthe

functionisintendedonlyforuseinWesternEuropeorinNorth

America,thisisnoproblem.Ontheotherhand,amoregeneral

versionrequiresonlyalittleextrawork,soitshouldatleastbe

considered.

Anotherproblemarisesifafieldwidthisset.Inthiscase,theresultis

probablynotwhatmightbeexpected.Thefieldwidthappliestothe

immediatelyfollowingwrite;inthiscase,tothenumerator.Thus,the

statements



Fractionvat(16,100);//I'mGermanandwehaveau

std::cout<<"VAT:\""<
<


resultinthisoutput:



VAT:"16/100"

Thenextversionsolvesbothoftheseproblems:



//io/frac2out.hpp

#include

#include



template

inline

std::basic_ostream&

operator<<(std::basic_ostream&strm,

constFraction&f)

{

/*stringstream

*-withsameformat

*-withoutspecialfieldwidth

*/

std::basic_ostringstreams;

s.copyfmt(strm);

s.width(0);

//fillstringstream

s<
//printstringstream

strm<
returnstrm;

}



Theoperatorhasbecomeatemplatefunctionthatisparameterizedto

suitallkindsofstreams.Theproblemwiththefieldwidthisaddressedby

writingthefractionfirsttoastringstreamwithoutsettinganyspecific

width.Theconstructedstringisthensenttothestreampassedasthe

argument.Thisresultsinthecharactersrepresentingthefractionbeing

writtenwithonlyonewriteoperation,towhichthefieldwidthisapplied.

Thus,thestatements



Fractionvat(16,100);//I'mGerman...

std::cout<<"VAT:\""<
<
nowproducethefollowingoutput:



VAT:"15/100"

13.12.2ImplementingInputOperators

Inputoperatorsareimplementedaccordingtothesameprincipleas

outputoperators(describedintheprevioussubsection).However,input

incursthelikelyproblemofreadfailures.Inputfunctionsnormallyneed

specialhandlingofcasesinwhichreadingmightfail.

Whenimplementingareadfunctionyoucanchoosebetweensimpleor

flexibleapproaches.Forexample,thefollowingfunctionusesasimple

approach.Itreadsafractionwithoutcheckingforerrorsituations:



//io/frac1in.hpp

#include



inline

std::istream&operator>>(std::istream&strm,Fract

{

intn,d;



strm>>n;//readvalueofthenumerator

strm.ignore();//skip'/'

strm>>d;//readvalueofthedenominator



f=Fraction(n,d);//assignthewholefractio

returnstrm;

}

Thisimplementationhastheproblemthatitcanbeusedonlyforstreams

withthecharactertypechar.Inaddition,whetherthecharacter

betweenthetwonumbersisindeedthecharacter'/'isnotchecked.

Anotherproblemariseswhenundefinedvaluesareread.Whenreadinga

zeroforthedenominator,thevalueofthereadfractionisnotwelldefined.Thisproblemisdetectedintheconstructoroftheclass

FractionthatisinvokedbytheexpressionFraction(n,d).

However,handlinginsideclassFractionmeansthataformaterror

automaticallyresultsinanerrorhandlingoftheclassFraction.

Becauseitiscommonpracticetorecordformaterrorsinthestream,it

mightbebettertosetios_base::failbitinthiscase.

Lastly,thefractionpassedbyreferencemightbemodifiedevenifthe

readoperationisnotsuccessful.Thiscanhappen,forexample,whenthe

readofthenumeratorsucceeds,butthereadofthedenominatorfails.

Thisbehaviorcontradictscommonconventionsestablishedbythe

predefinedinputoperators,andthusisbestavoided.Areadoperation

shouldbesuccessfulorhavenoeffect.

Thefollowingimplementationisimprovedtoavoidtheseproblems.Itis

alsomoreflexiblebecauseitisparameterizedtobeapplicabletoall

streamtypes:



//io/frac2in.hpp



#include



template

inline

std::basic_istream&

operator>>(std::basic_istream&strm,

{

intn,d;

//readvalueofnumerator

strm>>n;

/*ifavailable

*-read'/'andvalueofdemonimator

*/

if(strm.peek()=='/'){

strm.ignore();

strm>>d;

}

else{

d=1;

}

/*ifdenominatoriszero

*-setfailbitasI/Oformaterror

*/

if(d==0){

strm.setstate(std::ios::failbit);

returnstrm;

}

/*ifeverythingisfinesofar

*changethevalueofthefraction



*/

if(strm){

f=Fraction(n,d);

}

returnstrm;

}

Herethedenominatorisreadonlyifthefirstnumberisfollowedbythe

character'/';otherwise,adenominatorofoneisassumedandthe

integerreadisinterpretedasthewholefraction.Hence,thedenominator

isoptional.

Thisimplementationalsotestswhetheradenominatorwithvalue0was

read.Inthiscase,theios_base::failbitisset,whichmight

triggeracorrespondingexception(seeSection13.4.4).Ofcourse,the

behaviorcanbeimplementeddifferentlyifthedenominatoriszero.For

example,anexceptioncouldbethrowndirectly,orthecheckcouldbe

skippedsothatthefractionisinitializedwithzero,whichwouldthrowthe

appropriateexceptionbyclassFraction.

Lastly,thestateofthestreamischeckedandthenewvalueisassigned

tothefractiononlyifnoinputerroroccurred.Thisfinalcheckshould

alwaysbedonetomakesurethatthevalueofanobjectischangedonly

ifthereadwassuccessful.

Ofcourse,itcanbearguedwhetheritisreasonabletoreadintegersas

fractions.Inaddition,thereareothersubtletiesthatmaybeimproved.For

example,thenumeratormustbefollowedbythecharacter'/'without

separatingwhitespaces.Butthedenominatormaybeprecededby

arbitrarywhitespacesbecausenormallytheseareskipped.Thishintsat

thecomplexityinvolvedinreadingnontrivialdatastructures.



13.12.3Input/OutputUsingAuxiliaryFunctions

IftheimplementationofanI/Ooperatorrequiresaccesstotheprivate



dataofanobject,thestandardoperatorsshoulddelegatetheactualwork

toauxiliarymemberfunctions.Thistechniquealsoallowspolymorphic

readandwritefunctions.Thismightlookasfollows:



classFraction{

...

public:

virtualvoidprintOn(std::ostream&strm)const;

virtualvoidscanFrom(std::istream&strm);

...

};



std::ostream&operator<<(std::ostream&strm,const

{

f.printOn(strm);

returnstrm;

}



std::istream&operator>>(std::istream&strm,Fract

{

f.scanFrom(strm);

returnstrm;

}

Atypicalexampleisthedirectaccesstothenumeratoranddenominator

ofafractionduringinput:



voidFraction::scanFrom(std::istream&strm)

{

...

//assignvaluesdirectlytothecomponents

num=n;

denom=d;

}



Ifaclassisnotintendedtobeusedasabaseclass,theI/Ooperators

canbemadefriendsoftheclass.However,notethatthisapproach

reducesthepossibilitiessignificantlywheninheritanceisused.Friend

functionscannotbevirtual;soasaresult,thewrongfunctionmightbe

called.Forexample,ifareferencetoabaseclassactuallyreferstoan

objectofaderivedclassandisusedasanargumentfortheinput

operator,theoperatorforthebaseclassiscalled.Toavoidthisproblem,

derivedclassesshouldnotimplementtheirownI/Ooperators.Thus,the

implementationsketchedpreviouslyismoregeneralthantheuseof

friendfunctions.Itshouldbeusedasastandardapproach,although

mostexamplesusefriendfunctionsinstead.



13.12.4User-DefinedOperatorsUsingUnformatted

Functions

TheI/Ooperatorsimplementedintheprevioussubsectionsdelegate

mostoftheworktosomepredefinedoperatorsforformattedI/O.Thatis,

operators<>areimplementedintermsofthecorresponding

operatorsformorebasictypes.

TheI/OoperatorsdefinedintheC++standardlibraryaredefined

differently.Thecommonschemeusedfortheseoperatorsisasfollows:

First,withsomepreprocessingthestreamispreparedforactualI/O.

ThentheactualI/Oisdone,followedbysomepostprocessing.This

schemeshouldbeusedforyourownI/Ooperators,too,toprovide

consistencyforI/Ooperators.

Theclassesbasic_istreamandbasic_ostreameachdefine

anauxiliaryclasssentry.Theconstructoroftheseclassesdoesthe

preprocessing,andthedestructordoesthecorresponding

postprocessing.Theseclassesreplacethememberfunctionsthatwere

usedinformerimplementationsoftheIOStreamlibrary(ipfx(),

isfx(),opfx(),andosfx()).Usingthenewclassesensures

thatthepostprocessingisinvokedeveniftheI/Oisabortedwithan

exception.

IfanI/OoperatorusesafunctionforunformattedI/Ooroperatesdirectly



onthestreambuffer,thefirstthingtobedoneshouldbetheconstruction

ofacorrespondingsentryobject.Theremainingprocessingshould

thendependonthestateofthisobject,whichindicateswhetherthe

streamisOK.Thisstatecanbecheckedusingtheconversionofthe

sentryobjecttobool.Thus,I/Ooperatorsgenerallylooklikethis:



sentryse(strm);//indirectpre-andpostproces

if(se){

...//theactualprocessing

}

Thesentryobjecttakesthestreamstrm,onwhichthe

preprocessingandpostprocessingshouldbedone,astheconstructor

argument.

TheadditionalprocessingisusedtoarrangegeneraltasksoftheI/O

operators.Thesetasksincludesynchronizingseveralstreams,checking

whetherthestreamisOK,andskippingwhitespaces,aswellaspossibly

implementation-specifictasks.Forexample,inamultithreaded

environment,theadditionalprocessingcanbeusedforcorresponding

locking.

Forinputstreams,thesentryobjectcanbeconstructedwithan

optionalBooleanvaluethatindicateswhetherskippingofwhitespace

shouldbeavoidedeventhoughtheflagskipwsisset:



sentryse(strm,true);//don'tskipwhitespacesd

ThefollowingexamplesdemonstratethisforclassRow,whichisusedto

representthelinesinatextprocessororeditor:

Theoutputoperatorwritesalinebyusingthestreambuffer's

memberfunctionsputn():



std::ostream&operator<<(std::ostream&strm,con

{

//ensurepre-andpostprocessing



std::ostream::sentryse(strm);

if(se){

//performtheoutput

strm.write(row.c_str(),row.len());

}



returnstrm;

}

Theinputoperatorreadsalinecharacter-by-characterinaloop.The

argumenttrueispassedtotheconstructorofthesentryobject

toavoidtheskippingofwhitespaces:



std::istream&operator>>(std::istream&strm,Row

{

/*ensurepre-andpostprocessing

*-true:Yes,don'tignoreleadingwhitespac

*/

std::istream::sentryse(strm,true);

if(se){

//performtheinput

charc;

row.clear();

while(strm.get(c)&&c!='\n'){

row.append(c);

}

}

returnstrm;

}

Ofcourse,itisalsopossibletousethisframeworkeveniffunctionsdo

notuseunformattedfunctionsfortheirimplementationbutuseI/O

operatorsinstead.However,usingbasic_istreamor



basic_ostreammembersforreadingorwritingcharacterswithin

codeguardedbysentryobjectsisunnecessarilyexpensive.

Wheneverpossible,thecorrespondingbasic_streambufshouldbe

usedinstead.



13.12.5User-DefinedFormatFlags

Whenuser-definedI/Ooperatorsarebeingwritten,itisoftendesirableto

haveformattingflagsspecifictotheseoperators,probablysetbyusinga

correspondingmanipulator.Forexample,itwouldbeniceiftheoutput

operatorforfractions,shownpreviously,couldbeconfiguredtoplace

spacesaroundtheslashthatseparatesnumeratoranddenominator.

Thestreamobjectssupportthisbyprovidingamechanismtoassociate

datawithastream.Thismechanismcanbeusedtoassociate

correspondingdata(forexample,usingamanipulator),andlaterretrieve

thedata.Theclassios_basedefinesthetwofunctionsiword()

andpword(),eachtakinganintargumentastheindex,toaccessa

specificlong&orvoid*&respectively.Theideaisthatiword()and

pword()accesslongorvoid*objectsinanarrayofarbitrarysize

storedwithastreamobject.Formattingflagstobestoredforastream

arethenplacedatthesameindexforallstreams.Thestaticmember

functionxalloc()oftheclassios_baseisusedtoobtainanindex

thatisnotyetusedforthispurpose.

Initially,theobjectsaccessedwithiword()orpword()aresetto0.

Thisvaluecanbeusedtorepresentthedefaultformattingortoindicate

thatthecorrespondingdatawasnotyetaccessed.Hereisanexample:



//getindexfornewostreamdata

staticconstintiword_index=std::ios_base::xalloc

//definemanipulatorthatsetsthisdata

std::ostream&fraction_spaces(std::ostream&strm)

{



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

12 Input/Output Operators for User-Defined Types

Tải bản đầy đủ ngay(0 tr)

×