Pensar en C++ (Volumen 1) - Grupo ARCO

i i “Volumen1” — 2012/1/12 — 13:52 — page I — #1 i i i i i i Pensar en C++ (Volumen 1) Traducción (cuasi-terminada) del libro Thinking in C++, Volumen 1 Bruce Eckel


Texto en PDF


i
i
“Volumen1”—2012/1/12—13:52—pageII—#2i
i
i
i
i
i
PensarenC++(Volumen1)byBruceEckelCopyright©2000BruceEckel
II
i
i
“Volumen1”—2012/1/12—13:52—pageIII—#3i
i
i
i
i
i
Índicegeneral1.IntroducciónalosObjetos11.1.Elprogresodeabstracción..........................11.2.Cadaobjetotieneunainterfaz........................31.3.Laimplementaciónoculta..........................51.4.Reutilizarlaimplementación........................61.5.Herencia:reutilizacióndeinterfaces....................71.5.1.Relacioneses-unvs.es-como-un.................101.6.Objetosintercambiablesgraciasalpolimorsmo.............111.7.Creaciónydestruccióndeobjetos......................151.8.Gestióndeexcepciones:tratamientodeerrores..............161.9.Análisisydiseño...............................161.9.1.Fase0:Hacerunplan........................181.9.1.1.Declaracióndeobjetivos.................191.9.2.Fase1:¿Quéestamoshaciendo?..................191.9.3.Fase2:¿Cómopodemosconstruirlo?...............221.9.3.1.Lascincoetapasdeldiseñodeobjetos.........231.9.3.2.Directricesparadesarrollodeobjetos..........241.9.4.Fase3:Construirelnúcleo.....................251.9.5.Fase4:Iterarloscasosdeuso....................251.9.6.Fase5:Evolución...........................261.9.7.Losplanesvalenlapena.......................271.10.ProgramaciónExtrema............................271.10.1.Escribaprimerolaspruebas.....................281.10.2.Programaciónenparejas.......................291.11.PorquétriunfaC++..............................301.11.1.UnCmejor..............................301.11.2.Ustedyaestáenlacurvadeaprendizaje.............301.11.3.Eciencia................................311.11.4.Lossistemassonmásfácilesdeexpresaryentender.......31
III
i
i
“Volumen1”—2012/1/12—13:52—pageIV—#4i
i
i
i
i
i
Índicegeneral
1.11.5.Aprovechamientomáximoconlibrerías..............311.11.6.Reutilizacióndecódigofuenteconplantillas...........321.11.7.Manejodeerrores...........................321.11.8.Programaralogrande........................321.12.Estrategiasdetransición...........................331.12.1.Directrices...............................331.12.1.1.Entrenamiento.......................331.12.1.2.Proyectosdebajoriesgo.................331.12.1.3.Modelardesdeeléxito..................331.12.1.4.Uselibreríasdeclasesexistentes.............341.12.1.5.NoreescribaenC++códigoqueyaexiste.......341.12.2.Obstáculosdelagestión.......................341.12.2.1.Costesiniciales.......................341.12.2.2.Cuestionesderendimiento................351.12.2.3.Errorescomunesdediseño................351.13.Resumen....................................362.Construiryusarobjetos372.1.Elprocesodetraduccióndellenguaje...................372.1.1.Intérpretes...............................372.1.2.Compiladores.............................382.1.3.Elprocesodecompilación......................392.1.3.1.Comprobaciónestáticadetipos.............402.2.Herramientasparacompilaciónmodular.................402.2.1.Declaracionesvsdeniciones....................412.2.1.1.Sintaxisdedeclaracióndefunciones..........412.2.1.2.Unapuntualización....................422.2.1.3.Denicióndefunciones..................422.2.1.4.Sintaxisdedeclaracióndevariables...........422.2.1.5.Incluircherosdecabecera................432.2.1.6.FormatodeinclusióndelestándarC++.........442.2.2.Enlazado................................452.2.3.Usodelibrerías............................452.2.3.1.Cómobuscaelenlazadorunalibrería..........462.2.3.2.Añadidosocultos.....................462.2.3.3.UsodelibreríasCplano.................472.3.SuprimerprogramaenC++.........................472.3.1.Usodelasclasesiostream.....................47
IV
i
i
“Volumen1”—2012/1/12—13:52—pageVI—#6i
i
i
i
i
i
Índicegeneral
3.4.6.IntroducciónalasreferenciasdeC++...............843.4.7.PunterosyReferenciascomomodicadores...........853.5.Alcance.....................................863.5.1.Denicióndevariables«alvuelo».................873.6.Especicarlaubicacióndelespaciodealmacenamiento.........893.6.1.Variablesglobales..........................893.6.2.Variableslocales...........................903.6.2.1.Variablesregistro.....................903.6.3.Static..................................913.6.4.extern.................................923.6.4.1.Enlazado..........................933.6.5.Constantes...............................933.6.5.1.Valoresconstantes.....................943.6.6.Volatile.................................953.7.Losoperadoresysuuso...........................953.7.1.Asignación...............................953.7.2.Operadoresmatemáticos......................963.7.2.1.Introducciónalasmacrosdelpreprocesador......973.7.3.Operadoresrelacionales.......................973.7.4.Operadoreslógicos..........................973.7.5.Operadoresparabits.........................983.7.6.Operadoresdedesplazamiento...................983.7.7.Operadoresunarios.........................1013.7.8.Eloperadorternario.........................1023.7.9.Eloperadorcoma...........................1023.7.10.Trampashabitualescuandoseusanoperadores.........1033.7.11.Operadoresdemoldeado......................1033.7.12.LosmoldesexplícitosdeC++....................1043.7.12.1.static_cast.......................1053.7.12.2.const_cast........................1063.7.12.3.reinterpret_cast...................1073.7.13.sizeof-unoperadorensimismo.................1083.7.14.Lapalabrareservadaasm......................1083.7.15.Operadoresexplícitos........................1093.8.Creacióndetiposcompuestos........................1093.8.1.Creacióndealiasusandotypedef.................1093.8.2.Usarstructparacombinarvariables..............1103.8.2.1.Punterosyestructuras..................112
VI
i
i
“Volumen1”—2012/1/12—13:52—pageVII—#7i
i
i
i
i
i
Índicegeneral
3.8.3.Programasmásclarosgraciasaenum...............1123.8.3.1.Comprobacióndetiposparaenumerados.......1143.8.4.Cómoahorrarmemoriaconunion................1143.8.5.Arrays.................................1153.8.5.1.Punterosyarrays.....................1173.8.5.2.Elformatodepuntootante...............1203.8.5.3.Aritméticadepunteros..................1213.9.Consejosparadepuración..........................1243.9.1.Banderasparadepuración......................1243.9.1.1.Banderasdedepuraciónparaelpreprocesador....1243.9.1.2.Banderasparadepuraciónentiempodeejecución..1243.9.2.Convertirvariablesyexpresionesencadenas...........1263.9.3.LamacroCassert()..........................1263.10.Direccionesdefunción............................1273.10.1.Denicióndeunpunteroafunción................1273.10.2.Declaracionesydenicionescomplicadas.............1283.10.3.Usodeunpunteroafunción....................1293.10.4.Arraysdepunterosafunciones...................1293.11.Make:cómohacercompilaciónseparada.................1303.11.1.LasactividadesdeMake.......................1313.11.1.1.Macros...........................1323.11.1.2.Reglasdesujo......................1323.11.1.3.Objetivospredeterminados................1333.11.2.LosMakelesdeestelibro......................1343.11.3.UnejemplodeMakele.......................1343.12.Resumen....................................1363.13.Ejercicios....................................1364.AbstraccióndeDatos1414.1.UnalibreríapequeñaalestiloC.......................1424.1.1.Asignacióndinámicadememoria.................1454.1.2.Malassuposiciones..........................1484.2.¿Quétienedemalo?.............................1494.3.Elobjetobásico................................1504.4.¿Quéesunobjeto?..............................1564.5.Tiposabstractosdedatos...........................1564.6.Detallesdelobjeto...............................1574.7.Convecionesparaloscherosdecabecera.................158
VII
i
i
“Volumen1”—2012/1/12—13:52—pageIX—#9i
i
i
i
i
i
Índicegeneral
6.9.Ejercicios....................................2037.Sobrecargadefuncionesyargumentospordefecto2057.1.Másdecoracióndenombres.........................2067.1.1.Sobrecargaenelvalorderetorno..................2077.1.2.EnlaceconFIXME:tiposseguros..................2077.2.Ejemplodesobrecarga............................2087.3.Uniones.....................................2117.4.Argumentospordefecto...........................2137.4.1.Argumentosderelleno........................2147.5.Elecciónentresobrecargayargumentospordefecto...........2157.6.Resumen....................................2197.7.Ejercicios....................................2208.Constantes2238.1.Sustitucióndevalores............................2238.1.1.constenarchivosdecabecera...................2248.1.2.constantesseguras..........................2258.1.3.Vectores................................2268.1.4.DiferenciasconC...........................2268.2.Punteros....................................2288.2.1.Punteroaconstante..........................2288.2.2.Punteroconstante...........................2288.2.2.1.Formato...........................2298.2.3.Asignaciónycomprobacióndetipos................2298.2.3.1.Literalesdecadena....................2308.3.Argumentosdefuncionesyvaloresderetorno..............2308.3.1.Pasoporvalorconstante.......................2318.3.2.Retornoporvalorconstante.....................2318.3.2.1.Temporarios........................2338.3.3.Pasoyretornodedirecciones....................2348.3.3.1.Criteriodepasodeargumentos.............2358.4.Clases......................................2368.4.1.constenlasclases..........................2378.4.1.1.Lalistadeinicializacióndelconstructor.........2378.4.1.2.Constructoresparalostiposdellenguaje........2388.4.2.Constantesentiempodecompilacióndentrodeclases.....2398.4.2.1.Elenumeradoencodigoantiguo............2408.4.3.Objetosymétodosconstantes....................241
IX
i
i
“Volumen1”—2012/1/12—13:52—pageXI—#11i
i
i
i
i
i
Índicegeneral
10.2.2.2.Ladirectivausing....................28410.2.2.3.Ladeclaraciónusing...................28610.2.3.Elusodelosespaciosdenombres.................28710.3.MiembrosestáticosenC++.........................28710.3.1.Denicióndelalmacenamientoparaatributosestáticos.....28810.3.1.1.Inicializacióndevectoresestáticos............28910.3.2.Clasesanidadasylocales......................29110.3.3.Métodosestáticos...........................29210.4.Dependenciaenlainicializacióndevariablesestáticas..........29410.4.1.Quéhacer...............................29610.4.1.1.Técnicauno.........................29610.4.1.2.Técnicados.........................29810.5.Especicacionesdeenlazadoalternativo..................30210.6.Resumen....................................30210.7.Ejercicios....................................30311.Lasreferenciasyelconstructordecopia30711.1.PunterosenC++................................30711.2.ReferenciasenC++..............................30811.2.1.Referenciasenlasfunciones.....................30811.2.1.1.Referenciasconstantes..................30911.2.1.2.Referenciasapuntero...................31011.2.2.Consejosparaelpasodeargumentos...............31111.3.Elconstructordecopia............................31111.3.1.Pasoyretornoporvalor.......................31111.3.1.1.Pasoyretornodeobjetosgrandes............31211.3.1.2.Marcodepilaparallamadasafunción.........31311.3.1.3.Re-entrada.........................31311.3.1.4.Copiabitabitvs.inicialización.............31411.3.2.Construcciónporcopia.......................31611.3.2.1.Objetostemporales....................32011.3.3.Elconstructordecopiapordefecto.................32011.3.4.Alternativasalaconstrucciónporcopia..............32211.3.4.1.Evitandoelpasoporvalor................32211.3.4.2.Funcionesquemodicanobjetosexternos.......32311.4.Punterosamiembros.............................32311.4.1.Funciones...............................32511.4.1.1.Unejemplo.........................326
XI
i
i
“Volumen1”—2012/1/12—13:52—pageXIII—#13i
i
i
i
i
i
Índicegeneral
13.1.1.AsignacióndinámicaenC......................37813.1.2.Eloperadornew...........................38013.1.3.Eloperadordelete.........................38013.1.4.Unejemplosencillo..........................38113.1.5.Trabajoextraparaelgestordememoria..............38113.2.Rediseñodelosejemplosanteriores....................38213.2.1.deletevoid*probablementeesunerror.............38213.2.2.Responsabilidaddelalimpiezacuandoseusanpunteros....38313.2.3.Stashparapunteros.........................38413.2.3.1.Unaprueba.........................38613.3.newydeleteparavectores........................38813.3.1.Cómohacerqueunpunteroseamásparecidoaunvector...38913.3.2.Cuandosesuperaelespaciodealmacenamiento.........38913.3.3.Sobrecargadelosoperadoresnewydelete...........39013.3.3.1.Sobrecargaglobaldenewydelete...........39113.3.3.2.Sobrecargadenewydeleteespecícaparaunacla-se..............................39313.3.3.3.Sobrecargadenewydeleteparavectores......39513.3.3.4.Llamadasalconstructor..................39713.3.3.5.Operadoresnewydeletede[FIXMEemplazamien-to(situación)].......................39813.4.Resumen....................................40013.5.Ejercicios....................................40014.HerenciayComposición40314.1.Sintaxisdelacomposición..........................40314.2.Sintaxisdelaherencia............................40514.3.Listadeinicializadoresdeunconstructor.................40614.3.1.Inicializacióndeobjetosmiembros.................40714.3.2.Tipospredenidosenlalistadeinicializadores..........40714.3.3.Combinacióndecomposiciónyherencia.............40814.3.3.1.Llamadasautomáticasaldestructor..........40914.3.4.Ordendellamadadeconstructoresydestructores........40914.4.Ocultacióndenombres............................41114.5.Funcionesquenoheredanautomáticamente...............41414.5.1.Herenciaymétodosestáticos....................41714.5.2.Composiciónvs.herencia......................41714.5.2.1.Subtipado..........................41914.5.2.2.Herenciaprivada.....................421
XIII
i
i
“Volumen1”—2012/1/12—13:52—pageXIV—#14i
i
i
i
i
i
Índicegeneral
14.5.2.2.1.Publicarlosmiembrosheredadosdeformaprivada......................42114.6.Protected....................................42214.6.1.Herenciaprotegida..........................42214.7.Herenciaysobrecargadeoperadores....................42314.8.Herenciamúltiple...............................42414.9.Desarrolloincremental............................42414.10.Upcasting...................................42514.10.1.¿Porqué«upcasting»?........................42614.10.2.FIXMEUpcastingyelconstructordecopia............42614.10.3.Composiciónvs.herenciaFIXME(revisited)...........42914.10.4.FIXMEUpcastingdepunterosyreferencias............43014.10.5.Unacrisis...............................43014.11.Resumen....................................43114.12.Ejercicios....................................43115.PolimorsmoyFuncionesvirtuales43515.1.EvolucióndelosprogramadoresdeC++..................43515.2.Upcasting...................................43615.3.Elproblema..................................43715.3.1.Ligaduradelasllamadasafunciones...............43715.4.Funcionesvirtuales..............................43815.4.1.Extensibilidad.............................43915.5.CómoimplementaC++laligaduradinámica...............44115.5.1.Almacenandoinformacióndetipo.................44215.5.2.Pintarfuncionesvirtuales......................44315.5.3.Detrásdeltelón............................44515.5.4.Instalarelvpointer..........................44615.5.5.Losobjetossondiferentes......................44615.6.¿Porquéfuncionesvirtuales?........................44715.7.Clasesbaseabstractasyfuncionesvirtualespuras............44815.7.1.Denicionesvirtualespuras.....................45215.8.HerenciaylaVTABLE............................45315.8.1.FIXME:Objectslicing........................45515.9.Sobrecargaryredenir............................45715.9.1.Tipoderetornovariante.......................45815.10.funcionesvirtualesyconstructores.....................46015.10.1.Ordendelasllamadasalosconstructores.............460
XIV
i
i
“Volumen1”—2012/1/12—13:52—pageXVI—#16i
i
i
i
i
i
Índicegeneral
A.6.Ordendelos#includes............................529A.7.Guardasdeinclusiónencherosdecabecera...............529A.8.Usodelosespaciosdenombres.......................529A.9.Utilizaciónderequire()yassure().................530B.DirectricesdeProgramación531C.Lecturasrecomendadas541C.1.SobreC.....................................541C.2.SobreC++engeneral.............................541C.2.1.Mipropialistadelibros.......................541C.3.Losrinconesoscuros.............................542C.4.SobreAnálisisyDiseño...........................543
XVI
i
i
“Volumen1”—2012/1/12—13:52—pageXVIII—#18i
i
i
i
i
i
Índicedeguras
16.2.Herenciamúltiple...............................48316.3.ContenedordeobjetosFigura.......................484
XVIII
i
i
“Volumen1”—2012/1/12—13:52—pageXIX—#19i
i
i
i
i
i
Índicedecuadros3.CenC++3.1.Expresionesqueutilizanbooleanos.....................773.2.MoldesexplícitosdeC++..........................1053.3.Nuevaspalabrasreservadasparaoperadoresbooleanos.........10912.Sobrecargadeoperadores12.1.Directricesparaelegirentremiembroyno-miembro...........357
XIX
i
i
“Volumen1”—2012/1/12—13:52—pageXXII—#22i
i
i
i
i
i
Prólogoalatraducción
participateintheproject(twoeasypossibilitiesarehttp://www.egroups.comorhttp://www.topica.com).
Youmustmaintainadownloadableversionofthepartiallyorfullytranslatedversionofthebook.
Someonemustberesponsiblefortheorganizationofthetranslation(Icannotbeactivelyinvolved-Idon'thavethetime).
Thereshouldonlybeonelanguagetranslationprojectforeachbook.Wedon'thavetheresourcesforafork.
Asinanopen-sourceproject,theremustbeawaytopassresponsi-bilitytosomeoneelseiftherstpersonbecomestoobusy.
Thebookmustbefreelydistributable.
Thebookmaybemirroredonothersites.
Namesofthetranslatorsshouldbeincludedinthetranslatedbook.TecnicismosSehantraducidolamayorpartedelostérminosespecícostantodeorientaciónaobjetoscomodeprogramaciónengeneral.Paraevitarconfusionesoambigüedadesaloslectoresquemanejenliteraturaeningléshemosincluidoentreparéntesiseltérminooriginallaprimeravezqueaparecetraducido.Paratraducirtecnicismosespecialmentecomplicadoshemosutilizadocomorefe-rencialasegundaedicióndeEllenguajedeProgramaciónC++(encastellano)asícomolaWikipedia.Encontadasocasionessehamantenidoeltérminooriginaleninglés.Enbene-ciodelalegibilidad,hemospreferidonohacertraduccionesdemasiadoforzadasniutilizarexpresionesquepudieranresultardesconocidasenelargotoenloslibrosespecializadosdisponiblesencastellano.Nuestropropósitoestenerunlibroquepuedasercomprendidoporhispano-hablantes.EsatodaslucesimposiblerealizarunatraducciónrigurosaacordeconlasnormaslingüísticasdelaRAE,puestoque,enalgunoscasos,elautorinclusoutilizapalabrasdesupropiainvención.CódigofuentePorhacerProducciónTodoelprocesodetraducción,edición,formatoytipografíahasidorealizadoíntegramenteconsoftwarelibre.Todoelsoftwareutilizadoestádisponibleenladis-tribuciónDebianGNU/Linux,queeslaquesehautilizadoprincipalmenteparalaactualizaciónymantenimientodelosdocumentosobtenidoscomoresultado.EltextohasidoescritoenellenguajedemarcadoDocBookversión4.5ensuvarianteXML.Cadacapítuloestácontenidoenuncheroindependienteytodosellosseincluyenenunchero«maestro»utilizandoXInclude.DebidoaquemuchosprocesadoresdeDocBooknosoportanadecuadamentelacaracterísticaXInclude,seusalaherramientaxsltproc4paragenerarunúnicoche-
4http://xmlsoft.org/XSLT/xsltproc2.html
XXII
i
i
“Volumen1”—2012/1/12—13:52—pageXXIII—#23i
i
i
i
i
i
Producción
roXMLquecontieneeltextodetodoellibro,yesesecheroresultanteelqueseprocesa.CódigofuenteTambiénseutilizaXIncludeparaañadirensulugarelcontenidodeloscherosdecódigofuenteescritosenC++.Deesemodo,eltextodeloslistadosqueaparecenenellibroesidénticoaloscherosC++quedistribuyeelautor.Deesemodo,laediciónesmuchamáslimpiaysobretodoseevitanposibleserroresdetranscripcióndeloslistados.UtilizandounpequeñoprogramaescritoenlenguajePython5,sesubstituyenlosnombresetiquetadosdeloscherosporlasentenciaXIncludecorrespondiente:
//:V1C02:Hello.cpp
pasaaser:
example�
title�C02/Hello.cpptitle&#x/000;
programlistinglanguage="C�++"
xi:includeparse="text"href="./code_v1/C02/Hello.cpp�"/
programlisting&#x/000;
example&#x/000;
Unaverrealizadaestasubstitución,seutilizadenuevoxsltprocparamontartantoeltextocomoloslistadosenunúnicocheroXML.Convencionestipográcas
Palabrasreservadas:struct
Códigofuente:printf("Helloworld");
Nombresdecheros:fichero.cpp
Aplicaciónocherobinario:make
Entrecomillado:«upcasting»EsquemasydiagramasLosdibujosydiagramasoriginalessehanrehechoenformato.svgusandolaherramientainkscape6.Apartirdelcherofuente.svgsegeneranversionesenformato.pngparalaversiónHTMLy.pdfparalaversiónPDF.GeneracióndeproductosApartirdeldocumentocompletoenformatoDocBooksegenerandosresultadosdistintos;
5./utils/fix_includes.py6http://inkscape.org/
XXIII
i
i
“Volumen1”—2012/1/12—13:52—pageXXIV—#24i
i
i
i
i
i
Prólogoalatraducción
HTMLenunasolapáginaUnapáginawebXHTML.ParaelloseutilizatambiénlaherramientaxsltprocaplicandohojasdeestiloXSLTquepuedenencontrarseenelrepositoriodefuentesdelproyecto.Estasplantillassonmodicacionesdelasdelproyectodedocumentacióndelprograma«TheGimp»,quetienenlicenciaGPL.Paraelcoloreadodeloslistadosdecódigofuentesehautilizadoelprogramahighlight.Paraello,unpequeñoprogramaPythonmarcaloslistadosparasuextracción,acontinuaciónsecoloreanyporúltimosevuelvenainsertarenlapáginaHTML.HTML(unapáginaporsección)UnconjuntodepáginasXHTML.Automáticamen-tesegeneranenlacesparanavegarporeldocumentoytablasdecontenidos.PDFUndocumentoenformatoPDFutilizandolaaplicacióndblatex7.Hasidone-cesariocrearunahojadeestiloespecícamenteparamanipularelformatodepágina,títuloseíndices.ParaelresaltedesintaxisdeloslistadossehautilizadoelpaqueteLaTeXlistings.ElequipoLassiguientespersonashancolaboradoenmayoromenormedidaenalgúnmo-mentodesdeelcomienzodelproyectodetraduccióndePensarenC++:
DavidVillaAlises(coordinador)dvilla#gmx.net
MíguelÁngelGarcíamiguelangel.garcia#gmail.com
JavierCorralesGarcíajcg#damir.iem.csic.es
BárbaraTeruggibwire.red#gmail.com
SebastiánGurin
GloriaBarberánGonzálezglobargon#gmail.com
FernandoPerfumoVelázqueznperfumo#telefonica.net
JoséMaríaGómezjosemaria.gomez#gmail.com
DavidMartínezMorenoender#debian.org
CristóbalTelloctg#tinet.org
JesúsLópezMollo(pre-Lucas)
JoséMaríaRequenaLópez(pre-Lucas)
JavierFenollRejas(pre-Lucas)AgradecimientosPorhacer:LuCAS,[email protected],[email protected]
7http://dblatex.sourceforge.net/
XXIV
i
i
“Volumen1”—2012/1/12—13:52—pageXXV—#25i
i
i
i
i
i
PrefacioComocualquierlenguajehumano,C++proporcionamétodospa-raexpresarconceptos.Siseutilizadeformacorrecta,estemediodeexpresiónserásignicativamentemássencilloyexiblequeotrasalternativascuandolosproblemasaumentanentamañoycompleji-dad.NosepuedeverC++sólocomounconjuntodecaracterísticas,yaquealgunasdeesascaracterísticasnotienensentidoporseparado.Sólosepuedeutilizarlasumadelaspartessiseestápensandoeneldiseño,nosóloenelcódigo.YparaentenderC++deestaforma,sedebencomprenderlosproblemasexistentesconCyconlaprogramaciónengeneral.Estelibrotratalosproblemasdeprogramación,porquesonproblemas,yelenfoquequetieneC++parasolucionarlos.Además,elconjuntodecaracterísticasqueexplicoencadacapítulosebasaráenlaformaenqueyoveountipodeproblemaenparticularycómoresolverloconellenguaje.Deestaformaesperollevarallector,pocoapoco,deentenderCalpuntoenelqueC++seconviertaensupropialengua.Durantetodoellibro,miactitudserápensarqueellectordeseaconstruirensucabezaunmodeloquelepermitacomprenderellenguajebajandohastasusraíces;sisetropiezaconunrompecabezas,serácapazdecompararloconsumodelomentalydeducirlarespuesta.Tratarédecomunicarlelaspercepcionesquehanreorientadomicerebropara«PensarenC++».MaterialnuevoenlasegundaediciónEstelibroesunaminuciosareescrituradelaprimeraediciónparareejartodosloscambiosquehanaparecidoenC++traslanalizacióndelestándarquelorige,ytambiénparareejarloqueheaprendidodesdequeescribílaprimeraedición.Heexaminadoyreescritoeltextocompleto,enocasionesquitandoviejosejemplos,avecescambiándolos,ytambiénañadiendomuchosejerciciosnuevos.Lareorga-nizaciónyreordenacióndelmaterialtuvolugarparareejarladisponibilidaddemejoresherramientas,asícomomimejorcomprensióndecómolagenteaprendeC++.Heañadidounnuevocapítulo,comointroducciónalrestodellibro,unain-troducciónrápidaalosconceptosdeCyalascaracterísticasbásicasdeC++paraaquellosquenotienenexperienciaenC.ElCD-ROMincluidoalnaldellibroenlaediciónenpapelcontieneunseminario:unaintroducciónaúnmásligeraaloscon-ceptosdeCnecesariosparacomprenderC++(oJava).ChuckAllisonloescribióparamiempresa(MindView,Inc.),ysellama«PensarenC:conceptosbásicosdeJavayC++».PresentalosaspectosdeCquenecesitaconocerparapodercambiaraC++oJava,abandonandolosdesagradablesbitsdebajonivelconlosquelosprograma-doresdeCtratanadiario,peroquelenguajescomoC++yJavamantienenlejos(o
XXV
i
i
“Volumen1”—2012/1/12—13:52—pageXXVI—#26i
i
i
i
i
i
Prefacio
inclusoeliminan,enelcasodeJava).Asíquelarespuestacortaalapregunta«¿Quéesdiferenteenlasegundaedi-ción?»seríaqueaquelloquenoescompletamentenuevosehareescrito,aveceshastaelpuntoenelquenopodríareconocerlosejemplosyelmaterialoriginaldelaprimeraedición.¿Quécontieneelvolumen2deestelibro?ConlaconclusióndelestándardeC++tambiénseañadieronalgunasimportantesbibliotecasnuevas,talescomostringyloscontenedores,yalgoritmosdelaLibreríaEstándarC++,ytambiénsehaañadidocomplejidadalasplantillas.Éstosyotrostemasmásavanzadossehanrelegadoalvolumen2deestelibro,incluyendoasuntoscomolaherenciamúltiple,elmanejodeexcepciones,patronesdediseño,ymaterialsobrelacreaciónydepuracióndesistemasestables.Cómoobtenerelvolumen2Delmismomodoqueellibroqueleeenestosmomentos,PensarenC++,Volumen2sepuededescargardesdemisitiowebwww.BruceEckel.com.Puedeencontrarin-formaciónenelsitiowebsobrelafechaprevistaparalaimpresióndelVolumen2.Elsitiowebtambiéncontieneelcódigofuentedeloslistadosparaamboslibros,juntoconactualizacioneseinformaciónsobreotrosseminariosenCD-ROMqueofre-ceMidViewInc.,seminariospúblicosyformacióninterna,consultas,soporteyasis-tentespasoapaso.RequisitosEnlaprimeraedicióndeestelibro,decidísuponerqueotrapersonayalehabíaenseñadoCyqueellectortenía,almenos,unnivelaceptabledelecturadelmismo.Miprimeraintenciónfuehablardeloquemeresultódifícil:ellenguajeC++.EnestaediciónheañadidouncapítulocomointroducciónrápidaaC,acompañadadelseminarioen-CDThinkinginC,perosigoasumiendoqueellectortienealgúntipodeexperienciaenprogramación.Además,delmismomodoqueseaprendenmuchaspalabrasnuevasintuitivamente,viéndolasenelcontextodeunanovela,esposibleaprendermuchosobreCporelcontextoenelqueseutilizaenelrestodellibro.AprenderC++YomeadentréenC++exactamentedesdelamismaposiciónenlaqueesperoqueseencuentrenmuchosdeloslectoresdeestelibro:comounprogramadorconunaactitudmuysensatayconmuchosviciosdeprogramación.Peoraún,miexperienciaerasobreporgramacióndesistemasempotradosanivelhardware,enlaqueave-cesseconsideraaCcomounlenguajedealtonivelyexcesivamenteinecienteparaahorrarbits.DescubrímástardequenuncahabíasidounbuenprogramadorenC,camuandoasímiignoranciasobreestructuras,malloc()yfree(),setjmp()ylongjmp(),yotrosconceptossosticados,ymuriéndomedevergüenzacuandoestostérminosentrabanenunaconversación,enlugardeinvestigarsuutilidad.CuandocomencémiluchaporaprenderC++,elúnicolibrodecenteeralaauto-
XXVI
i
i
“Volumen1”—2012/1/12—13:52—pageXXVIII—#28i
i
i
i
i
i
Prefacio
normalmentequedanmáscontentoscuandopuedencomprendercadadetalledeunejemploquesiendoimpresionadosporelámbitodelproblemaqueso-luciona.Además,hayunlímiteenlacantidaddecódigoquesepuedeasimilarenunaclase.Porello,avecesrecibocríticasporusarejemplosdejuguete,perotengolabuenavoluntaddeaceptarlasenfavordeproduciralgopedagógica-menteútil.3.Lacuidadosapresentaciónsecuencialdecapacidadesparaquenoseveaalgoquenohasidoexplicado.Deacuerdo,estonosiempreesposible;enesoscasos,seofreceunabrevedescripciónintroductoria.4.Indicarleloquecreoqueesimportanteparaquesecomprendaellenguaje,másquetodoloquesé.Creoquehayuna"jerarquíadelaimportanciadelainformación",yhayalgunoshechosqueel95porcientodelosprogramadoresnuncanecesitarásaberyquesólopodríanconfundirlesyaanzarsupercep-cióndelacomplejidaddellenguaje.TomandounejemplodeC,simemorizalatabladeprecedenciadelosoperadores(yonuncalohice),puedeescribircódigomáscorto.Perosilopiensa,estoconfundiráallector/mantenedordeesecódigo.Asíqueolvidelaprecedencia,yutiliceparéntesiscuandolascosasnoesténclaras.EstamismaactitudlautilizaréconalgunaotrainformacióndellenguajeC++,quecreoqueesmásimportanteparaescritoresdecompiladoresqueparaprogramadores.5.Mantenercadasecciónsucientementeenfocadacomoparaqueeltiempodelectura-yeltiempoentrebloquesdeejercicios-searazonable.Esomantienelasmentesdelaaudienciamásactivaseinvolucradasduranteunseminariopráctico,yademásledaallectorunamayorsensacióndeavance.6.Ofreceraloslectoresunabasesólidademaneraquepuedancomprenderlascuestioneslosucientementebiencomoparapasaraotroscursosylibrosmásdifíciles(enconcreto,elVolumen2deestelibro).7.HetratadodenoutilizarningunaversióndeC++deningúnproveedorenparticularporque,paraaprenderellenguaje,nocreoquelosdetallesdeunaimplementaciónconcretaseantanimportantescomoellenguajemismo.Ladocumentaciónsobrelasespecicacionesdeimplementaciónpropiadecadaproveedorsueleseradecuada.CapítulosC++esunlenguajeenelqueseconstruyencaracterísticasnuevasydiferentessobreunasintaxisexistente(porestarazón,nosreferiremosaélcomounlengua-jedeprogramaciónorientadoaobjetoshíbrido).Comomuchagentepasaporunacurvadeaprendizaje,hemoscomenzadoporadaptarnosalaformaenquelospro-gramadorespasanporlasetapasdelascualidadesdellenguajeC++.Comoparecequelaprogresiónnaturalesladeunamenteentrenadadeformaprocedural,hede-cididocomprenderyseguirelmismocaminoyacelerarelprocesoproponiendoyresolviendolaspreguntasquesemeocurrieroncuandoyoaprendíaellenguajeytambiénlasqueselesocurrieronalagentealaqueloenseñaba.Elcursofuediseñadoconalgoenmente:hacermásecienteelprocesodeapren-derC++.Lareaccióndelaaudienciameayudóacomprenderquéparteserandifí-cilesynecesitabanunaaclaraciónextra.Enlasáreasenlasquemevolvíaambiciosoeincluíademasiadascosasdeunavez,medícuenta-mediantelapresentaciónde
XXVIII
i
i
“Volumen1”—2012/1/12—13:52—pageXXX—#30i
i
i
i
i
i
Prefacio
quelaimplementaciónsepuedacambiarfácilmentesinafectaralcódigodelclien-te.Lapalabraclaveclasstambiénsepresentacomounamaneramáselaboradadedescribiruntipodedatosnuevo,ysedesmiticaelsignicadodelapalabra«objeto»(esunavariableelaborada).Capítulo6:Inicializaciónylimpieza.UnodeloserroresmáscomunesenCsedebealasvariablesnoinicializadas.ElconstructordeC++permitegarantizarquelasvariablesdesunuevotipodedatos(«objetosdesuclase»)siempreseinicializaráncorrectamente.Sisusobjetostambiénrequierenalgúntipodereciclado,ustedpuedegarantizarqueeserecicladoserealicesiempremedianteeldestructorC++.Capítulo7:Sobrecargadefuncionesyargumentospordefecto.C++estápensa-doparaayudaraconstruirproyectosgrandesycomplejos.Mientraslohace,puededarlugaramúltipleslibreríasqueutilicenelmismonombredefunción,ytambiénpuededecidirutilizarunmismonombrecondiferentessignicadosenlamismabiblioteca.ConC++essencillograciasala«sobrecargadefunciones»,loqueleper-mitereutilizarelmismonombredefunciónsiemprequelalistadeargumentosseadiferente.Losargumentospordefectolepermitenllamaralamismafuncióndedife-rentesmanerasproporcionando,automáticamente,valorespordefectoparaalgunosdesusargumentos.Capítulo8:Constantes.Estecapítulocubrelaspalabrasreservadasconstyv-olatile,queenC++tienenunsignicadoadicional,especialmentedentrodelasclases.Aprenderáloquesignicaaplicarconstaunadenicióndepuntero.Elca-pítulotambiénmuestracómovaríaelsignicadodeconstsegúnseutilicedentroofueradelasclasesycómocrearconstantesdentrodeclasesentiempodecompila-ción.Capítulo9:Funcionesinline.Lasmacrosdelpreprocesadoreliminanlasobrecar-gadellamadaafunción,peroelpreprocesadortambiéneliminalavaliosacomproba-cióndetiposdeC++.Lafuncióninlineleofrecetodoslosbeneciosdeunamacrodepreprocesadorademásdelosbeneciosdeunaverdaderallamadaafunción.Estecapítuloexploraminuciosamentelaimplementaciónyusodelasfuncionesinline.Capítulo10:Controldenombres.Laeleccióndenombresesunaactividadfun-damentalenlaprogramacióny,cuandounproyectosevuelvegrande,elnúmerodenombrespuedeserarrollador.C++lepermiteungrancontroldelosnombresenfun-cióndesucreación,visibilidad,lugardealmacenamientoyenlazado.EstecapítulomuestracómosecontrolanlosnombresenC++utilizandodostécnicas.Primero,lapalabrareservadastaticseutilizaparacontrolarlavisibilidadyenlazado,yseex-plorasusignicadoespecialparaclases.UnatécnicamuchomásútilparacontrolarlosnombresanivelglobaleselnamespacedeC++,quelepermitedividirelespaciodenombresglobalendistintasregiones.Capítulo11:Lasreferenciasyelconstructordecopia.LospunterosdeC++tra-bajancomolospunterosdeCconelbenecioadicionaldelacomprobacióndetiposmásfuertedeC++.C++tambiénproporcionaunmétodoadicionalparamanejardirecciones:C++imitalareferenciadeAlgolyPascal,quepermitealcompiladorma-nipularlasdirecciones,peroutilizandolanotaciónordinaria.Tambiénencontraráelconstructor-de-copia,quecontrolalamaneraenquelosobjetossepasanporvalorhaciaodesdelasfunciones.Finalmente,seexplicaelpuntero-a-miembrodeC++.Capítulo12:Sobrecargadeoperadores.Estacaracterísticasellamaalgunasve-ces«azúcarsintáctico»;permitedulcicarlasintaxisdeusodesutipopermitiendooperadoresasícomollamadasafunciones.Enestecapítuloaprenderáquelasobre-cargadeoperadoressóloesuntipodellamadaafuncióndiferenteyaprenderácómoescribirsuspropiosoperadores,manejandoel-avecesconfuso-usodelosargumen-tos,devolviendotipos,yladecisióndesiimplementareloperadorcomométodoo
XXX
i
i
“Volumen1”—2012/1/12—13:52—pageXXXII—#32i
i
i
i
i
i
Prefacio
SolucionesalosejerciciosLassolucionesalosejerciciosseleccionadospuedenencontrarseeneldocumentoelectrónicoElSolucionariodePensarenC++,disponibleporunapequeñacantidadenwww.BruceEckel.com.CódigofuenteElcódigofuentedeloslistadosdeestelibroestáregistradocomofreeware,distri-buidomedianteelsitioWebwww.BruceEckel.com.Elcopyrightleimpidepublicarelcódigoenunmedioimpresosinpermiso,peroseleotorgaelderechodeusarlodemuchasotrasmaneras(vermásabajo).Elcódigoestádisponibleenuncherocomprimido,destinadoaextraersedes-decualquierplataformaquetengaunautilidadzip(puedebuscarenInternetparaencontrarunaversiónparasuplatarformasiaúnnotieneunainstalada).Eneldi-rectorioinicialdondedesempaqueteelcódigoencontrarálasiguientenotasobrederechosdecopia:Copyright(c)2000,BruceEckelSourcecodefilefromthebook"ThinkinginC++"AllrightsreservedEXCEPTasallowedbythefollowingstatements:Youcanfreelyusethisfileforyourownwork(personalorcommercial),includingmodificationsanddistributioninexecutableformonly.Permissionisgrantedtousethisfileinclassroomsituations,includingitsuseinpresentationmaterials,aslongasthebook"ThinkinginC++"iscitedasthesource.Exceptinclassroomsituations,youcannotcopyanddistributethiscode;instead,thesoledistributionpointishttp://www.BruceEckel.com(andofficialmirrorsites)whereitisavailableforfree.Youcannotremovethiscopyrightandnotice.Youcannotdistributemodifiedversionsofthesourcecodeinthispackage.Youcannotusethisfileinprintedmediawithouttheexpresspermissionoftheauthor.BruceEckelmakesnorepresentationaboutthesuitabilityofthissoftwareforanypurpose.Itisprovided"asis"withoutexpressorimpliedwarrantyofanykind,includinganyimpliedwarrantyofmerchantability,fitnessforaparticularpurpose,ornon-infringement.Theentireriskastothequalityandperformanceofthesoftwareiswithyou.BruceEckelandthepublishershallnotbeliableforanydamagessufferedbyyouoranythirdpartyasaresultofusingordistributingthissoftware.InnoeventwillBruceEckelorthepublisherbeliableforanylostrevenue,profit,ordata,orfordirect,indirect,special,consequential,incidental,orpunitivedamages,howevercausedandregardlessof
XXXII
i
i
“Volumen1”—2012/1/12—13:52—pageXXXIII—#33i
i
i
i
i
i
Estándaresdellenguaje
thetheoryofliability,arisingoutoftheuseoforinabilitytousesoftware,evenifBruceEckelandthepublisherhavebeenadvisedofthepossibilityofsuchdamages.Shouldthesoftwareprovedefective,youassumethecostofallnecessaryservicing,repair,orcorrection.Ifyouthinkyou'vefoundanerror,pleasesubmitthecorrectionusingtheformyouwillfindatwww.BruceEckel.com.(Pleaseusethesameformfornon-codeerrorsfoundinthebook.)Sepuedeusarelcódigoenproyectosyclasessiempreycuandosemantengalanotadecopyright.EstándaresdellenguajeDurantetodoellibro,cuandosehagareferenciaalestándardeCISO,general-mentesedirá«C».SólosisenecesitadistinguirentreCestándaryotrosmásviejos,versionespreviasalestándardeC,seharáunadistinción.Cuandoseescribióestelibro,elComitédeEstándaresdeC++yahabíaterminadodetrabajarenellenguaje.Poreso,seusaráeltérminoC++Estándarparareferirseallenguajeestandarizado.SisehacereferenciasimplementeaC++,deberíaasumirquesequieredecir«C++Estándar».HayalgunaconfusiónsobreelnombrerealdelComitédeEstándaresdeC++yelnombredelestándarmismo.SteveClamage,elpresidentedelcomité,claricóesto:HaydoscomitésdeestandarizacióndeC++:ElcomitéNCITS(antigua-menteX3)J16yelcomitéISOJTC1/SC22/WG14.ANSIalquilaNCITSparacrearcomitéstécnicosparadesarrollarestándaresnacionalesameri-canos.J16fuealquiladoen1989paracrearunestándaramericanoparaC++.Porelaño1991sealquilóWG14paracrearunestándarinternacional.ElproyectoJ16seconvirtióenunproyecto«TipoI»(Internacional)ysesubordinóalesfuerzodeestandarizacióndeISO.Losdoscomitésseencontrabanalmismotiempoenelmismositio,yelvotodeJ16constituyeelvotoamericanoconWG14.WG14delegaeltrabajotécnicoaJ16.WG14votaporeltrabajotécnicodeJ16.ElestándardeC++fuecreadooriginalmentecomounestándarISO.AN-SIvotómástarde(comorecomendabaJ16)paraadoptarelestándardeC++ISOcomoelestándaramericanoparaC++.Poreso,«ISO»eslaformacorrectadereferirsealEstándarC++.SoportedellenguajePuedequesucompiladornodispongadetodaslascaracterísticasdiscutidasenestelibro,especialmentesinotienelaversiónmásrecentedelcompilador.Imple-
XXXIII
i
i
“Volumen1”—2012/1/12—13:52—pageXXXIV—#34i
i
i
i
i
i
Prefacio
mentarunlenguajecomoC++esunatareahercúlea,ypuedeesperarquelascarac-terísticasapareceránpocoapocoenlugardetodasalavez.Perosipruebaunodelosejemplosdellibroyobtieneunmontóndeerroresdelcompilador,noesnece-sariamenteunerrorenelcódigooenelcompilador;simplementepuedenoestarimplementadoaúnensucompiladorparticular.ElCD-ROMdellibroElcontenidoprincipaldelCD-ROMempaquetadoalnaldeestelibroesun«seminarioenCD-ROM»tituladoPensarenC:FundamentosparaJavayC++obradeChuckAllison(publicadoporMindView,Inc.,ytambiéndisponibleenwww.BruceEckel.com).Contienemuchashorasdegrabacionesytransparencias,quepuedenmostrarseenlamayoríadelascomputadorasquedispongandelectordeCD-ROMysistemadesonido.ElobjetivodePensarenCesllevarlecuidadosamenteatravésdelosfundamentosdellenguajeC.SecentraenelconocimientoquenecesitaparapoderpasarseaC++oJavaenlugardeintentarhacerleunexpertoentodoslosrecovecosdeC(unadelasrazonesdeutilizarunlenguajedealtonivelcomoC++oJavaes,precisamente,quesepuedenevitarmuchosdeesosrecovecos).Tambiéncontieneejerciciosysolucionesguiadas.TéngaloencuentaporqueelCapítulo3deestelibrovamásalládelCDdePensarenC,elCDnoesunaalternativaaestecapítulo,sinoquedeberíautilizarsecomopreparaciónparaestelibro.Porfavor,tengaencuentaqueelCD-ROMestábasadoennavegador,porloquedeberíatenerunnavegadorWebinstaladoensumáquinaantesdeutilizarlo.CD-ROMs,seminarios,yconsultoríaHayseminariosenCD-ROMplaneadosparacubrirelVolumen1yelVolumen2deestelibro.Comprendenmuchashorasdegrabacionesmíasqueacompañanlastransparenciasquecubrenelmaterialseleccionadodecadacapítulodellibro.SepuedenverenlamayoríadelascomputadorasquedisponendelectordeCDROMysistemadesonido.EstosCDspuedencomprarseenwww.BruceEckel.com,dondeencontrarámásinformaciónylecturasdeejemplo.Micompañía,MindView,Inc.,proporcionaseminariospúblicosdepreparaciónprácticabasadosenelmaterialdeestelibroytambiénentemasavanzados.Elmate-rialseleccionadodecadacapítulorepresentaunalección,quesecontinúaconunpe-riododeejerciciosmonitorizadosparaquecadaestudianterecibaatenciónpersonal.Tambiénproporcionamospreparación«insitu»,consultoría,tutorización,diseñoyasistentesdecódigo.Puedeencontrarlainformaciónylosformulariosparalospró-ximosseminarios,asícomootrainformacióndecontacto,enwww.BruceEckel.com.Avecesmeencuentrodisponibleparaconsultasdediseño,evaluacióndepro-cesosyasistencia.Cuandocomencéaescribirsobrecomputadoras,mimotivaciónprincipalfueincrementarmisactividadesdeconsultoría,porqueencontrabaquelaconsultoríaeracompetitiva,educacional,yunademisexperienciasprofesionalesmásvaliosas.Asíqueharétodoloquepuedaparaincluirleaustedenmiagenda,oparaofrecerleunodemissocios(quesongentequeconozcobienyconlaquehetratado,yamenudoco-desarrollaneimpartenseminariosconmigo).
XXXIV
i
i
“Volumen1”—2012/1/12—13:52—pageXXXV—#35i
i
i
i
i
i
Errores
ErroresNoimportacuántostrucosempleeunescritorparadetectarloserrores,algunossiempreseescapanysaltandelpapelallectoratento.Siencuentraalgoquecreaqueesunerror,porfavor,utiliceelformulariodecorreccionesqueencontraráenwww.BruceEckel.com.Seagradecesuayuda.SobrelaportadaLaprimeraedicióndeestelibroteníamicaraenlaportada,peroparalasegundaediciónyoqueríadesdeelprincipiounaportadaqueseparecieramásunaobradearte,comolaportadadePensarenJava.Poralgunarazón,C++parecesugerirmeArtDecóconsuscurvassimplesypinceladascromadas.Teníaenmentealgocomoesoscartelesdebarcosyavionesconcuerposlargos.MiamigoDanielWill-Harris,(www.Will-Harris.com)aquienconocíenlasclasesdelcorodelinstituto,ibaallegaraserundiseñadoryescritordetallamundial.Élhahechoprácticamentetodosmisdiseños,incluídalaportadaparalaprimeraedicióndeestelibro.Duranteelprocesodediseñodelaportada,Daniel,insatisfechoconelprogresoquerealizábamos,siemprepreguntaba:«¿Quérelaciónhayentrelaspersonasylascomputadoras?».Estábamosatascados.Comocapricho,sinnadaenmente,mepidióquepusieramicaraenelescáner.Danielteníaunodesusprogramasgrácos(CorelXara,sufavorito)que«autotra-zó»micaraescaneada.Éllodescribedelasiguentemanera:«Elautotrazadoeslaformaenlaquelacomputadoratransformaundibujoenlostiposdelíneasycurvasquerealmentelegustan».Entoncesjugóconellohastaqueobtuvoalgoqueparecíaunmapatopográcodemicara,unaimagenquepodríaserlamaneraenquelacomputadoravealagente.Cogíestaimagenylafotocopiéenpapeldeacuarela(algunascopiadoraspuedenmanejarpapelesgruesos),yentoncescomenzóarealizarmontonesdeexperimentosañadiendoacuarelaalaimagen.Seleccionamoslasquenosgustabanmás,entoncesDaniellasvolvióaescanearylasorganizóenlaportada,añadiendoeltextoyotroselementosdediseño.Elprocesototalrequirióvariosmeses,mayormenteacausadeltiempoquemetomóhacerlasacuarelas.Peromehedivertidoespecialmenteporqueconseguíparticiparenelartedelaportada,yporquemediounincentivoparahacermásacuarelas(loquedicensobrelaprácticarealmenteescierto).DiseñodellibroyproducciónEldiseñodelinteriordellibrofuecreadoporDanielWill-Harris,quesolíaju-garconletras(FIXME:rub-on)enelinstitutomientrasesperabalainvencióndelascomputadorasylapublicacióndeescritorio.Detodosmodos,yomismoprodujelaspáginasparaimpresión(camera-ready),porloqueloserrorestipográcossonmíos.SeutilizóMicrosoft®WordparaWindowsVersiones8y9paraescribirellibroycrearlaversiónparaimpresión,incluyendolageneracióndelatabladeconteni-dosyelíndice(creéunservidorautomatizadoCOMenPython,invocadodesdelasmacrosVBAdeWord,paraayudarmeenelmarcadodelosíndices).Python(veawww.python.com)seutilizóparacrearalgunasdelasherramientasparacompro-barelcódigo,ylohabríautilizadocomoherramientadeextraccióndecódigosilohubiesedescubiertoantes.CreélosdiagramasutilizandoVisio®.GraciasaVisioCorporationporcrearuna
XXXV
i
i
“Volumen1”—2012/1/12—13:52—pageXXXVII—#37i
i
i
i
i
i
Agradecimientos
SondaDonovanmeayudóconlaproduccióndelCDROM.DanielWill-Harris(porsupuesto)creóeldiseñodelaportadaqueseencuentraenelpropioCD.ParatodoslosgrandesamigosdeCrestedButte,graciasporhacerdeélunlugarmágico,especialmenteaAlSmith(creadordelmaravillosoCamp4CoffeeGarden),misvecinosDaveyErika,MarshadelalibreríaHeg'sPlace,PatyJohndeTeocalliTemale,SamdeBarkeryCafé,yaTillerporsuayudaconlainvestigaciónenaudio.YatodalagentefenomenalqueandaporCamp4yhaceinteresantesmismañanas.Lalistadeamigosquemehandadosoporteincluye,peronoestálimitada,aZackUrlocker,AndrewBinstock,NeilRubenking,KraigBrocschmidt,SteveSinofsky,JDHildebrandt,BrianMcElhinney,BrinkeyBarr,LarryO'Brien,BillGatesenMidnightEngineeringMagazine,LarryConstantine,LucyLockwood,TomKeffer,DanPutter-man,GeneWang,DaveMayer,DavidIntersimone,ClaireSawyers,losItalianos(An-dreaProvaglio,RossellaGioia,LauraFallai,Marco&LellaCantu,Corrado,IlsayChristinaGiustozzi),ChrisyLauraStrand(yParker),losAlquimistas,BradJerbic,MarilynCvitanic,elMabrys,elHalingers,losPollocks,PeterVinci,losRobbins,losMoelters,DaveStoner,LaurieAdams,losCranstons,LarryFogg,MikeykarenSequeira,GaryEntsmingeryAllisonBrody,Kevin,Sonda&EllaDonovan,ChesteryShannonAndersen,JoeLordi,DaveyBrendaBarlett,losRentschlers,LynnyToddysusfamilias.Yporsupuesto,aMamáyPapá.
XXXVII
i
i
“Volumen1”—2012/1/12—13:52—pageXXXVIII—#38i
i
i
i
i
i
i
i
“Volumen1”—2012/1/12—13:52—page1—#39i
i
i
i
i
i
1:IntroducciónalosObjetosElorigendelarevolucióninformáticaocurriódentrodeunamá-quina.Portanto,elorigendenuestroslenguajesdeprogramacióntiendeaparecerseaesamáquina.Perolosordenadoresnosontantomáquinascomoherramientasdeamplicacióndelamente(«bicicletasparalamente»,comolegustadeciraSteveJobs)yunme-diodeexpresióndiferente.Comoresultado,lasherramientasempiezanaparecersemenosalasmáquinasymásapartesdenuestramente,ytambiénaotrosmediosdeexpresióncomolaescritura,lapintura,laescultura,laanimaciónylacinematogra-fía.Laprogramaciónorientadaaobjetosespartedeestemovimientohaciaunusodelordenadorcomomediodeexpresión.Estecapítuloleservirádeintroducciónalosconceptosbásicosdelaprograma-ciónorientadaaobjetos(POO),incluyendounresumendelosmétodosdedesarrollodelaPOO.Estecapítulo,yestelibro,presuponenqueellectoryatieneexperien-ciaconunlenguajedeprogramaciónprocedural,aunquenotieneporquéserC.SicreequenecesitamáspreparaciónenprogramaciónyenlasintaxisdeCantesdeabordarestelibro,deberíaleerelCD-ROMdeentrenamientoThinkinginC:Foun-dationsforC++andJava,queacompañaaestelibro,yestádisponibletambiénenwww.BruceEckel.com.Estecapítulocontienematerialbásicoysuplementario.Muchagentenosesientecómodaadentrándoseenlaprogramaciónorientadaaobjetossintenerantesunavisiónglobal.Poreso,aquíseintroducenmuchosconceptosqueintentandarleunavisiónsólidadelaPOO.Sinembargo,muchaspersonasnocaptanlosconceptosglo-baleshastaquenohanvistoprimeropartedelamecánica;puedequeseatasquenosepierdansinohayningúntrozodecódigoalqueponerlelasmanosencima.Sius-tedperteneceaesteúltimogrupo,yestáansiosoporllegaralasespecicacionesdellenguaje,siéntaselibredesaltarestecapítulo;esonoleimpediráescribirprogramasoaprenderellenguaje.Sinembargo,quizáquieravolveraestecapítuloparacom-pletarsusconocimientosypodercomprenderporquésonimportanteslosobjetosycómodiseñarconellos.1.1.ElprogresodeabstracciónTodosloslenguajesdeprogramaciónproporcionanabstracciones.Sepuedear-marquelacomplejidaddelosproblemasquesepuedenresolverestádirectamenterelacionadaconeltipoycalidaddelaabstracción.Por«tipo»mereeroa«¿Quéesloqueestáabstrayendo?».Ellenguajeensambladoresunapequeñaabstraccióndelamáquinasubyacente.Muchoslenguajesllamados«imperativos»quesiguie-ron(comoFortran,BASICyC)eranabstraccionesdellenguajeensamblador.Estos
1
i
i
“Volumen1”—2012/1/12—13:52—page2—#40i
i
i
i
i
i
Capítulo1.IntroducciónalosObjetos
lenguajessuponengrandesmejorasconrespectoallenguajeensamblador,perosuabstracciónprimariatodavíarequierepensarentérminosdelaestructuradelorde-nador,enlugardelaestructuradelproblemaqueintentaresolver.Elprogramadordebeestablecerlaasociaciónentreelmodelodelamáquina(enel«espaciodesolu-ciones»,queesellugardondeestámodelandoeseproblema,comounordenador)yelmodelodelproblemaqueseestáresolviendo(enel«espaciodeproblemas»,queesellugardondeexisteelproblema).Elesfuerzorequeridopararealizarestacorres-pondencia,yelhechodequeseaextrínsecoallenguajedeprogramación,produceprogramasdifícilesdeescribirycarosdemantenery,comoefectosecundario,creótodalaindustriade«métodosdeprogramación».Laalternativaamodelarlamáquinaesmodelarelproblemaqueestáintentandoresolver.LosprimeroslenguajescomoLISPyAPLeligieronconcepcionesdelmun-doparticulares(«Todoslosproblemassonlistasenúltimainstancia»,o«Todoslosproblemassonalgorítmicos»).PROLOGreducetodoslosproblemasacadenasdedecisiones.Sehancreadolenguajesparaprogramaciónbasadosenrestriccionesyparaprogramarmanipulandoexclusivamentesímbolosgrácos(loúltimodemos-tróserdemasiadorestrictivo).Cadaunodeestosmétodosesunabuenasoluciónparaeltipoparticulardeproblemaparaelquefuerondiseñados,perocuandounosaledeesedominiosehacendifícilesdeusar.Elmétodoorientadoaobjetosvaunpasomásallá,proporcionandoherramien-tasparaqueelprogramadorrepresenteloselementosenelespaciodelproblema.Estarepresentacióneslosucientementegeneralcomoparaqueelprogramadornoestélimitadoauntipoparticulardeproblema.Nosreferimosaloselementosenelespaciodelproblema,yasusrepresentacionesenelespaciodelasolución,como«objetos»(porsupuesto,necesitaráotrosobjetosquenotengananalogíaseneles-paciodelproblema).Laideaesquepermitaalprogramaadaptarseallenguajedelproblemaañadiendonuevostiposdeobjetosdemodoquecuandoleaelcódigoquedescribelasolución,estéleyendopalabrasqueademásexpresanelproblema.Esunlenguajedeabstracciónmásexibleypotentequelosquehayausadoantes.Deestamanera,laPOOpermitedescribirelproblemaentérminosdelproblema,enlugardeusartérminosdelacomputadoraenlaqueseejecutarálasolución.Sinembargo,todavíaexisteunaconexiónconlacomputadora.Cadaobjetosepareceunpocoaunapequeñacomputadora;tieneunestadoyoperacionesqueselepuedepedirquehaga.Sinembargo,nopareceunamalaanalogíaalosobjetosenelmundoreal;todosellostienencaracterísticasycomportamientos.Algunosdiseñadoresdelenguajeshandecididoquelaprogramaciónorientadaaobjetosensímismanoesadecuadapararesolverfácilmentetodoslosproblemasdeprogramación,yaboganporunacombinacióndevariasaproximacionesenlen-guajesdeprogramaciónmultiparadigma.1AlanKayresumiólascincocaracterísticasbásicasdeSmalltalk,elprimerlengua-jeorientadoaobjetosconéxitoyunodeloslenguajesenlosqueestábasadoC++.Esascaracterísticasrepresentanunaaproximaciónalaprogramaciónorientadaaob-jetos:1.Todoesunobjeto.Pienseenunobjetocomounavariableelaborada;almace-nadatos,peropuede«hacerpeticiones»aesteobjeto,solicitandoquerealiceoperacionesensímismo.Enteoría,puedecogercualquiercomponentecon-ceptualdelproblemaqueestáintentandoresolver(perros,edicios,servicios,etc.)yrepresentarloscomounobjetoensuprograma.
1VerMultiparadigmProgramminginLedadeTimothyBudd(Addison-Wesley1995).
2
i
i
“Volumen1”—2012/1/12—13:52—page6—#44i
i
i
i
i
i
Capítulo1.IntroducciónalosObjetos
ríacambiarlaimplementacióninternadelaclasesinpreocuparsedecómoafectaráalosprogramadoresclientes.Porejemplo,podríaimplementarunaclaseparticulardeunamanerasencillaparaundesarrollofácil,ymástardedescubrirquenecesitareescribirlaparahacerlamásrápida.Silainterfazylaimplementaciónestáncla-ramenteseparadasyprotegidas,puedelograrlofácilmenteysólorequierequeelusuariovuelvaaenlazarlaaplicación.C++utilizatrespalabrasreservadasexplícitasparaponerlímitesenunaclase:public,private,yprotected.Suusoysignicadosonbastantesencillos.Estosespecicadoresdeaccesodeterminanquiénpuedeusarlasdenicionesquesiguen.pu-blicsignicaquelasdenicionesposterioresestándisponiblesparacualquiera.Lapalabrareservadaprivate,porotrolado,signicaquenadiepuedeaccederaestasdenicionesexceptoelcreadordeltipo,esdecir,losmétodosinternosdelaclase.p-rivateesunaparedentreelcreadordelaclaseyelprogramadorcliente.Sialguienintentaaccederaunmiembroprivado,obtendráunerroralcompilar.protectedactúacomoprivate,conlaexcepcióndequelasclasesderivadastienenaccesoamiembrosprotegidos,peronoalosprivados.Laherenciaseexplicaráenbreve.1.4.ReutilizarlaimplementaciónUnavezqueunaclasesehacreadoyprobado,deberíaconstituir(idealmente)unaunidadútildecódigo.Sinembargo,estareutilizaciónnoestanfácildeconse-guircomomuchosesperarían;producirunbuendiseñorequiereexperienciayco-nocimientos.Perounavezquelotiene,pideserreutilizado.Elcódigoreutilizadoesunadelasmejoresventajasdeloslenguajesparaprogramaciónorientadaaobjetos.Laformamásfácildereutilizarunaclaseesprecisamenteutilizarunobjetodeesaclasedirectamente,perotambiénpuedecolocarunobjetodeestaclasedentrodeunaclasenueva.Podemosllamarlo«crearunobjetomiembro».Sunuevaclasepuedeestarcompuestadevariosobjetosdecualquiertipo,encualquiercombina-ciónquenecesiteparaconseguirlafuncionalidaddeseadaensunuevaclase.Comoestácomponiendounanuevaclaseapartirdeclasesexistentes,esteconceptosella-macomposición(odeformamásgeneral,agregación).Amenudonosreferimosalacomposicióncomounarelación«tiene-un»,comoen«uncochetiene-unmotor».
Figura1.2:Uncochetieneunmotor(EldiagramaUMLanteriorindicacomposiciónconelromborelleno,locualim-plicaquehayuncoche.Típicamenteusaréunaformamássimple:sólounalínea,sinelrombo,paraindicarunaasociación.5)Lacomposiciónesunmecanismomuyexible.Losobjetosmiembrodesunuevaclasenormalmentesonprivados,haciéndolosinaccesiblesparalosprogramadoresclientesqueestánusandolaclase.Esopermitecambiaresosmiembrossinperturbaralcódigoclienteexistente.Tambiénpuedecambiarlosmiembrosdelobjetoentiem-
5Normalmenteestoessucienteparalamayoríadelosdiagramasynonecesitaespecicarsiestáusandoagregaciónocomposición.
6
i
i
“Volumen1”—2012/1/12—13:52—page9—#47i
i
i
i
i
i
1.5.Herencia:reutilizacióndeinterfaces
denenviar,esosignicaquelaclasederivadaesdelmismotipoquelaclasebase.Enelejemploanterior,«uncírculoesunagura».Estaequivalenciadetiposvíaherenciaesunodelasclavesfundamentalesparacomprenderlaprogramaciónorientadaaobjetos.Porloquetantolaclasebasecomoladerivadatienenlamismainterfaz,debehaberalgunaimplementaciónquecorrespondaaesainterfaz.Esdecir,debehabercódigoparaejecutarcuandounobjetorecibeunmensajeparticular.Sisimplementeheredadeunaclaseynohacenadamás,losmétodosdelainterfazdelaclasebaseestándisponiblesenlaclasederivada.Estosignicaquelosobjetosdelaclasederi-vadanosólotienenelmismotipo,tambiéntienenelmismocomportamiento,locualnoesparticularmenteinteresante.Haydoscaminosparadiferenciarlanuevaclasederivadadelaclasebaseorigi-nal.Elprimeroesbastantesencillo:simplementehayqueañadirnuevasfuncionesalaclasederivada.Estasnuevasfuncionesnosonpartedelainterfazdelaclasebase.Esosignicaquelaclasebasesimplementenohacetodoloquenecesitamos,porloqueseañadenmásfunciones.Esteusosimpleyprimitivodelaherenciaes,ave-ces,lasoluciónperfectaamuchosproblemas.Sinembargo,quizádeberíapensarenlaposibilidaddequesuclasebasepuedenecesitartambiénfuncionesadicionales.Esteprocesodedescubrimientoeiteracióndesudiseñoocurreregularmenteenlaprogramaciónorientadaaobjetos.
Figura1.5:EspecializacióndeFiguraAunquelaherenciaalgunasvecessuponequesevanaañadirnuevasfuncionesalainterfaz,noesnecesariamentecierto.Elsegundoymásimportantecaminoparadiferenciarsunuevaclaseescambiarelcomportamientorespectodeunafuncióndeunaclasebaseexistente.Aestoselellamareescribir(override)unafunción.
9
i
i
“Volumen1”—2012/1/12—13:52—page10—#48i
i
i
i
i
i
Capítulo1.IntroducciónalosObjetos
Figura1.6:ReescriturademétodosParareescribirunafunción,simplementehayquecrearunanuevadeniciónpa-raesafunciónenlaclasederivada.Estádiciendo,«Estoyusandolamismafuncióndeinterfazaquí,peroquierohaceralgodiferenteparaminuevotipo».1.5.1.Relacioneses-unvs.es-como-unHayciertacontroversiaquepuedeocurrirconlaherencia:¿laherenciadeberíalimitarseaanularsólofuncionesdelaclasebase(ynoañadirnuevosmétodosquenoesténenlaclasebase)?Estopuedesignicarqueeltipoderivadoesexactamenteelmismotipoquelaclasebasedadoquetieneexactamentelamismainterfaz.Comoresultado,sepuedesustituirunobjetodeunaclasederivadaporunobjetodelaclasebase.Sepuedepensarcomounasustituciónpura,ysesuelellamarprincipiodesustitución.Enciertomodo,estaeslaformaidealdetratarlaherencia.Amenudonosreferimosalasrelacionesentrelaclasebaseyclasesderivadasenestecasocomounarelaciónes-un,porquesedice«uncírculoesunagura».Unmododeprobarlaherenciaesdeterminarsisepuedeconsiderarlarelaciónes-unsobrelasclasesysitienesentido.Hayocasionesenlasquesedebenañadirnuevoselementosalainterfazdeuntipoderivado,deestamaneraseamplíalainterfazysecreauntiponuevo.Elnuevotipotodavíapuedesersustituidoporeltipobase,perolasustituciónnoesperfectaporquesusnuevasfuncionesnosonaccesiblesdesdeeltipobase.Estarelaciónseconocecomoes-como-un;elnuevotipotienelainterfazdelviejotipo,perotambiéncontieneotrasfunciones,porloquesepuededecirqueesexactamenteelmismo.Porejemplo,considereunaireacondicionado.Supongaquesucasaestáconectadacontodosloscontrolespararefrigerar;esdecir,tieneunainterfazquelepermitecontrolarlatemperatura.Imaginequeelaireacondicionadoseaveríayloreemplazaporunabombadecalor,lacualpuededarcaloryfrío.Labombadecalores-como-unaireacondicionado,peropuedehacermáscosas.Comoelsistemadecontroldesucasaestádiseñadosóloparacontrolarelfrío,estárentringidaacomunicarsesólocon
10
i
i
“Volumen1”—2012/1/12—13:52—page13—#51i
i
i
i
i
i
1.6.Objetosintercambiablesgraciasalpolimorsmo
cuerpodelafunción,usandoinformaciónalmacenadaenelobjeto(esteprocesosetratacondetalleenelCapítulo15).Deestemodo,cualquierobjetosepuedecompor-tardeformadiferentedeacuerdoconelcontenidodeestetrozoespecialdecódigo.Cuandoenvíaunmensajeaunobjeto,elobjetocomprenderealmentequéhacerconelmensaje.Esposibledisponerdeunafunciónquetengalaexibilidaddelaspropiedadesdelaligaduratardíausandolapalabrareservadavirtual.Nonecesitaentenderelmecanismodevirtualparausarla,perosinellanopuedehacerprogramaciónorientadaaobjetosenC++.EnC++,deberecordarañadirlapalabrareservadavi-rtualporque,pordefecto,losmétodosnoseenlazandinámicamente.Losmétodosvirtualeslepermitenexpresarlasdiferenciasdecomportamientoenclasesdelamis-mafamilia.Estasdiferenciassonlasquecausancomportamientospolimórcos.Considereelejemplodelagura.Eldiagramadelafamiliadeclases(todasbasa-dasenlamismainterfazuniforme)aparecióantesenestecapítulo.Parademostrarelpolimorsmo,queremosescribirunaúnicapiezadecódigoqueignorelosdetallesespecícosdetipoyhablesóloconlaclasebase.Estecódigoestádesacopladodelainformacióndeltipoespecíco,ydeesamaneraesmássimpledeescribirymásfácildeentender.Y,sitieneunnuevotipo-unHexágono,porejemplo-seañadeatravésdelaherencia,elcódigoqueescribafuncionaráigualdebienparaelnuevotipodeFiguracomoparalostiposanteriores.Deestamanera,elprogramaesextensible.SiescribeunafunciónC++(podráaprenderdentrodepococómohacerlo):
voidhacerTarea(Figura&f){
f.borrar();
//...
f.dibujar();
}
EstafunciónsepuedeaplicaracualquierFigura,demodoqueesindependientedeltipoespecícodelobjetoquesedibujayborra(el«&»signica«tomaladireccióndelobjetoquesepasaahacerTarea()»,peronoesimportantequeentiendalosdetallesahora).SienalgunaotrapartedelprogramausamoslafunciónhacerTa-rea():
Circuloc;
Triangulot;
Lineal;
hacerTarea(c);
hacerTarea(t);
hacerTarea(l);
LasllamadasahacerTarea()funcionanbienautomáticamente,apesardeltipoconcretodelobjeto.Enefectoesuntrucobonitoyasombroso.Considerelalínea:
hacerTarea(c);
LoqueestáocurriendoaquíesqueestápasandounCírculoaunafunciónqueesperaunaFigura.ComounCírculoesunaFigurasepuedetratarcomotalporpartedehacerTarea().Esdecir,cualquiermensajequepuedaenviarhacerTar-ea()aunaFigura,unCírculopuedeaceptarlo.Poreso,esalgocompletamente
13
i
i
“Volumen1”—2012/1/12—13:52—page14—#52i
i
i
i
i
i
Capítulo1.IntroducciónalosObjetos
lógicoyseguro.Aesteprocesodetrataruntipoderivadocomosifuerasutipobaseselellamaupcasting(moldeadohaciaarriba6).Elnombrecast(molde)seusaenelsentidodeadap-taraunmoldeyeshaciaarribaporlaformaenquesedibujanlosdiagramasdeclasesparaindicarlaherencia,coneltipobaseenlapartesuperiorylasclasesderivadascolgandodebajo.Deestamanera,moldearuntipobaseesmoversehaciaarribaporeldiagramadeherencias:«upcasting»
Figura1.9:UpcastingTodoprogramaorientadoaobjetostienealgúnupcastingenalgunaparte,por-queasíescomosedespreocupadetenerqueconocereltipoexactoconelqueestátrabajando.MireelcódigodehacerTarea():
f.borrar();
//...
f.dibujar();
Observequenodice«SiesunCírculo,hazesto,siesunCuadrado,hazestootro,etc.».SiescribeuntipodecódigoquecompruebatodoslosposiblestiposqueunaFigurapuedetenerrealmente,resultarásucioytendráquecambiarlocadavezqueañadaunnuevotipodeFigura.Aquí,sólodice«Eresunagura,séquetepuedesborrar()ydibujar()atimisma,hazlo,ypreocúpatedelosdetalles».LoimpresionantedelcódigoenhacerTarea()esque,dealgunamanera,fun-cionabien.Llamaradibujar()paraunCírculoejecutadiferentecódigoquecuandollamaadibujar()paraunCuadradoounaLínea,perocuandoseenvíaelmensajedibujar()aunFiguraanónima,laconductacorrectasucedeenbaseeneltiporealdeFigura.Estoesasombrosoporque,comosemencionóanterior-mente,cuandoelcompiladorC++estácompilandoelcódigoparahacerTarea(),nosabeexactamentequétiposestámanipulando.Poresonormalmente,esdeespe-rarqueacabeinvocandolaversióndeborrar()ydibujar()paraFigura,ynoparaelCírculo,Cuadrado,oLíneaespecíco.Yaúnasíocurredelmodocorrec-toacausadelpolimorsmo.Elcompiladoryelsistemaseencargandelosdetalles;todoloquenecesitasaberesqueestoocurreyloqueesmásimportante,cómoutili-zarloensusdiseños.Siunmétodoesvirtual,entoncescuandoenvíeelmensajea
6N.deT:Enellibroseutilizaráeltérminooriginaleninglésdebidoasuusocomún,inclusoenlaliteraturaencastellano.
14
i
i
“Volumen1”—2012/1/12—13:52—page15—#53i
i
i
i
i
i
1.7.Creaciónydestruccióndeobjetos
unobjeto,elobjetoharálocorrecto,inclusocuandoestéinvolucradoelupcasting.1.7.CreaciónydestruccióndeobjetosTécnicamente,eldominiodelaPOOsonlostiposabstractosdedatos,laherenciayelpolimorsmo,perootrosasuntospuedenseralmenosigualdeimportantes.Estasecciónofreceunavisióngeneraldeesosasuntos.Esespecialmenteimportantelaformaenquesecreanysedestruyenlosobjetos.¿Dóndeestáeldatoparaunobjetoycómosecontrolalavidadeesteobjeto?Dife-renteslenguajesdeprogramaciónusandistintaslosofíasalrespecto.C++adoptaelenfoquedequeelcontroldeecienciaeslacuestiónmásimportante,peroesodelegalaelecciónalprogramador.Paraunavelocidadmáximadeejecución,elal-macenamientoylavidasedeterminanmientraselprogramaseescribe,colocandolosobjetosenlapilaoenalmacenamientoestático.Lapilaesunáreadememoriausadadirectamenteporelmicroprocesadorparaalmacenardatosdurantelaejecu-cióndelprograma.Aveceslasvariablesdelapilasellamanvariablesautomáticasodeámbito(scoped).Eláreadealmacenamientoestáticoessimplementeunparchejodememoriaalojadoantesdequeelprogramaempieceaejecutarse.Usarlapilaoeláreadealmacenamientoestáticojaunaprioridadenlarapidezdeasignaciónyliberacióndememoria,quepuedeservaliosoenalgunassituaciones.Sinembargo,sesacricaexibilidadporquesedebeconocerlacantidadexacta,vida,ytipodeobjetosmientraselprogramadorescribeelprograma.Siestáintentandoresolverunproblemamásgeneral,comoundiseñoasistidoporcomputadora,gestióndealma-cén,ocontroldetrácoaéreo,esotambiénesrestrictivo.Elsegundoenfoqueescrearobjetosdinámicamenteenunespaciodememoriallamadomontículo(heap).Enesteenfoquenosesabehastaelmomentodelaejecu-cióncuántosobjetossenecesitan,cuálserásuciclodevida,osutipoexacto.Estasdecisionessetomandeimprovisomientraselprogramaestáenejecución.Sinecesitaunnuevoobjeto,simplementecreeloenelmontículocuandolonecesite,usandolapalabrareservadanew.Cuandoyanonecesiteeseespaciodealmacenamiento,debeliberarlousandolapalabrareservadadelete.Comolamemoriaseadministradinámicamenteentiempodeejecución,lacanti-daddetiemporequeridoparareservarespacioenelmontículoesconsiderablementemayorqueeltiempoparamanipularlapila(reservarespacioenlapilaamenudoesunaúnicainstruccióndelmicroprocesadorparamoverelpunterodelapilahaciaabajo,yotroparamoverlodenuevohaciaarriba).Elenfoquedinámicoasumequelosobjetostiendenasercomplicados,poresolasobrecargaextradeencontrarespa-cioparaalojarlosydespuésliberarlos,notieneunimpactoimportanteenlacreacióndeunobjeto.Además,elaumentodeexibilidadesesencialpararesolverproblemasgeneralesdeprogramación.Hayotracuestión,sinembargo,yeseltiempodevidadeunobjeto.Sicreaunobjetoenlapilaoenespacioestático,elcompiladordeterminacuántotiempoduraelobjetoypuededestruirloautomáticamente.Perosilocreaenelmontículo,elcom-piladornotieneconocimientodesutiempodevida.EnC++,elprogramadordebedeterminarprogramáticamentecuándodestruirelobjeto,yentoncesllevaracaboladestrucciónusandolapalabrareservadadelete.Comoalternativa,elentornopue-deproporcionarunacaracterísticallamadarecolectordebasura(garbagecollector)queautomáticamentedescubrequéobjetosyanoseusanylosdestruye.Naturalmente,escribirprogramasusandounrecolectordebasuraesmuchomásconveniente,perorequierequetodaslasaplicacionesseancapacesdetolerarlaexistenciadelrecolectordebasuraylasobrecargaquesupone.Esonoencajaenlosrequisitosdeldiseñodel
15
i
i
“Volumen1”—2012/1/12—13:52—page17—#55i
i
i
i
i
i
1.9.Análisisydiseño
cuandoseusaunametodología.EspecialmenteenPOO,lametodologíaesuncampodemuchosexperimentos,asíqueantesdeelegirunmétodo,esimportantequecomprendacuáleselproblemaqueresuelve.EsoesparticularmenteciertoconC++,enelqueellenguajedeprogra-maciónpretendereducirlacomplejidad(comparadoconC)queimplicaexpresarunprograma.Dehecho,puedealiviarlanecesidaddemetodologíasaúnmáscomplejas.Encambio,otrasmássimplespodríansersucientesenC++paramuchostiposdeproblemasgrandesquepodríamanejarusandometodologíassimplesconlenguajesprocedurales.Tambiénesimportantedarsecuentadequeeltérmino«metodología»amenudoesdemasiadograndeyprometedor.Apartirdeahora,cuandodiseñeyescribaunprogramaestaráusandounametodología.Puedesersupropiametodología,ypue-denoserconsciente,peroesunprocesoporelquepasacuandocreaunprograma.Siesunprocesoefectivo,puedequesólonecesiteunpequeñoajusteparaquefun-cioneconC++.Sinoestásatisfechoconsuproductividadyconelcaminoquesusprogramashantomado,puedeconsideraradoptarunmétodoformal,oelegirtrozosdeentremuchosmétodosformales.Mientraspasaporelprocesodedesarrollo,elusomásimportanteeséste:noper-derse.Esoesfácildehacer.Lamayoríadelosanálisisymétodosdediseñopretendenresolverlosproblemasmásgrandes.Recuerdequelamayoríadelosproyectosnoencajanenestacategoría,normalmentepuedetenerunanálisisydiseñoexitosoconunsubconjuntorelativamentepequeñodeloquerecomiendaelmétodo7.Peromu-chostiposdeprocesos,sinimportarlolimitadosquesean,generalmenteleofreceránuncaminomuchomejorquesimplementeempezaracodicar.Tambiénesfácilquedarseestancado,caerenanálisis-parálisis,dondesentiráquenopuedeavanzarporqueenlaplataformaqueestáusandonoestáespecicadoca-dapequeñodetalle.Recuerde,noimportacuántoanálisishaga,hayalgunascosassobreelsistemaquenoserevelanhastaelmomentodeldiseño,ymáscosasquenoserevelaránhastaqueestécodicando,oinclusohastaqueelprogramaestéfuncio-nando.Poreso,escrucialmoversebastanterápidodurantedelanálisisydiseño,eimplementaruntestdelsistemapropuesto.Estepuntomerecelapenaenfatizarlo.Debidoanuestraexperienciaconloslen-guajesprocedurales,esencomiablequeunequipoquieraprocederconcuidadoyen-tendercadapequeñodetalleantesdepasaraldiseñoyalaimplementación.Desdeluego,cuandocreaunSGBD(SistemaGestordeBasesdeDatos),convieneentenderlanecesidaddeunclienteafondo.PerounSGBDestáenunaclasedeproblemasquesonmuyconcretosybienentendidos;enmuchosprogramassemejantes,laestructu-radelabasededatoseselproblemaquedebeafrontarse.Eltipodeproblemadepro-gramacióntratadoenestecapítuloesdelavariedad«comodín»(conmispalabras),enelquelasoluciónnoessimplementeadaptarunasoluciónbienconocida,encam-bioinvolucraunoomás«factorescomodín»-elementosparalosquenohaysoluciónpreviabienentendida,yparalosqueesnecesarioinvestigar8.Intentaranalizarmi-nuciosamenteunproblemacomodínantesdepasaraldiseñoylaimplementaciónprovocaunanálisis-parálisisporquenosetienesucienteinformaciónpararesolverestetipodeproblemadurantelafasedeanálisis.Resolverestosproblemasrequiere
7UnejemploexcelenteesUMLDistilled,deMartinFowler(Addison-Wesley2000),quereduceel,amenudo,insoportableprocesoUMLaunsubconjuntomanejable.8Mireglageneralparaelcálculodesemejantesproyectos:Sihaymásdeuncomodín,nointenteplanearcuántotiempolellevaráocuántocostaráhastaquehayacreadounprototipofuncional.Tambiénhaymuchosgradosdelibertad.
17
i
i
“Volumen1”—2012/1/12—13:52—page18—#56i
i
i
i
i
i
Capítulo1.IntroducciónalosObjetos
interacciónatravésdelciclocompleto,yesorequierecomportamientosarriesgados(locualtienesentido,porqueestáintentandohaceralgonuevoylosbeneciospo-tencialessonmayores).Puedeparecerqueelriesgoestácompuestopor«prisas»enunaimplementaciónpreliminar,peroencambiopuedereducirelriesgoenunpro-yectocomodínporqueestádescubriendoprontosiesviableunenfoqueparticularparaelproblema.Eldesarrollodelproductoesgestiónderiesgos.Amenudoseproponeque«construyaunodesechable».ConlaPOO,todavíadebeandarpartedeestecamino,perodebidoaqueelcódigoestáencapsuladoenclases,durantelaprimeraiteracióninevitablementeproduciráalgunosdiseñosdeclasesútilesydesarrollaráalgunasideasválidassobreeldiseñodelsistemaquenonecesariamentesondesechables.Deestamanera,laprimerapasadarápidaalproblemanoproducesóloinformacióncríticaparalasiguienteiteracióndeanálisis,diseño,eimplementación,sinoqueademáscreaelcódigobaseparaesaiteración.Esdecir,siestábuscandounametodologíaquecontengadetallestremendosysugieramuchospasosydocumentos,esaúnmásdifícilsabercuándoparar.Tengapresenteloqueestáintentandoencontrar:1.¿Cuálessonlosobjetos?(¿Cómodividesuproyectoensuspartescomponen-tes?)2.¿Cuálessonsusinterfaces?(¿Quémensajesnecesitaenviaraotrosobjetos?)Sisólocuentaconlosobjetosysusinterfaces,entoncespuedeescribirunprogra-ma.Porvariasrazonespodríanecesitarmásdescripcionesydocumentos,peronopuedehacerloconmenos.Elprocesosepuederealizarencincofases,yunafase0queessimplementeelcompromisoinicialdeusaralgúntipodeestructura.1.9.1.Fase0:HacerunplanPrimerodebedecidirquépasosvaadarensuproceso.Parecefácil(dehecho,to-doestoparecefácil)ysinembargolagenteamenudonotomaestadecisiónantesdeponerseaprogramar.Sisuplanes«ponersedirectamenteaprogramar»,deacuer-do(avecesesadecuadocuandoesunproblemabienconocido).Almenosestarádeacuerdoenqueesoeselplan.Tambiéndebedecidirenestafasesinecesitaalgunaestructuradeprocesoadicio-nal,peronolasnueveyardascompletas.Bastantecomprensible,algunosprograma-dorespreerentrabajaren«modovacaciones»encuyocasonoseimponeningunaestructuraenelprocesodedesarrollodesutrabajo;«Seharácuandosehaga».Esopuederesultaratractivoduranteuntiempo,perosehadescubiertoquetenerunospocoshitosalolargodelcaminoayudaaenfocareimpulsarsusesfuerzosentornoaesoshitosenlugardeempezaraatascarseconelúnicoobjetivode«nalizarelpro-yecto».Además,divideelproyectoenpiezasmáspequeñasyhacequedémenosmiedo(yademásloshitosofrecenmásoportunidadesparacelebraciones).Cuandoempecéaestudiarlaestructuradelahistoria(poresoalgúndíaescribiréunanovela)inicialmentemeresistíaalaideadeunaestructura,sentíaquecuan-doescribíasimplementepermitíaqueuyeraenlapágina.Peromástardemedicuentadequecuandoescribosobrecomputadoraslaestructuraesbastanteclara,peronopiensomuchosobreello.Peroaúnasíestructuromitrabajo,aunquesólosemi-inconscientementeenmicabeza.Siaúnpiensaquesuplanessóloponerseacodicar,dealgúnmodo,ustedpasaráporlasposterioresfasesmientraspreguntayrespondeciertascuestiones.
18
i
i
“Volumen1”—2012/1/12—13:52—page19—#57i
i
i
i
i
i
1.9.Análisisydiseño
DeclaracióndeobjetivosCualquiersistemaconstruido,noimportacuancomplicadosea,tieneunpropósi-tofundamental,elnegocioquehayenél,lanecesidadbásicaquesatisface.Sipuedeverlainterfazdeusuario,elhardwareolosdetallesespecícosdelsistema,losalgo-ritmosdecodicaciónylosproblemasdeeciencia,nalmenteencontraráelnúcleodesuexistencia,simpleysencillo.ComoelasíllamadoconceptodealtoniveldeunapelículadeHollywood,puededescribirloenunaodosfrases.Estadescripciónpuraeselpuntodepartida.Elconceptodealtonivelesbastanteimportanteporqueledaeltonoasuproyec-to;esunadeclaracióndeprincipios.Notieneporquéconseguirlonecesariamentelaprimeravez(podríatenerquellegaraunafaseposteriordelproyectoantesdetener-locompletamenteclaro),perosigaintentándolohastaqueloconsiga.Porejemplo,enunsistemadecontroldetrácoaéreopuedeempezarconunconceptodealtonivelcentradoenelsistemaqueestáconstruyendo:«Elprogramadelatorresiguelapistaalosaviones».Peroconsiderequéocurrecuandoadaptaelsistemaparaunpequeñoaeropuerto;quizásólohayauncontroladorhumanooninguno.Unmodelomásútilnosepreocuparádelasoluciónqueestácreandotantocomoladescripcióndelproblema:«Llegaunavión,descarga,serevisayrecarga,ysemarcha».1.9.2.Fase1:¿Quéestamoshaciendo?Enlageneraciónpreviadediseñodeprogramas(llamadodiseñoprocedural),estosellamaba«crearelanálisisderequisitosyespecicacióndelsistema».Éstos,porsupues-to,eranlugaresdondeperderse;documentosconnombresintimidantesquepodríanllegarasergrandesproyectosensímismos.Sinembargo,suintenciónerabuena.Elanálisisderequisitosdice:«Hagaunalistadelasdirectricesqueusaráparasabercuándohahechosutrabajoyelclienteestarásatisfecho».Laespecicacióndelsiste-madice:«Hayunadescripcióndeloqueharáelprograma(nocómo)porsatisfacerlosrequisitos».Elanálisisderequisitosesrealmenteuncontratoentreustedyelcliente(inclusosielclientetrabajadentrodesucompañíaoesalgúnotroobjetoosistema).Lasespecicacionesdelsistemasonunaexploracióndealtoniveldelproblemayenalgúnsentidoundescubrimientodesisepuedehacerycuántosetardará.Dadoqueambosrequeriránconsensoentrelagente(yporquesuelencambiartodoeltiempo),creoqueesmejormantenerlostodoloescuetoposible-enelmejordeloscasos,lis-tasydiagramasbásicos-paraahorrartiempo.Podríatenerotrasrestriccionesqueleexijanampliarlaendocumentosmásgrandes,peromanteniendoeldocumentoinicialpequeñoyconciso,puedecrearseenalgunassesionesdetormentasdeideasdegrupoconunlíderquecreeladescripcióndinámicamente.Estonosólosolicitaparticipacióndetodos,tambiénfomentaaprobacióninicialyllegaraacuerdosentretodos.Quizálomásimportanteseaempezarelproyectoconmuchoentusiasmo.Esnecesarionoperderdevistaloqueestáintentandoconseguirenestafase:determinarelsistemaquesesuponequequierehacer.Laherramientamásvaliosaparaesoesunacoleccióndelosllamados«casosdeuso».Loscasosdeusoiden-ticancaracterísticasclaveenelsistemaquepuedenrevelaralgunasdelasclasesfundamentalesqueseusarán.Enesenciasonrespuestasdescriptivasapreguntascomo:9:1.«¿Quiénusaráelsistema?»2.«¿Quépuedenhacerestosactoresconelsistema?»
9GraciasaJamesHJarrettporsuayuda.
19
i
i
“Volumen1”—2012/1/12—13:52—page24—#62i
i
i
i
i
i
Capítulo1.IntroducciónalosObjetos
2.Montajedeobjetos.Siestáconstruyendounobjetodescubrirálanecesidaddenuevosmiembrosquenoaparecendurantelafasededescubrimiento.Lasne-cesidadesinternasdelobjetopuedenrequerirotrasclasesqueledensoporte.3.Construccióndelsistema.Unavezmás,puedenaparecermásrequisitosparaunobjetoalolargodeestaetapa.Conformeaprende,evolucionasusobjetos.Lanecesidaddecomunicacióneinterconexiónconotrosobjetosenelsistemapuedecambiarlasnecesidadesdesusclasesorequerirclasesnuevas.Porejem-plo,puededescubrirlanecesidaddeclasesutileríaoayudantes(helper),comounalistaenlazada,quecontienenonounapequeñainformacióndeestadoyquesimplementeayudanalafuncióndeotrasclases.4.Extensióndelsistema.Cuandoañadanuevascaracterísticasaunsistemapue-dedescubrirquesudiseñoprevionosoportabaextensionessencillasdelsiste-ma.Conestanuevainformación,puedereestructurarpartesdelsistema,posi-blementeañadiendonuevasclasesojerarquíadeclases.5.Reutilizacióndeobjetos.Estaeslaverdaderapruebadeestrésparaunaclase.Sialguienintentareutilizarlaenunasituacióncompletamentenueva,probable-mentedescubriráalgunosdefectos.Sicambiaunaclaseparaadaptarlaanue-vosprogramas,losprincipiosgeneralesdelaclaseseveránmásclaros,hastaqueconsigauntipoverdaderamentereutilizable.Sinembargo,noesperequemuchosobjetosdeldiseñodeunsistemaseanreutilizables-esperfectamenteaceptablequelamayorpartedelosobjetosseanespecícosparaelsistema.Lostiposreutilizablestiendenasermenoscomunes,ydebenresolverproblemasmásgeneralesparaserreutilizables.DirectricesparadesarrollodeobjetosEstasetapassugierenalgunasdirectricescuandosepiensasobreeldesarrollodeclases:1.Permitaqueunproblemaespecícodélugaraunaclase,despuésdejequelaclasecrezcaymaduredurantelasolucióndeotrosproblemas.2.Recuerde,descubrirlasclasesquenecesita(ysusinterfaces)suponelamayorpartedeldiseñodelsistema.Siyateníaesasclases,seráunproyectofácil.3.Noseesfuerceporsabertododesdeelprincipio;aprendaconformeavanza.Ocurriráasídetodosmodos.4.Comienceaprogramar;consigateneralgofuncionandoparapoderaprobarodesaprobarsudiseño.Notengamiedoaqueacabehaciendocódigoproceduralespagueti-lasclasesdividenelproblemayayudanacontrolarlaanarquíaylaentropía.Lasclasesmalasnoestropeanlasbuenas.5.Manténgalosimple.Pequeñosobjetosclarosconutilidadesobviassonmejoresquegrandesinterfacescomplicadas.Cuandoaparezcanlospuntosdedecisión,apliqueelprincipiodelaNavajadeOccam:Considerelasalternativasyelijalamássimple,porquelasclasessimplescasisiempresonmejores.Empiececonclasespequeñasysencillas,ypodráampliarlainterfazcuandolaentiendamejor,perocuandoestoocurra,serádifícileliminarelementosdelaclase.
24
i
i
“Volumen1”—2012/1/12—13:52—page28—#66i
i
i
i
i
i
Capítulo1.IntroducciónalosObjetos
prácticasmejorarásensiblementesuproductividadyabilidad.1.10.1.EscribaprimerolaspruebasElprocesodepruebaseharelegadotradicionalmentealapartenaldelproyecto,despuésdeque«consigatenertodofuncionando,peronecesiteestarseguro».Implí-citamentehatenidounaprioridadbaja,ylagentequeseespecializaenellonuncahatenidoestatusysueletrabajarenelsótano,lejosdelos«programadoresreales».Losequiposdepruebashanrespondidoalestereotipo,vistiendotrajesnegrosyhablan-doconregocijosiemprequeencontrabanalgo(paraserhonesto,yoteníaesamismasensacióncuandoencontrabafallosenloscompiladoresdeC++).XPrevolucionacompletamenteelconceptodelprocesodepruebadándolelamisma(oinclusomayor)prioridadquealcódigo.Dehecho,seescribenlasprue-basantesdeescribirelcódigoqueestáprobando,ylaspruebaspermanecenconelcódigosiempre.Laspruebassedebenejecutarconéxitocadavezquehaceunainte-gracióndelproyecto(algoqueocurreamenudo,avecesmásdeunavezaldía).Escribirprimerolaspruebastienedosefectosextremadamenteimportantes.Primero,fuerzaunadeniciónclaradelainterfazdelaclase.Amenudosugieroquelagente«imaginelaclaseperfectapararesolverunproblemaparticular»co-mounaherramientacuandointentadiseñarelsistema.LaestrategiadelprocesodepruebadeXPvamáslejosqueeso-especicaexactamentecualeselaspectodelaclase,paraelconsumidordeesaclase,yexactamentecómodebecomportarselaclase.Enciertostérminos.Puedeescribirtodalaprosa,ocreartodoslosdiagramasdondequieradescribircómodebecomportarseunaclaseyquéaspectodebetener,peronadaestanrealcomounconjuntodepruebas.Loprimeroesunalistadede-seos,perolaspruebassonuncontratoforzadoporelcompiladoryelprograma.Esdifícilimaginarunadescripciónmásconcretadeunaclasequelaspruebas.Mientrassecreanlaspruebas,elprogramadorestácompletamenteforzadoaela-borarlaclaseyamenudodescubriránecesidadesdefuncionalidadquehabríansidoomitidasdurantelosexperimentosdediagramasUML,tarjetasCRC,casosdeuso,etc.Elsegundoefectoimportantedeescribirlaspruebasprimeroprocededelapro-piaejecucióndelaspruebascadavezquehaceunaconstruccióndelsoftware.Estaactividadleofrecelaotramitaddelprocesodepruebaqueesefectuadoporelcompi-lador.Simiralaevolucióndeloslenguajesdeprogramacióndesdeestaperspectiva,veráquelasmejorasrealesenlatecnologíagiranrealmentealrededordelprocesodeprueba.Ellenguajeensambladorsólosejaenlasintaxis,peroCimponealgu-nasrestriccionesdesemántica,yéstasleimpidencometerciertostiposdeerrores.LoslenguajesPOOimponeninclusomásrestriccionessemánticas,silopiensasonrealmenteformasdelprocesodeprueba.«¿Seutilizaapropiadamenteestetipodedatos?¿Seinvocaestafuncióndelmodocorrecto?»soneltipodepruebasquesellevanacaboporelcompiladorentiempodeejecucióndelsistema.Sehanvistolosresultadosdetenerestaspruebasincorporadasenellenguaje:lagentehasidocapazdeescribirsistemasmáscomplejos,yhanfuncionado,conmuchomenostiempoyesfuerzo.Hetratadodecomprenderporquéocurreeso,peroahoramedoycuentadequesonlaspruebas:elprogramadorhacealgomal,ylareddeseguridaddelaspruebasincorporadasledicequehayunproblemayleindicadónde.Perolaspruebasincorporadasqueproporcionaeldiseñodellenguajenopuedenirmáslejos.Enestepunto,elprogramadordebeinterveniryañadirelrestodelaspruebasqueproducenunjuegocompleto(encooperaciónconelcompiladoryel
28
i
i
“Volumen1”—2012/1/12—13:52—page32—#70i
i
i
i
i
i
Capítulo1.IntroducciónalosObjetos
Comolosnombresestánjerarquizadossegúnlaspartesdesuprogramaporme-diodelosespaciosdenombresdeC++,puedeusartantaslibreríascomoquierasinlosconictosdenombrestípicosdeC.1.11.6.ReutilizacióndecódigofuenteconplantillasHayunacategoríasignicativadetiposquerequieremodicacionesdelcódi-gofuenteparalograrunareutilizaciónefectiva.LasplantillasdeC++llevanacabolamodicacióndelcódigofuenteautomáticamente,convirtiéndolaenunaherra-mientaespecialmentepotenteparalareutilizacióndelcódigodelaslibrerías.Sisediseñauntipousandoplantillasfuncionaráfácilmenteconmuchosotrostipos.Lasplantillassonespecialmenteinteresantesporqueocultanalprogramadorclientelacomplejidaddeestaformadereutilizarcódigo.1.11.7.ManejodeerroresLagestióndeerroresenCesunproblemamuyconocido,yamenudoignorado-cruzandolosdedos.Siestáconstruyendounprogramacomplejoygrande,nohaynadapeorquetenerunerrorenterradoencualquierlugarsinlamenorideadecómollegóallí.LagestióndeexcepcionesdeC++(introducidaenestevolumen,yexpli-cadaendetalleenelVolumen2,quesepuededescargardewww.BruceEckel.com)esuncaminoparagarantizarquesenoticaunerroryqueocurrealgocomoconse-cuencia.1.11.8.ProgramaralograndeMuchoslenguajestradicionalestienenlimitacionespropiasparahacerprogra-masgrandesycomplejos.BASIC,porejemplo,puedevalerparasolucionarciertasclasesdeproblemasrápidamente,perosielprogramatienemásdeunascuantaspáginasosesaledeldominiodeproblemasdeeselenguaje,escomointentarnadaratravésdeunuidocadavezmásviscoso.Ctambiéntieneestaslimitaciones.Porejemplo,cuandounprogramatienemásde50.000líneasdecódigo,losconictosdenombresempiezanaserunproblema-efectivamente,sequedasinnombresdefuncionesovariables.Otroproblemaparticularmentemalosonlospequeñosagu-jerosenellenguajeC-erroresenterradosenunprogramagrandequepuedenserextremadamentedifícilesdeencontrar.Nohayunalíneaclaraquedigacuandounlenguajeestáfallando,ysilahubiese,deberíaignorarla.Nodiga:«MiprogramaBASICsehahechodemasiadogrande;¡lotendréquereescribirenC!»Ensulugar,intentecalzarunascuantaslíneasmásparaañadirleunanuevacaracterística.Deesemodo,elcosteextralodecideusted.C++estádiseñadoparaayudarleaprogramaralogrande,esdecir,eliminarlasdiferenciasdecomplejidadentreunprogramapequeñoyunogrande.CiertamentenonecesitausarPOO,plantillas,espaciosdenombresnimanejadoresdeexcepcionescuandoestéescribiendounprogramatipo«holamundo»,peroestasprestacionesestánahíparacuandolasnecesite.Yelcompiladoresagresivoenladeteccióndeerrorestantoparaprogramaspequeñoscomograndes.
32
i
i
“Volumen1”—2012/1/12—13:52—page38—#76i
i
i
i
i
i
Capítulo2.Construiryusarobjetos
des.ElBASIC,porejemplo,fueunlenguajeinterpretadobastantepopular.Losin-térpretesdeBASICtradicionalestraducenyejecutanunalíneacadavez,ydespuésolvidanlalíneatraducida.Estoloshacelentosdebidoaquedebenvolveratradu-circualquiercódigoqueserepita.BASICtambiénhasidocompiladoparaganarenvelocidad.Lamayoríadelosintérpretesmodernos,comolosdePython,traducenelprogramaenteroenunlenguajeintermedioqueesejecutableporunintérpretemuchomásrápido1.Losintérpretestienenmuchasventajas.Latransicióndelcódigoescritoalcódigoejecutableescasiinmediata,yelcódigofuenteestásiempredisponible,porloqueelintérpretepuedesermuchomásespecícocuandoocurreunerror.Losbeneciosquesesuelenmencionardelosintérpreteseslafacilidaddeinteracciónyelrápidodesarrollo(perononecesariamenteejecución)delosprogramas.Loslenguajesinterpretadosamenudotienenseveraslimitacionescuandosecons-truyengrandesproyectos(Pythonpareceserunaexcepción).Elintérprete(ounaversiónreducida)debeestarsiempreenmemoriaparaejecutarelcódigoeinclusoelintérpretemásrápidopuedeintroducirrestriccionesdevelocidadinaceptables.Lamayoríadelosintérpretesrequierenquetodoelcódigofuenteselesenvíedeunasolavez.Estonosólointroducelimitacionesdeespacio,sinoquepuedecausarerro-resdifícilesdedetectarsiellenguajenoincluyefacilidadesparalocalizarelefectodelasdiferentesporcionesdecódigo.2.1.2.CompiladoresUncompiladortraduceelcódigofuentedirectamentealenguajeensambladoroinstruccionesmáquina.Elproductonalsueleserunoovarioscherosquecontie-nencódigomáquina.Laformaderealizarlosueleserunprocesoqueconstadevariospasos.Latransicióndelcódigoescritoalcódigoejecutableessignicativamentemáslargaconuncompilador.Dependiendodelaperspicaciadelescritordelcompilador,losprogramasgene-radosporuncompiladortiendenarequerirmuchomenosespacioparaserejecuta-dos,yseejecutanmuchomásrápido.Aunqueeltamañoylavelocidadsonproba-blementelasrazonesmáscitadasparausaruncompilador,enmuchassituacionesnosonlasmásimportantes.Algunoslenguajes(comoelC)estándiseñadosparaadmitirtrozosdeprogramascompiladosindependientemente.Estaspartestermi-nancombinandoenunprogramaejecutablenalmedianteunaherramientallamadaenlazador(linker).Esteprocesoseconocecomocompilaciónseparada.Lacompilaciónseparadatienemuchosbenecios.Unprogramaque,tomadodeunavez,excederíaloslímitesdelcompiladorodelentornodecompilaciónpuedesercompiladoporpiezas.Losprogramassepuedenserconstruiryprobarpiezaapieza.Unavezqueunapartefunciona,sepuedeguardarytratarsecomounbloque.Losconjuntosdepiezasyafuncionalesyprobadassepuedencombinarenlibreríasparaqueotrosprogramadorespuedanusarlos.Comosecreanpiezas,lacomplejidaddelasotraspiezassemantieneoculta.Todasestascaracterísticasayudanalacreacióndeprogramasgrandes,2.Lascaracterísticasdedepuracióndelcompiladorhanmejoradoconsiderable-
1Loslímitesentreloscompiladoresylosintérpretestiendenaserdifusos,especialmenteconPython,quetienemuchasdelascaractéristicasyelpoderdeunlenguajecompiladoperotambiéntienepartedelasventajasdeloslenguajesinterpretados.2Pythonvuelveaserunaexcepción,debidoaquepermitecompilaciónseparada.
38
i
i
“Volumen1”—2012/1/12—13:52—page40—#78i
i
i
i
i
i
Capítulo2.Construiryusarobjetos
enunúnicochero.Unalibreríasecreaymantieneporunprogramaconocidocomobibliotecario(librarian).ComprobaciónestáticadetiposElcompiladorrealizaunacomprobacióndetiposdurantelaprimerapasada.Lacomprobacióndetiposaseguraelcorrectousodelosargumentosenlasfuncionesyprevienemuchostiposdeerroresdeprogramación.Comoestacomprobacióndetiposocurresehacelacompilaciónynocuandoelprogramaseestáejecutado,seconocecomocomprobaciónestáticadetipos.Algunoslenguajesorientadosaobjetos(Javaporejemplo)realizancomproba-cionesentiempodeejecución(comprobacióndinámicadetipos).Sisecombinaconlaestática,lacomprobacióndinámicaesmáspotentequesólolaestática.Sinembargo,añadeunasobrecargaalaejecucióndelprograma.C++usalacomprobaciónestáticadetiposdebidoaqueellenguajenopuedeasumirningúnsoporteparticulardurantelaejecución.Lacomprobaciónestáticadetiposnoticaalprogramadormalosusosdelostiposdurantelacompilación,yasímaximizalavelocidaddeejecución.AmedidaqueaprendaC++,comprobaráquelamayoríadelasdecisionesdediseñodellenguajeestántomadasenfavordelamejoradelrendimiento,motivoporelcualCesfamosoenlaprogramaciónorientadaalaproducción.SepuededeshabilitarlacomprobaciónestáticadetiposenC++,einclusoper-mitealprogramadorusarsupropiacomprobacióndinámicadetipos-simplementenecesitaescribirelcódigo.2.2.HerramientasparacompilaciónmodularLacompilaciónmodularesparticularmenteimportantecuandoseconstruyengrandesproyectos.EnCyenC++,unprogramasepuedecrearenpequeñaspiezas,manejablesycomprobablesdeformaindependiente.Laherramientamásimpor-tanteparadividirunprogramaenpiezasmáspequeñaseslacapacidaddecrearsubrutinasosubprogramasquetenganunnombrequelasidentique.EnCyenC++,estossubprogramassellamanafunciones,quesonlaspiezasdecódigoquesepuedenalmacenarendiferentescheros,permitiendolacompilaciónseparada.Di-chodeotraforma,unafuncióneslaunidadatómicadecódigo,debidoaquenosepuedetenerunapartedeunafunciónenuncheroyelrestoenotro(aunqueloscherospuedencontenermásdeunafunción).Cuandoseinvocaunafunción,selesuelenpasarunaseriedeargumentos,quesonvaloresquedeseaquelafunciónutilicedurantesuejecución.Cuandolafun-cióntermina,normalmentedevuelveunvalorderetorno,queequivalealresultado.Tambiénesposiblecrearfuncionesquenotenganniargumentosnivalorderetorno.Paracrearunprogramaconmúltiplescheros,lasfuncionesdeuncherodebenaccederalasfuncionesylosdatosdeotroscheros.Cuandosecompilaunchero,elcompiladordeCoC++debeconocerlasfuncionesylosdatosdelosotroscheros,enparticularsusnombresysuusoapropiado.Elcompiladoraseguraquelasfun-cionesylosdatossonusadoscorrectamente.Elprocesode"decirlealcompilador"losnombresdelasfuncionesexternasylosdatosquenecesitanesconocidocomodeclaración.Unavezdeclaradaunafunciónounavariable,elcompiladorsabecómocomprobarquelafunciónseutilizaadecuadamente.
40
i
i
“Volumen1”—2012/1/12—13:52—page42—#80i
i
i
i
i
i
Capítulo2.Construiryusarobjetos
ejemplo,sepuededeclararfunc1()conunaaparienciadiferenteperoconelmismosignicado:
intfunc1(intlength,intwidth);
UnapuntualizaciónExisteunadiferenciasignicativaentreCyelC++paralasfuncionesconlistadeargumentosvacía.EnC,ladeclaración:
intfunc2();
signica«unafuncionconcualquiernúmeroytipodeargumentos»,locualanulalacomprobacióndetipos.EnC++,sinembargo,signica«unafunciónsinargumen-tos».DenicióndefuncionesLadenicióndefuncionesseparecealadeclaraciónexceptoenquetienencuer-po.Uncuerpoesunconjuntodesentenciasencerradasentrellaves.Lasllavesin-dicanelcomienzoyelnaldelcódigo.Paradarafunc1()unadeniciónconuncuerpovacío(uncuerpoquenocontienecódigo),escriba:
intfunc1(intancho,intlargo){}
Notequeenladenicióndelafunciónlasllavessustituyenelpuntoycoma.Comolasllavescontienenunasentenciaogrupodesentencias,noesnecesariounpuntoycoma.Tengaencuentaademásquelosargumentosenladenicióndelafuncióndebennombressilosquiereusarenelcuerpodelafunción(comoaquínoseusan,sonopcionales).SintaxisdedeclaracióndevariablesElsignicadoatribuidoalafrase«declaracióndevariables»históricamentehasidoconfusoycontradictorio,yesimportantequeentiendaelsignicadocorrectoparapoderleerelcódigocorrectamente.Unadeclaracióndevariabledicealcom-piladorcómoeslavariable.Dicealcompilador,«Séquenohasvistoestenombreantes,peroteprometoqueexisteenalgúnlugar,yqueesunavariabledetipoX».Enunadeclaracióndefunción,sedauntipo(elvalorderetorno),elnombredelafunción,lalistadeargumentos,yunpuntoycoma.Conestoelcompiladoryatienesucienteinformaciónparasabercómoserálafunción.Porinferencia,unadeclaracióndevariableconsistiráenuntiposeguidoporunnombre.Porejemplo:
inta;
podríadeclararlavariableacomounenterousandolalógicausadaanterior-mente.Peroaquíestáelconicto:existesucienteinformaciónenelcódigoanteriorcomoparaqueelcompiladorpuedacrearespacioparaunenterollamadoayesexactamenteloqueocurre.Pararesolvereldilema,fuenecesariaunapalabrare-servadaenCyC++paradecir«Estoessólounadeclaración;estavariableestarádenidaenalgúnotrolado».Lapalabrareservadaesexternquepuedesignicar
42
i
i
“Volumen1”—2012/1/12—13:52—page45—#83i
i
i
i
i
i
2.2.Herramientasparacompilaciónmodular
#includeiostream�
Eltraductorpuedeimplementarlasentenciadelincludedetalformaqueseamoldealasnecesidadesdeuncompiladorysistemaoperativoparticular,aunqueseanecesariotruncarelnombreyañadirunaextensión.Evidentemente,tambiénpuedecopiarlascabecerasqueofreceelfabricantedesucompiladoraotrassinex-tensionessiquiereusarestenuevoestiloantesdequesufabricantelosoporte.LaslibreríasheredadasdeCaúnestándisponiblesconlaextensióntradicional«.h».Sinembargo,sepuedenusarconelestilodeinclusiónmásmodernocolocandouna«c»alnombre.Esdecir:
#includestdio.h�
#includestdlib.h�
Setransformaríaen:
#includecstdio�
#includecstdlib�
YasíparatodascabecerasdelCEstándar.Esoproporcionaallectorunadistin-cióninteresanteentreelusodelibreríasCversusC++.Elefectodelnuevoformatodeincludenoesidénticoalantiguo:usarel«.h»dacomoresultadounaversiónmásantigua,sinplantillas,yomitiendoel«.h»leofre-celanuevaversiónconplantillas.Normalmentepodríatenerproblemassiintentamezclarlasdosformasdeinclusiónenunmismoprograma.2.2.2.EnlazadoElenlazador(linker)agrupalosmódulosobjeto(queamenudotienenextensio-nescomo.oó.obj),generadosporelcompilador,enunprogramaejecutablequeelsistemaoperativopuedecargaryejecutar.Eslaúltimafasedelprocesodecompi-lación.Lascaracterísticasdelenlazadorvaríandeunsistemaaotro.Engeneral,simple-menteseindicanalenlazadorlosnombresdelosmódulosobjeto,laslibreríasquesedeseanenlazaryelnombredelejecutabledesalida.Algunossistemasrequierenqueseaelprogramadorelqueinvoquealenlazador,aunqueenlamayoríadelospaque-tesdeC++sellamaalenlazadoratravésdelcompilador.Enmuchassituaciones,demaneratransparente.Algunosenlazadoresantiguosnobuscabancherosobjetomásdeunavezybus-cabanenlalistaqueselespasabadeizquierdaaderecha.Estosignicaqueelordendeloscherosobjetoylaslibreríaspuedeserimportante.Siseencuentraconalgúnproblemamisteriosoquenoaparecehastaelprocesodeenlazado,unaposiblerazóneselordenenelqueseindicanloscherosalenlazador.2.2.3.UsodelibreríasAhoraqueyaconocelaterminologíabásica,puedeentendercómoutilizarunalibrería.Parausarla:
45
i
i
“Volumen1”—2012/1/12—13:52—page46—#84i
i
i
i
i
i
Capítulo2.Construiryusarobjetos
1.Seincluyeelcherodecabeceradelalibrería.2.Seusanlasfuncionesylasvariablesdelalibrería.3.Seenlazalalibreríajuntoconelprogramaejecutable.Estospasostambiénseaplicancuandolosmódulosobjetonosecombinanparaformarunalibrería.IncluirelcherocabecerayenlazarlosmódulosobjetoeslabaseparalacompilaciónseparadaenCyenC++.CómobuscaelenlazadorunalibreríaCuandosehaceunarefenciaexternaaunafunciónounavariableenCoC++,alenlazador,unavezencontradaestareferencia,puedehacerdoscosas.Sitodavíanohaencontradoladenicióndelafunciónovariable,añadeelidenticadorasulistade«referenciasnoresueltas».Sielenlazadoryahabíaencontradoladenición,seresuelvelareferencia.Sielenlazadornopuedeencontrarladeniciónenlalistademódulosobjeto,buscaenlaslibrerías.Laslibreríastienenalgúntipodeindexaciónparaqueelen-lazadornonecesitebuscarentodoslosmódulosobjetoenlalibrería-solamentemiraenelíndice.Cuandoelenlazadorencuentraunadeniciónenunalibrería,elmóduloobjetoentero,nosóloladenicióndelafunción,seenlazaalprogramaeje-cutable.Desecuentaquenoseenlazalalibreríacompleta,tansoloelmóduloobjetodelalibreríaquecontieneladeniciónquesenecesita(deotraformalosprogramassevolveríaninnecesariamentelargos).Sisedeseaminimizareltamañodelprogra-maejecutable,sedeberíaconsiderarponerunaúnicafunciónencadacherofuentecuandoseconstruyanlibreríaspropias.Estorequieremástrabajodeedición,4peropuedesermuyútilparaelusuario.Debidoaqueelenlazadorbuscaloscherosenelordenqueseledan,sepuedeprevenirelusodeunafuncióndeunalibreríainsertandouncheroconsupropiafunción,usandoelmismonombredefunción,enlalistaantesdequeaparezcaelnombredelalibrería.Cuandoelenlazadorresuelvacualquierreferenciaaesafun-ciónencontrandolafunciónantesdebuscarenlalibrería,seutilizarásufunciónenlugardelaqueseencuentraenlalibrería.Esotambiénpuedeserunafuentedeerrores,yeslaclasedecosasquesepuedeevitarusandolosespaciosdenombres(namespaces)deC++.AñadidosocultosCuandosecreaunprogramaejecutableenC/C++,ciertoselementosseenlazanensecreto.Unodeestoselementoseselmódulodearranque,quecontienerutinasdeinicializaciónquedebenejecutarsecadavezquearrancaunprogramaCoC++.Estasrutinaspreparanlapilaeinicializanciertasvariablesdelprograma.Elenlazadorsiemprebuscalalibreríaestándarparalasversionescompiladasdecualquierfunción«estándar»llamadaenelprograma.Debidoaquesebuscasiem-preenlalibreríaestándar,sepuedeusarcualquiercosadeestalibreríasimplementeañadiendoasuprogramalacabeceraapropiada;nonecesitaindicardóndehayquebuscarlalibreríaestándar.Lasfuncionesdeujodeentrada-salida(iostream),porejemplo,estánenlaLibreríaEstándardeC++.Parausarla,sólodebeincluirelcherodecabecera&#x-500;iostream.
4YolerecomendaríausarPerloPythonparaautomatizarestastareascomopartedesuprocesodeempaquetamientodelibrerías(verwww.Perl.orgówww.Python.org).
46
i
i
“Volumen1”—2012/1/12—13:52—page48—#86i
i
i
i
i
i
Capítulo2.Construiryusarobjetos
envíalacadena«howdy!»alobjetollamadocout(queesundiminutivode«con-soleoutput»(salidaporconsola).Demomentoyahemosvistosucientesobrecargadeoperadorescomoparapo-derempezar.ElCapítulo12cubrelasobrecargadeoperadorescondetalle.2.3.2.EspaciosdenombresComosemencionaenelCapítulo1,unodelosproblemasdellenguajeCesque«nosquedamossinnombres»parafuncioneseidenticadorescuandolosprogra-maslleganaserdeciertotamaño.Porsupuestoquerealmentenonosquedamossinnombres;aunquesehacemásdifícilpensarennombresnuevosdespuésdeunrato.Ytodavíamásimportante,cuandounprogramaalcanzaciertotamañoesnor-malfragmentarloentrozosmáspequeñoscadaunodeloscualesesmantenidopordiferentespersonasogrupos.ComoCsólotieneunruedoparalidiarcontodoslosidenticadoresynombresdefunción,traecomoconsecuenciaquetodoslosdesa-rrolladoresdebentenercuidadodenousaraccidentalmentelosmismosnombresensituacionesenlasquepuedenponerseenconicto.Estoseconvierteenunapérdidadetiempo,sehacetediosoyenúltimotérmino,esmáscaro.ElC++Estándartieneunmecanismoparaimpedirestascolisiones:lapalabrareservadanamespace(espaciodenombres).Cadaconjuntodedenicionesdeunalibreríaoprogramase«envuelve»enunespaciodenombres,ysiotradenicióntieneelmismonombre,peroestáenotroespaciodenombres,entoncesnoseproducecolisión.Elespaciodenombresesunaherramientaútilyconveniente,perosupresen-ciaimplicaquedebesaberusarlaantesdeescribirunprograma.Sisimplementeescribeuncherodecabecerayusaalgunasfuncionesuobjetosdeesacabecera,probablementerecibaextrañosmensajescuandocompileelprograma,debidoaqueelcompiladornopuedaencontrarlasdeclaracionesdeloselementosdelcherodecabecera.Despuésdeverestemensajeunpardevecesseleharáfamiliarsusigni-cado(quees:Ustedhaincluidoelcherodecabeceraperotodaslasdeclaracionesestánsinunespaciodenombresynoledijoalcompiladorquequeríausarlasdeclaracioneseneseespaciodenombres).Hayunapalabrareservadaquelepermitedecir«quierousarlasdeclaracionesy/odenicionesdeesteespaciodenombres».Esapalabrareservada,bastanteapro-piadaporcierto,esusing.TodaslaslibreríasdeC++Estándarestánincluidasenunúnicoespaciodenombres,queesstd(por«standard»).Comoestelibrousalalibreríaestándarcasiexclusivamente,verálasiguientedirectivausingencasitodoslosprogramas.
usingnamespacestd;
Estosignicaquequiereusartodosloselementosdelespaciodenombresllama-dostd.Despuésdeestasentencia,yanohayquepreocuparsedesisucomponenteolibreríaparticularperteneceaunespaciodenombres,porqueladirectivausinghacequeelespaciodenombresestédisponibleparatodoelcherodondeseescribióladirectivausing.Exponertodosloselementosdeunespaciodenombresdespuésdequealguiensehamolestadoenocultarlos,parececontraproducente,ydehecho,ellectordeberátenercuidadosiconsiderahacerlo(comoaprenderámástardeenestelibro).Sin
48
i
i
“Volumen1”—2012/1/12—13:52—page50—#88i
i
i
i
i
i
Capítulo2.Construiryusarobjetos
/**/yseusaampliamenteenestelibro.2.3.4.«Hello,World!»Yporn,elprimerprograma:
//:C02:Hello.cpp
//SayingHellowithC++
#include&#xiost;&#xream;//Streamdeclarations
usingnamespacestd;
intmain(){
cout"Hello,World!Iam"
8"Today!"endl;
}///:~
Elobjetocoutmanejaunaseriedeargumentospormediodelosoperadores,queimprimelosargumentosdeizquierdaaderecha.Lafunciónespecialen-dlprovocaunsaltodelínea.Conlosiostreamssepuedeencadenarunaseriedeargumentoscomoaquí,loquehacequeseaunaclasefácildeusar.EnC,eltextoqueseencuentraentrecomillasdoblessedenomina«cadena»(string).Sinembargo,lalibreríaEstándardeC++tieneunapoderosaclasellama-dastringparamanipulacióndetexto,porloqueusaremoseltérminomásprecisoarraydecaracteresparaeltextoqueseencuentreentredoblescomillas.ElcompiladorpideespaciodememoriaparalosarraysdecaracteresyguardaelequivalenteASCIIparacadacarácterenesteespacio.Elcompiladornalizaauto-máticamenteestearraydecaracteresañadiendoelvalor0paraindicarelnal.Dentrodelarraydecaracteres,sepuedeninsertarcaracteresespecialesusandolassecuenciasdeescape.Consistenenunabarrainvertida(\)seguidadeuncódigoespecial.porejemplo\nsignicasaltodelínea.ElmanualdelcompiladorolaguíaconcretadeCofreceunalistacompletadesecuencia;entreotrasseincluye:\t(ta-bulador),\\(barrainvertida),y\b(retroceso).Tengaencuentaquelasentenciapuedecontinuarenotraslíneas,ylasentenciacompletaterminaconunpuntoycoma.Losargumentosdetipoarraydecaracteresylosnúmerosconstantesestánmez-cladosenlasentenciacoutanterior.Comoeloperadorestásobrecargadoconvariossignicadoscuandoseusaconcout,sepuedenenviardistintosargumentosycoutseencargarádemostrarlos.Alolargodeestelibronotaráquelaprimeralíneadecadacheroesuncomen-tario(empezandonormalmentecon//),seguidodedospuntos,ylaúltimalíneadecadalistadodecódigoacabaconuncomentarioseguidode«/-».Setratadeunaunatécnicaqueusoparaextraerfácilmenteinformacióndeloscherosfuen-te(elprogramaquelohacesepuedeencontrarenelVolumen2deestelibro,enwww.BruceEckel.com).Laprimeralíneatambiéntieneelnombreylocalizacióndelchero,porloquesepuedelocalizarfácilmenteenloscherosdecódigofuentedellibro(quetambiénsepuededescargardewww.BruceEckel.com).
50
i
i
“Volumen1”—2012/1/12—13:52—page54—#92i
i
i
i
i
i
Capítulo2.Construiryusarobjetos
#include&#xiost;&#xream;
usingnamespacestd;
intmain(){
strings1,s2;//Emptystrings
strings3="Hello,World.";//Initialized
strings4("Iam");//Alsoinitialized
s2="Today";//Assigningtoastring
s1=s3+""+s4;//Combiningstrings
s1+="8";//Appendingtoastring
couts1+s2+"!"endl;
}///:~
Lasdosprimerascadenas,s1ys2empiezanestandovacías,mientrasques3ys4muestrandosformasdeinicializarlosobjetosstringconarraysdecaracteres(puedeinicializarobjetosstringigualdefácilconotrosobjetosstring).Sepuedeasignaraunobjetostringusando=.Esosustituyeelcontenidopre-viodelacadenaconloqueseencuentraenelladoderechodelaasignación,ynohayquepreocuparsedeloqueocurreconelcontenidoanteriorporquesecontrolaautomáticamente.Paracombinarlascadenassimplementedebeusareloperadordesuma«+»,quetambienlepermiteconcatenarcadenas(strings)conarraysdeca-racteres.Siquiereañadirunacadenaounarraydecaracteresaotracadena,puedeusareloperador+=.Finalmente,désecuentaqueiostreamsabecomotratarlascadenas,porloqueustedpuedeenviarunacadena(ounaexpresiónqueproduzcaunstring,queesloquesucedecons1+s2+"!"�)directamenteacoutparaimprimirla.2.6.LecturayescrituradecherosEnC,elprocesodeabrirymanipularcherosrequeríaungranconocimientodellenguajeparaprepararleparalacomplejidaddelasoperaciones.Sinembargo,lalibreríaiostreamdeC++proporcionaunaformasimpledemanejarcheros,yporesosepuedepresentarmuchoantesdeloqueseharíaenC.Parapoderabriruncheroparaleeryescribir,debeincluirlalibreríafstream.Aunqueesoimplicalainclusiónautomáticadelalibreríaiostream,esprudenteincluiriostreamsiplaneausarcin,cout,etc.Paraabriruncheroparalectura,debecrearunobjetoifstreamqueseusarácomocin.Paracrearuncherodeescritura,secreaunobjetoofstreamquesecomportacomocout.Unavezquetieneabiertoelcheropuedeleeroescribirenélcomosiusaracualquierobjetoiostream.Asídesimple,queeselobjetivo,porsupuesto.Unadefuncioneslasmásútilesdelalibreríaiostreamesgetline(),quepermiteleerunalínea(terminadaennuevalínea)yguardarlaenunobjetostring5.Elprimerargumentoeselobjetoifstreamdelquesevaaleerlainformaciónyelsegundoargumentoeselobjetostring.Cuandoterminalallamadaalafunción,elobjetostringcontienelalíneacapturada.Aquíhayunejemploquecopiaelcontenidodeuncheroenotro.
5Actualmenteexistenvariantesdegetline(),quesediscutiránprofusamenteenelcapítulodeiostreamsenelVolumen2
54
i
i
“Volumen1”—2012/1/12—13:52—page55—#93i
i
i
i
i
i
2.6.Lecturayescrituradecheros
//:C02:Scopy.cpp
//Copyonefiletoanother,alineatatime
#include&#xstri;&#xng00;
#includestr;êm0;
usingnamespacestd;
intmain(){
ifstreamin("Scopy.cpp");//Openforreading
ofstreamout("Scopy2.cpp");//Openforwriting
strings;
while(getline(in,s))//Discardsnewlinechar
outs"\n";//...mustadditback
}///:~
Paraabrirloscheros,únicamentedebecontrolarlosnombresdecheroqueseusanenlacreacióndelosobjetosifstreamyofstream.Aquísepresentaunnuevoconcepto:elbuclewhile.Aunqueseráexplicadoendetalleenelsiguientecapítulo,laideabásicaconsisteenquelaexpresiónentrepa-réntesisquesiguealwhilecontrolalaejecucióndelasentenciasiguiente(puedensermúltiplessentenciasencerradasentrellaves).Mientraslaexpresiónentreparén-tesis(enestecasogetline(in,s)produzcaunresultado«verdadero»,lassen-tenciascontroladasporelwhileseejecutarán.getline()devuelveunvalorquesepuedeinterprercomo«verdadero»sisehaleidootralíneadeformasatisfactoria,y«falso»cuandosellegaalnaldelaentrada.Esoimplicaqueelwhileanteriorleetodaslaslíneasdelcherodeentradaylasenvíaalcherodesalida.getline()leeloscaracteresdecadalíneahastaquedescubreunsaltodelínea(elcarácterdeterminaciónsepuedecambiarperoesonoseveráhastaelcapítulosobreiostreamsdelVolumen2).Sinembargo,descartaelcarácterdenuevalíneaynoloalmacenaenelobjetostring.Porloquesiqueremoscopiarelcherodefor-maidénticaaloriginal,debemosañadirelcarácterdenuevalíneacomosemuestraarriba.Otroejemplointeresanteescopiarelcheroenteroenunúnicoobjetostring:
//:C02:FillString.cpp
//Readanentirefileintoasinglestring
#include&#xstri;&#xng00;
#include&#xiost;&#xream;
#includestr;êm0;
usingnamespacestd;
intmain(){
ifstreamin("FillString.cpp");
strings,line;
while(getline(in,line))
s+=line+"\n";
couts;
}///:~
Debidoalanaturalezadinámicadelosstrings,nohayquepreocuparsedelacantidaddememoriaquehayquereservarparaelstring.Simplementehayqueañadircosasyelstringiráexpandiéndoseparadarcabidaaloqueleintroduzca.
55
i
i
“Volumen1”—2012/1/12—13:52—page58—#96i
i
i
i
i
i
Capítulo2.Construiryusarobjetos
while(getline(in,line))
v.push_back(line);//Addthelinetotheend
//Addlinenumbers:
for(inti=0;iv.size();i++)
couti":"v[i]endl;
}///:~
Casitodoesteprogramaessimilaralanterior;seabreuncheroabiertoyseleenlaslíneasenobjetosstring(unocadavez).Sinembargo,estosobjetosstringseintroducenalnaldelvectorv.Unavezqueelbuclewhilehaterminado,elcheroenteroseencuentraenmemoriadentrodev.Lasiguientesentenciaenelprogramaesunbuclefor.Esparecidoaunbuclewhileaunqueañadeuncontrolextra.Comoenelbuclewhile,enelforhayuna«expresióndecontrol»dentrodelparéntesis.Sinembargo,estaexpresiónestádi-vididaentrespartes:unapartequeinicializa,unaquecompruebasihayquesalirdelbucle,yotraquecambiaalgo,normalmentedaunpasoenunasecuenciadeele-mentos.Esteprogramamuestraelbuclefordelamaneramáshabitual:lapartedeinicializacióninti=0creaunenteroiparausarlocomocontadoryledaelvalorinicialdecero.Lacomprobaciónconsisteenversiiesmenorqueelnúmerodeelementosdelvectorv.(Estoseconsigueusandolafunciónmiembrosize()-tamaño-quehayqueadmitirquetieneunsignicadoobvio)Elúltimotrozo,usaeloperadorde«autoincremento»paraaumentarenunoelvalordei.Efectivamente,i++dice«cogeelvalordeiañádeleunoyguardaelresultadoeni».Conclusión:elefectodelbucleforesaumentarlavariableidesdecerohastaeltamañodelv-ectormenosuno.Porcadanuevovalordeiseejecutalasentenciadelcout,queconstruyeunlineaconelvalordei(mágicamenteconvertidaaunarraydecarac-teresporcout),dospuntos,unespacio,lalíneadelcheroyelcarácterdenuevalíneaquenosproporcionaendl.Cuandolocompileyloejecuteveráelefectodenumeracióndelíneasdelchero.Debidoaqueeloperador��funcionaconiostreams,sepuedemodicarfácil-menteelprogramaanteriorparaqueconviertalaentradaenpalabrasseparadasporespacios,envezdelíneas:
//:C02:GetWords.cpp
//Breakafileintowhitespace-separatedwords
#include&#xstri;&#xng00;
#include&#xiost;&#xream;
#includestr;êm0;
#include&#xvect;&#xor00;
usingnamespacestd;
intmain(){
vectorstring�words;
ifstreamin("GetWords.cpp");
stringword;
while(in��word)
words.push_back(word);
for(inti=0;iwords.size();i++)
coutwords[i]endl;
}///:~
Laexpresión:
58
i
i
“Volumen1”—2012/1/12—13:52—page59—#97i
i
i
i
i
i
2.8.Resumen
while(in��word)
eslaqueconsiguequeseleauna«palabra»cadavez,ycuandolaexpresiónseevalúacomo«falsa»signicaquehallegadoalnaldelchero.Deacuerdo,deli-mitarunapalabramediantecaracteresenblancoesunpocotosco,perosirvecomoejemplosencillo.Mástarde,enestelibro,veráejemplosmássosticadosqueleper-mitendividirlaentradadelaformaquequiera.Parademostrarlofácilqueesusarunvectorconcualquiertipo,aquítieneunejemploquecreaunvectordeenteros:
//:C02:Intvector.cpp
//Creatingavectorthatholdsintegers
#include&#xiost;&#xream;
#include&#xvect;&#xor00;
usingnamespacestd;
intmain(){
vectorint&#x]TJ/;ྖ ;.96;d T; 37;&#x.659;&#x 0 T; [0;v;
for(inti=0;i10;i++)
v.push_back(i);
for(inti=0;iv.size();i++)
coutv[i]",";
coutendl;
for(inti=0;iv.size();i++)
v[i]=v[i]*10;//Assignment
for(inti=0;iv.size();i++)
coutv[i]",";
coutendl;
}///:~
Paracrearunvectorquemanejeuntipodiferentebastaconponereltipoentrelasllavesangulares(elargumentodelasplantillas).Lasplantillasylaslibreríasdeplantillaspretendenofrecerprecisamenteestafacilidaddeuso.Ademásesteejemplodemuestraotracaracterísticaesencialdelvectorenlaex-presión
v[i]=v[i]*10;
Puedeobservarqueelvectornoestálimitadoametercosasysacarlas.Tambiénpuedeasignar(esdecir,cambiar)cualquierelementodelvectormedianteelusodeloscorchetes.Esosignicaqueelvectoresunobjetoútil,exibleydepropósitogeneralparatrabajarconcoleccionesdeobjetos,yharemosusodeélenlossiguientescapítulos.2.8.ResumenEstecapítulopretendemostrarlelofácilquepuedellegaraserlaprogramaciónorientadaaobjetos-sialguienhahechoeltrabajodedenirlosobjetosporusted.Enestecaso,sólohayqueincluirelcherodecabecera,crearlosobjetosyenviarlesmensajes.Silostiposqueestáusandoestánbiendiseñadosysonpotentes,entonces
59
i
i
“Volumen1”—2012/1/12—13:52—page60—#98i
i
i
i
i
i
Capítulo2.Construiryusarobjetos
notendrámuchotrabajoysuprogramaresultantetambiénserápotente.EnesteprocesoparamostrarlasencillezdelaPOOcuandoseusanlibreríasdeclases,estecapítulo,tambiénintroducealgunosdelostiposdedatosmásbásicosyútilesdelaLibreríaEstándardeC++:Lafamiliadelosiostreams(enparticularaquellosqueleenyescribenenconsolaycheros),laclasestring,ylaplantillavector.Havistolosencilloqueesusarlosyahoraesprobablequeseimagineladecosasquesepuedenhacerconellos,perohaymuchasmáscosasquesoncapacesderealizar6.Apesardeestarusandounpequeñosubconjuntodelafuncionalidaddeestasherramientasenesteprincipiodellibro,suponeungranavancefrentealosrudimentarioscomienzosenelaprendizajedeunlenguajedebajonivelcomoC.YaunqueaprenderlosaspectosdebajoniveldeCeseducativotambiénllevatiempo.Alnalustedesmuchomásproductivositieneobjetosquemanejenlascaracterísticasdebajonivel.Despuésdetodo,elprincipalobjetivodelaPOOesesconderlosdetallesparaqueustedpueda«pintarconunabrochamásgorda».Sinembargo,debidoalaltonivelquelaPOOintentatener,hayalgunosaspectosfundamentalesdeCquenosepuedenobviar,ydeesotrataelsiguientecapítulo.2.9.EjerciciosLassolucionesalosejerciciossepuedenencontrareneldocumentoelectróni-cotitulado«TheThinkinginC++AnnotatedSolutionGuide»,disponibleporpocodineroenwww.BruceEckel.com.1.ModiqueHello.cppparaqueimprimasunombreyedad(otamañodepie,olaedaddesuperro,silegustamás).Compileyejecuteelprograma.2.UtilizandoStream2.cppyNumconv.cppcomoguías,creeunprogramaquelepidaelradiodeuncírculoylemuestreeláreadelmismo.Puedeusareloperador*paraelevarelradioalcuadrado.Nointenteimprimirelvalorenoctaloenhexadecimal(sólofuncionacontiposenteros).3.Creeunprogramaqueabrauncheroycuentelaspalabras(separadasporespaciosenblanco)quecontiene.4.Creeunprogramaquecuenteelnúmerodeocurrenciasdeunapalabraenconcretoenunchero(useeloperador==delaclasestringparaencontrarlapalabra)5.CambieFillvector.cppparaqueimprimalaslíneasalrevés(delaúltimaalaprimera).6.CambieFillvector.cppparaqueconcatenetodosloselementosdelaclasevectorenunúnicostringantesdeimprimirlo,peronoañadanumeracióndelíneas7.Muestreuncherolíneaalínea,esperandoqueelusuariopulseEnterdespuésdecadalínea.8.Creeunloa;&#xt000;vectoreintroduzcaenél25númerosenpuntootanteusandounbuclefor.Muestreelvector.
6Siestáespecialmenteinteresadoenvertodaslascosasquesepuedenhacerconloscompo-nentesdelaLibreríaEstándar,veaelVolumen2deestelibroenwww.BruceEckel.comytambiénenwww.dinkumware.com
60
i
i
“Volumen1”—2012/1/12—13:52—page61—#99i
i
i
i
i
i
2.9.Ejercicios
9.Creetresobjetosloa;&#xt000;vectoryrellenelosdosprimeroscomoenelejer-cicioanterior.Escribaunbucleforquesumeloselementoscorrespondientesylosañadaaltercervector.Muestrelostresvectores.10.Creeunloa;&#xt000;vectoreintroduzca25númerosenélcomoenelejercicioanterior.Elevecadanúmeroalcuadradoypongasuresultadoenlamismaposicióndelvector.Muestreelvectorantesydespuésdelamultiplicación.
61
i
i
“Volumen1”—2012/1/12—13:52—page62—#100i
i
i
i
i
i
i
i
“Volumen1”—2012/1/12—13:52—page63—#101i
i
i
i
i
i
3:CenC++ComoC++estábasadoenC,deberíaestarfamiliarizadoconlasintaxisdeCparapoderprogramarenC++,delmismomodoquede-beríatenerunauidezrazonableenálgebraparapoderhacercálcu-los.SinuncaanteshavistoC,estecapítuloledaráunabuenabasesobreelestilodeCusadoenC++.SiestáfamiliarizadoconelestilodeCdescritoenlaprimeraedicióndeKernighan&Ritchie(tambiénllamadoK&R)encontraráalgunascaracterísticasnuevasodiferentestantoenC++comoenelestándarC.SiestáfamiliarizadoconelestándarCdeberíaecharunvistazoalcapítuloenbuscadelascaracterísticasparticularesdeC++.NotequehayalgunascaracterísticasfundamentalesdeC++queseintroducenaquí,quesonideasbásicasparecidasacaracterísticasdeCoamenudomodicacionesenelmodoenqueChacelascosas.LascaracterísticasmássosticadasdeC++seexplicaránencapítulosposterioresEstecapítulotrataporencimalasconstruccionesdeCeintroducealgunascons-truccionesbásicasdeC++,suponiendoquetienealgunaexperienciaprogramandoenotrolenguaje.EnelCD-ROMqueacompañaaestelibrohayunaintroducciónmássuaveaC,tituladaThinkinginC:FoundationsforJava&C++deChuckAlison(publicadaporMidView,Inc.ydisponibletambiénenwww.MindView.net).SetratadeunseminarioenCD-ROMcuyoobjetivoesguiarlecuidadosamenteatravésdelosfundamentosdellenguajeC.Seconcentraenelconceptosnecesariosparapermi-tirlepasarseaC++oaJava,enlugardeintentarconvertirleenunexpertoentodoslososcurosrecovecosdeC(unadelasrazonesparausarunlenguajedealtonivelcomoC++oJavaesprecisamenteevitarmuchosdeestosrecovecos).Tambiéncon-tieneejerciciosysolucionesguiadas.TengapresentequeestecapítulovadespuésdelCDThinkinginC,elCDnoreemplazaaestecapítulo,sinoquedeberíatomarsecomounapreparaciónparaestecapítuloyparaellibro.3.1.CreacióndefuncionesEnelantiguoC(previoalestándar),sepodíainvocarunafunciónconcualquiernúmeroytipodeargumentossinqueelcompiladorsequejase.Todoparecíairbienhastaqueejecutabaselprograma.Elprogramaacababaconresultadosmisteriosos(opeor,elprogramafallaba)sinningunapistadelmotivo.Lafaltadeayudaacercadelpasodeargumentosylosenigmáticosbugsqueresultabanes,probablemente,lacausadequeCseconsiderase«unlenguajeensambladordealtonivel».Losprogra-madoresdepre-EstándarCsimplementeseadaptaron.CyC++Estándarusanunacaracterísticallamadaprototipadodefunciones.Cones-taherramientasehandedescribirlostiposdeargumentosaldeclararydeniruna
63
i
i
“Volumen1”—2012/1/12—13:52—page64—#102i
i
i
i
i
i
Capítulo3.CenC++
función.Estadescripciónesel«prototipo».Cuandolafunciónesllamada,elcompi-ladorusaelprototipoparaasegurarquelosargumentospasadossonlosapropiados,yqueelvalorretornadoestratadocorrectamente.Sielprogramadorcometeunerroralllamaralafunción,elcompiladordetectaelerror.Esencialmente,aprendiósobreprototipadodefunciones(sinllamarlasdeesemo-do)enelcapítuloprevio,yaquelaformadedeclararlasenC++requieredeunpro-totipadoapropiado.Enunprototipodefunción,lalistadeargumentoscontienelostiposdeargumentosquesedebenpasaralafuncióny(opcionalmenteparalade-claración),identicadoresparalosargumentos.Elordenytipodelosargumentosdebecoincidirenladeclaración,deniciónyllamadaalafunción.Acontinuaciónsemuestraunejemplodeunprototipodefunciónenunadeclaración:
inttranslate(floatx,floaty,floatz);
Nosepuedeusarlamismasintaxisparadeclararlosargumentosenelprototipodeunafunciónqueenlasdenicionesordinariasdevariables.Estosignicaquenosepuedeescribir:floatx,y,z.Sedebeindicareltipodecadaargumento.Enunadeclaracióndefunción,losiguientetambiénescorrecto:
inttranslate(float,float,float);
Yaqueelcompiladornohacemásquechequearlostiposcuandoseinvocalafun-ción,losidenticadoresseincluyensolamenteparamejorarlaclaridaddelcódigocuandoalguienloestáleyendo.Enladenicióndelafunción,losnombressonnecesariosyaquelosargumentossonreferenciadosdentrodelafunción:
inttranslate(floatx,floaty,floatz){
x=y=z;
//...
}
EstareglasóloseaplicaaC.EnC++,unargumentopuedenotenernombradoenlalistadeargumentosdeladenicióndelafunción.Comonotienenombre,nosepuedeutilizarenelcuerpodelafunción,porsupuesto.Losargumentossinnombresepermitenparadaralprogramadorunamanerade«reservarespacioenlalistadeargumentos».Decualquiermodo,lapersonaquecrealafunciónaúnasídebella-maralafunciónconlosparámetrosapropiados.Sinembargo,lapersonaquecrealafunciónpuedeutilizarelargumentoenelfuturosinforzarunamodicaciónenelcódigoquellamaalafunción.Estaopcióndeignorarunargumentoenlalistatam-biénesposiblesiseindicaelnombre,perosiempreapareceríaunmolestomensajedeadvertencia,informandoqueelvalornoseutiliza,cadavezquesecompilalafunción.Laadvertenciadesaparecesisequitaelnombredelargumento.CyC++tienenotrasdosmanerasdedeclararunalistadeargumentos.Sisetie-neunalistadeargumentosvacía,sepuededeclararestacomofunc()enC++,loqueindicaalcompiladorquehayexactamenteceroargumentos.HayquetenerencuentaqueestosólosignicaunalistadeargumentosvacíaenC++.EnCsignica«unnúmeroindeterminadodeargumentos»(loqueesun«agujero»enCyaquedeshabilitalacomprobacióndetiposenesecaso).Enambos,CyC++,ladeclara-ciónfunc(void);signicaunalistadeargumentosvacía.Lapalabraclavevoidsignica«nada»enestecaso(tambiénpuedesignicar«sintipo»enelcasodelos
64
i
i
“Volumen1”—2012/1/12—13:52—page66—#104i
i
i
i
i
i
Capítulo3.CenC++
}///:~
Encfunc(),elprimerifquecompruebaquelacondiciónseatruesaledelafunciónconlasentenciareturn.Fíjesequeladeclaracióndelafunciónnoesnecesariapuestoqueladeniciónapareceantesdeserutilizadaenmain(),demodoqueelcompiladorsabedesuexistenciadesdedichadenición.3.1.2.UsodefuncionesdelibreríasCTodaslasfuncionesenlalibreríalocaldefuncionesdeCestándisponiblescuan-doseprogramaenC++.Sedeberíabuscarbienenlalibreríadefuncionesantesdedenirunapropia-haymuchasprobabilidadesdequealguienhayaresueltoelpro-blemaantes,yprobablementehayadedicadomástiempopensandoydepurando.Unaadvertencia,delmismomodo:muchoscompiladoresincluyenmuchasfun-cionesextraquehacenlavidamuchomasfácilyresultantentadoras,peronosonpartedelaLibreríaCEstándar.Siestásegurodequejamásdesearáportarlaaplica-ciónaotraplataforma(¿yquiénestásegurodeeso?),adelante-utiliceesasfuncionesyhagasuvidamásfácil.Sideseaquelaaplicaciónpuedaserportada,deberíaceñirseúnicamentealusodefuncionesdelaLibreríaEstándar.Sideberealizaractividadesespecícasdelaplataforma,deberíaintentaraislarestecódigodetalmodoquepue-dacambiarsefácilmentealmigrarloaotraplataforma.EnC++,lasactividadesdeunaplataformaespecícaamenudoseencapsulanenunaclase,queeslasoluciónideal.Lafórmulaparausarunalibreríadefuncioneseslasiguiente:primero,encontrarlafunciónenlareferenciadeprogramación(muchasreferenciasdeprogramaciónordenanlasfuncionesporcategoríaademásdealfabéticamente).Ladescripcióndelafuncióndeberíaincluirunasecciónquedemuestrelasintaxisdelcódigo.Lapartesuperiordeestaseccióntienealmenosunalínea#include,mostrandoelcheroprincipalquecontieneelprototipodefunción.Debecopiareste#includeensucheroparaquelafunciónestécorrectamentedeclarada.Ahorapuedellamarlafuncióndelamismamaneraqueapareceenlaseccióndesintaxis.Sicometeunerror,elcompiladorlodescubrirácomparandolallamadaalafunciónconelprototipodelacabeceraeinformarádedichoerror.ElenlazadorbuscaenlaLibreríaEstándarpordefecto,demodoqueloúnicoquehayquehaceres:incluirelcherodecabecerayllamaralafunción.3.1.3.CreacióndelibreríaspropiasPuedereunirfuncionespropiasjuntasenunalibrería.LamayoríadepaquetesdeprogramaciónvienenconunFIXME:bibliotecarioquemanejagruposdemódulosobjeto.CadaFIXME:bibliotecariotienesuspropioscomandos,perolaideageneraleslasiguiente:sisedeseacrearunalibrería,sedebehaceruncherocabeceraquecontengaprototiposdetodaslasfuncionesdelalibrería.Hayqueubicarestecherodecabeceraenalgunapartedelarutadebúsquedadelpreprocesador,yaseaeneldirectoriolocal(demodoquesepodráencontrarmediante#include"header")obieneneldirectorioinclude(porloquesepodráencontrarmediante#inc-lude&#xhead;r00;).LuegosehandejuntartodoslosmódulosobjetoypasarlosalFIXME:bibliotecariojuntoconunnombreparalalibreríareciénconstruida(lama-yoríadelosbibliotecariosrequierenunaextensióncomún,comoporejemplo.libo.a).Sehadeubicarlalibreríacompletadonderesidantodaslasdemás,dema-
66
i
i
“Volumen1”—2012/1/12—13:52—page67—#105i
i
i
i
i
i
3.2.Controldeujo
neraqueelenlazadorsabrábuscaresasfuncionesendichalibreríaalserinvocadas.Puedenencontrartodoslosdetallesensudocumentaciónparticular,yaquepuedenvariardeunsistemaaotro.3.2.ControldeujoEstaseccióncubrelassentenciasdecontroldeujoenC++.DebefamiliarizarseconestassentenciasantesdequepuedaleeroescribircódigoCoC++.C++usatodaslassentenciasdecontroldeejecucióndeC.Estoincluyeif-else,do-while,for,yunasentenciadeselecciónllamadaswitch.C++tambiénadmiteelinfamegoto,elcualseráevitadoenestelibro.3.2.1.VerdaderoyfalsoTodaslassentenciascondicionalesutilizanlaveracidadolafalsedaddeunaex-presióncondicionalparadeterminarelcaminodeejecución.Unejemplodeexpre-sióncondicionalesA==B.Estoutilizaeloperadorcondicional==parasabersilavariableAesequivalentealavariableB.Laexpresiónproduceunbooleanotrueofalse(estassonpalabrasreservadassóloenC++;enCunaexpresiónesverdade-ra(true)siseevalúaconunvalordiferentedecero).Otrosoperadorescondicionalesson�,,&#x]TJ/;ཇ ; .96;& T; 5.;ॷ ;� Td;&#x [00;=,etc.Lassentenciascondicionalsetrataránafondomásadelanteenestecapítulo.3.2.2.if-elseLasentenciaif-elsepuedeexistirdedosformas:conosinelelse.Lasdosformasson:
if(óexpresin)
sentencia
ó
if(óexpresin)
sentencia
else
sentencia
La«expresión»seevalúacomotrueofalse.La«sentencia»puedeserunasimpleacabadaenunpuntoycoma,obienunacompuesta,loquenoesmásqueungrupodesentenciassimplesencerradasentrellaves.Siemprequeseutilizalapalabra«sentencia»,implicaquelasentenciaessimpleocompuesta.Tengaencuentaquedichasentenciapuedeserinclusootroif,demodoquesepuedenanidar.
//:C03:Ifthen.cpp
//Demonstrationofifandif-elseconditionals
#include&#xiost;&#xream;
usingnamespacestd;
intmain(){
67
i
i
“Volumen1”—2012/1/12—13:52—page68—#106i
i
i
i
i
i
Capítulo3.CenC++
inti;
cout"typeanumberand'Enter'"endl;
cin��i;
if(i�5)
cout"It'sgreaterthan5"endl;
else
if(i5)
cout"It'slessthan5"endl;
else
cout"It'sequalto5"endl;
cout"typeanumberand'Enter'"endl;
cin��i;
if(i10)
if(i�5)//"if"isjustanotherstatement
cout"5i10"endl;
else
cout"i5"endl;
else//Matches"if(i10)"
cout"i&#x-600;=10"endl;
}///:~
Porconvenioseindentaelcuerpodeunasentenciadecontroldeujo,demodoqueellectorpuededeterminarfácilmentedondecomienzaydóndeacaba1.3.2.3.whileEnlosbuclesdecontrolwhile,do-while,yfor,unasentenciaserepitehastaquelaexpresióndecontrolseafalse.Laestructuradeunbuclewhilees:
while(óexpresin)sentencia
Laexpresiónseevalúaunavezalcomienzodelbucleycadavezantesdecadaiteracióndelasentencia.EsteejemplosemantieneenelcuerpodelbuclewhilehastaqueintroduzcaelnúmerosecretoopresioneControl-C.
//:C03:Guess.cpp
//Guessanumber(demonstrates"while")
#include&#xiost;&#xream;
usingnamespacestd;
intmain(){
intsecret=15;
intguess=0;
//"!="isthe"not-equal"conditional:
while(guess!=secret){//Compoundstatement
cout"guessthenumber:";
cin��guess;
1Fíjeseenquetodaslasconvencionesparecenacabarestandodeacuerdoenquehayquehaceralgúntipodeindentación.Lapeleaentrelosestilosdeformateodecódigonotienen.EnelApéndiceAseexplicaelestilodecodicaciónqueseusaenestelibro.
68
i
i
“Volumen1”—2012/1/12—13:52—page69—#107i
i
i
i
i
i
3.2.Controldeujo
}
cout"Youguessedit!"endl;
}///:~
Laexpresióncondicionaldelwhilenoestárestringidaaunasimplepruebaco-moenelejemploanterior;puedesertancomplicadacomosedeseesiempreycuandoseproduzcaunresultadotrueofalse.Tambiénpuedeencontrarcódigoenelqueelbuclenotienecuerpo,sólounsimplepuntoycoma:
while(/*hacermuchascosas*/)
;
Enestoscasos,elprogramadorhaescritolaexpresióncondicionalnosólopararealizarlaevaluación,sinotambiénparahacereltrabajo.3.2.4.do-whileElaspectodedo-whilees
do
sentencia
while(óexpresin);
Eldo-whileesdiferentedelwhileyaquelasentenciasiempreseejecutaalmenosunavez,aúnsilaexpresiónresultafalselaprimeravez.Enunwhilenormal,silacondiciónesfalsalaprimeravez,lasentencianoseejecutanunca.Siseutilizaundo-whileenGuess.cpp,lavariableguessnonecesitaríaunvalorcticioinicial,yaqueseinicializaporlasentenciacinantesdequelavariableseaevaluada:
//:C03:Guess2.cpp
//Theguessprogramusingdo-while
#include&#xiost;&#xream;
usingnamespacestd;
intmain(){
intsecret=15;
intguess;//Noinitializationneededhere
do{
cout"guessthenumber:";
cin��guess;//Initializationhappens
}while(guess!=secret);
cout"Yougotit!"endl;
}///:~
Poralgunarazón,lamayoríadelosprogramadorestiendenaevitareldo-whileyselimitanatrabajarconwhile.
69
i
i
“Volumen1”—2012/1/12—13:52—page74—#112i
i
i
i
i
i
Capítulo3.CenC++
3.2.9.RecursividadLarecursividadesunatécnicadeprogramacióninteresanteyavecesútil,endondesellamaalafuncióndesdeelcuerpodelapropiafunción.Porsupuesto,siesoestodoloquehace,seestaríallamandoalafunciónhastaqueseacabaselamemoriadeejecución,demodoquedebeexistirunamanerade«escaparse»delallamadarecursiva.Enelsiguienteejemplo,esta«escapada»seconsiguesimplementeindicandoquelarecursiónsólocontinuaráhastaquecatexcedaZ:2
//:C03:CatsInHats.cpp
//Simpledemonstrationofrecursion
#include&#xiost;&#xream;
usingnamespacestd;
voidremoveHat(charcat){
for(charc='A';ccat;c++)
cout"";
if(cat'Z'){
cout"cat"catendl;
removeHat(cat+1);//Recursivecall
}else
cout"VOOM!!!"endl;
}
intmain(){
removeHat('A');
}///:~
EnremoveHat(),sepuedeverquemientrascatseamenorqueZ,removeH-at()sellamaráasímisma,efectuandoasílarecursividad.CadavezquesellamaremoveHat(),suargumentocreceenunaunidadmásqueelcatactualdemodoqueelargumentocontinúaaumentando.Larecursividadamenudoseutilizacuandoseevalúaalgúntipodeproblemaarbitrariamentecomplejo,yaquenoserestringelasoluciónaningúntamañopar-ticular-lafunciónpuedesimplementeefectuarlarecursividadhastaquesehayaalcanzadoelnaldelproblema.3.3.IntroducciónalosoperadoresSepuedenverlosoperadorescomountipoespecialdefunción(aprenderáqueenC++lasobrecargadeoperadoreslostrataprecisamentedeesaforma).Unoperadorrecibeunoomásargumentosyproduceunnuevovalor.Losargumentossepasandeunamaneradiferentequeenlasllamadasafuncionesnormales,peroelefectoeselmismo.Porsuexperienciapreviaenprogramación,debeestarrazonablementecómodoconlosoperadoresquesehanutilizados.Losconceptosdeadición(+),substracciónyrestaunaria(-),multiplicación(*),división(/),yasignación(=)tienentodoselmismosignicadoencualquierlenguajedeprogramación.Elgrupocompletodeoperadoresseenumeramásadelanteenestecapítulo.
2GraciasaKrisC.Matsonporproponeresteejercicio.
74
i
i
“Volumen1”—2012/1/12—13:52—page75—#113i
i
i
i
i
i
3.3.Introducciónalosoperadores
3.3.1.PrecedenciaLaprecedenciadeoperadoresdeneelordenenelqueseevalúaunaexpresiónconvariosoperadoresdiferentes.CyC++tienenreglasespecícasparadeterminarelordendeevaluación.Lomásfácilderecordaresquelamultiplicaciónyladivisiónseejecutanantesquelasumaylaresta.Luego,siunaexpresiónnoestransparentealprogramadorquelaescribe,probablementetampocoloseráparanadiequeleaelcódigo,demodoquesedebenusarparéntesisparahacerexplícitoelordendelaevaluación.Porejemplo:
A=X+Y-2/2+Z;
Tieneunsignicadomuydistintodelamismaexpresiónperoconuncongura-cióndeparéntesisparticular:
A=X+(Y-2)/(2+Z);
(IntenteevaluarelresultadoconX=1,Y=2,yZ=3.)3.3.2.AutoincrementoydecrementoC,yportantoC++,estállenodeatajos.Losatajospuedenhacerelcódigomuchomasfácildeescribir,yavecesmásdifícildeleer.QuizáslosdiseñadoresdellenguajeCpensaronqueseríamásfácilentenderuntrozodecódigocomplicadosilosojosnotienenqueleerunalargalíneadeletras.Losoperadoresdeauto-incrementoyauto-decrementosondelosmejoresatajos.Seutilizanamenudoparamodicarlasvariablesquecontrolanelnúmerodevecesqueseejecutaunbucle.Eloperadordeauto-decrementoes--quesignica«decrementardeaunauni-dad».Eloperadordeauto-incrementoes++quesignica«incrementardeaunaunidad».Siesunentero,porejemplo,laexpresión++Aesequivalentea(A=A+1).Losoperadoresdeauto-incrementoyauto-decrementoproducenelvalordelavariablecomoresultado.Sieloperadorapareceantesdelavariable(p.ej,++A),laoperaciónseejecutaprimeroydespuésseproduceelvalorresultante.Sieloperadorapareceacontinuacióndelavariable(p.ej,A++),primeroseproduceelvaloractual,yluegoserealizalaoperación.Porejemplo:
//:C03:AutoIncrement.cpp
//Showsuseofauto-increment
//andauto-decrementoperators.
#include&#xiost;&#xream;
usingnamespacestd;
intmain(){
inti=0;
intj=0;
cout++iendl;//Pre-increment
coutj++endl;//Post-increment
cout--iendl;//Pre-decrement
coutj--endl;//Postdecrement
}///:~
75
i
i
“Volumen1”—2012/1/12—13:52—page78—#116i
i
i
i
i
i
Capítulo3.CenC++
3.4.3.EspecicadoresLosespecicadoresmodicanelsignicadodelostipospredenidosbásicosylosexpandenaunconjuntomásgrande.Haycuatroespecicadores:long,short,signedyunsigned.longyshortmodicanlosvaloresmáximosymínimosqueuntipodedatospuedealmacenar.Unintplanodebeteneralmenoseltamañodeunshort.Laje-rarquíadetamañosparatiposenteroses:shortint,int,longint.Todospuedenserdelmismotamaño,siempreycuandosatisfaganlosrequisitosdemínimo/máximo.Enunamaquinaconunapalabrade64bits,pordefecto,todoslostiposdedatospodríanserde64bits.Lajerarquíadetamañoparalosnúmerosencomaotantees:oat,doubleylongdouble.«longoat»noesuntipoválido.Nohaynúmerosencomaotantesdetamañoshort.Losespecicadoressignedyunsignedindicanalcompiladorcómoutilizarelbitdelsignoconlostiposenterosyloscaracteres(losnúmerosdecomaotantesiemprecontienenunsigno).Unnúmerounsignednoguardaelvalordelsignoyporesotieneunbitextradisponible,demodoquepuedeguardareldobledenúmerospositivosquepuedenguardarseenunnúmerosigned.signedsesuponepordefectoysóloesnecesarioconchar,charpuedeseronopordefectounsigned.Especicandosignedchar,seestáforzandoelusodelbitdelsigno.Elsiguienteejemplomuestraeltamañodelostiposdedatosenbytesutilizandoeloperadorsizeof,descriptomásadelanteenesecapítulo:
//:C03:Specify.cpp
//Demonstratestheuseofspecifiers
#include&#xiost;&#xream;
usingnamespacestd;
intmain(){
charc;
unsignedcharcu;
inti;
unsignedintiu;
shortintis;
shortiis;//Sameasshortint
unsignedshortintisu;
unsignedshortiisu;
longintil;
longiil;//Sameaslongint
unsignedlongintilu;
unsignedlongiilu;
floatf;
doubled;
longdoubleld;
cout
"\nchar="sizeof(c)
"\nunsignedchar="sizeof(cu)
"\nint="sizeof(i)
"\nunsignedint="sizeof(iu)
"\nshort="sizeof(is)
"\nunsignedshort="sizeof(isu)
"\nlong="sizeof(il)
"\nunsignedlong="sizeof(ilu)
78
i
i
“Volumen1”—2012/1/12—13:52—page83—#121i
i
i
i
i
i
3.4.Introducciónalostiposdedatos
Porsupuesto,inicialmentexes47.Cuandosellamaf(),secreaunespaciotem-poralparaalojarlavariableadurantelaejecucióndelafunción,yelvalordexsecopiaaa,elcualesvericadomostrándoloporpantalla.Sepuedecambiarelvalordeaydemostrarquehacambiado.Perocuandof()termina,elespaciotemporalquesehabíacreadoparaadesaparece,ysepuedeobservarquelaúnicaconexiónqueexistíaentreayxocurriócuandoelvalordexsecopióena.Cuandoestádentrodef(),xeselobjetoexterno(miterminología),ycambiarelvalordelavariablelocalnoafectaalobjetoexterno,locualesbastantelógico,puestoquesondosubicacionesseparadasenlamemoria.Pero¿ysiquieremodicarelobjetoexterno?Aquíesdondelospunterosentranenacción.Enciertosentido,unpunteroesunaliasdeotravariable.Demodoquesiaunafunciónselepasaunpunteroenlugardeunvalorordinario,seestápasandodehechounaliasdelobjetoexterno,dandolaposibilidadalafuncióndequepuedamodicarelobjetoexterno,talcomosigue:
//:C03:PassAddress.cpp
#include&#xiost;&#xream;
usingnamespacestd;
voidf(int*p){
cout"p="pendl;
cout"*p="*pendl;
*p=5;
cout"p="pendl;
}
intmain(){
intx=47;
cout"x="xendl;
cout"&x="&xendl;
f(&x);
cout"x="xendl;
}///:~
Ahoraf()tomaelpunterocomounargumentoydereferenciaelpunteroduran-telaasignación,loquemodicaelobjetoexternox.Lasalidaes:
x=47
&x=0065FE00
p=0065FE00
*p=47
p=0065FE00
x=5
Tengaencuentaqueelvalorcontenidoenpeselmismoqueladireccióndex-elpunteropdehechoapuntaax.Siestonoessucientementeconvincente,cuandopesdereferenciadoparaasignarleelvalor5,sevequeelvalordexcambiaa5también.Deesemodo,pasandounpunteroaunafunciónlepermitiráaesafunciónmo-dicarelobjetoexterno.Severánmuchosotrosusosdelospunterosmásadelante,peropodríadecirsequeésteeselmásbásicoyposiblementeelmáscomún.
83
i
i
“Volumen1”—2012/1/12—13:52—page84—#122i
i
i
i
i
i
Capítulo3.CenC++
3.4.6.IntroducciónalasreferenciasdeC++LospunterosfuncionanmásomenosigualenCyenC++,peroC++añadeunmodoadicionaldepasarunadirecciónaunafunción.Setratadelpaso-por-referenciayexisteenotrosmuchoslenguajes,demodoquenoesunainvencióndeC++.Laprimeraimpresiónquedanlasreferenciasesquenosonnecesarias,quesepuedenescribircualquierprogramasinreferencias.Engeneral,esoesverdad,conlaexcepcióndeunospocoscasosimportantesquesetrataránmásadelanteenellibro,perolaideabásicaeslamismaquelademostraciónanteriorconelpuntero:sepue-depasarladireccióndeunargumentoutilizandounareferencia.Ladiferenciaentrereferenciasypunterosesqueinvocaraunafunciónquerecibereferenciasesmaslimpio,sintácticamente,quellamaraunafunciónquerecibepunteros(yesexac-tamenteesadiferenciasintácticalaquehacealasreferenciasesencialesenciertassituaciones).SiPassAddress.cppsemodicaparautilizarreferencias,sepuedeverladiferenciaenlallamadaalafunciónenmain():
//:C03:PassReference.cpp
#include&#xiost;&#xream;
usingnamespacestd;
voidf(int&r){
cout"r="rendl;
cout"&r="&rendl;
r=5;
cout"r="rendl;
}
intmain(){
intx=47;
cout"x="xendl;
cout"&x="&xendl;
f(x);//Lookslikepass-by-value,
//isactuallypassbyreference
cout"x="xendl;
}///:~
Enlalistadeargumentosdef(),enlugardeescribirint*parapasarunpuntero,seescribeint¶pasarunareferencia.Dentrodef(),sidicesimplementer(loqueproduciríaladirecciónsirfueseunpuntero)seobtieneelvalorenlavariablequerestáreferenciando.Siseasignaar,enrealidadseestáasignadoalavariablealaquequerreferencia.Dehecho,laúnicamaneradeobtenerladirecciónquecontieneresconeloperador&.Enmain(),sepuedeverelefectoclavedelasreferenciasenlasintaxisdelallamadaaf(),queessimplementef(x).Aunqueesopareceunpaso-por-valorordinario,elefectodelareferenciaesqueenrealidadtomaladirecciónylapasa,enlugardehacerunacopiadelvalor.Lasalidaes:
x=47
&x=0065FE00
r=47
&r=0065FE00
r=5
x=5
Demaneraquesepuedeverqueunpaso-por-referenciapermiteaunafunción
84
i
i
“Volumen1”—2012/1/12—13:52—page85—#123i
i
i
i
i
i
3.4.Introducciónalostiposdedatos
modicarelobjetoexterno,aligualquealpasarunpuntero(tambiénsepuedeob-servarquelareferenciaescondeelhechodequeseestápasandounadirección;estoseverámásadelanteenellibro).Graciasaestapequeñaintroducciónsepuedeasu-mirquelasreferenciassonsólounmodosintácticamentedistinto(avecesreferidocomo«azúcarsintáctico»)paraconseguirlomismoquelospunteros:permitiralasfuncionescambiarlosobjetosexternos.3.4.7.PunterosyReferenciascomomodicadoresHastaahora,sehanvistolostiposbásicosdedatoschar,int,oat,ydouble,juntoconlosespecicadoressigned,unsigned,short,ylong,quesepuedenutilizarconlostiposbásicosdedatosencasicualquiercombinación.Ahorahemosañadidolospunterosylasreferencias,quesonloortogonalalostiposbásicosdedatosylosespecicadores,demodoquelascombinacionesposiblesseacabandetriplicar:
//:C03:AllDefinitions.cpp
//Allpossiblecombinationsofbasicdatatypes,
//specifiers,pointersandreferences
#include&#xiost;&#xream;
usingnamespacestd;
voidf1(charc,inti,floatf,doubled);
voidf2(shortintsi,longintli,longdoubleld);
voidf3(unsignedcharuc,unsignedintui,
unsignedshortintusi,unsignedlongintuli);
voidf4(char*cp,int*ip,float*fp,double*dp);
voidf5(shortint*sip,longint*lip,
longdouble*ldp);
voidf6(unsignedchar*ucp,unsignedint*uip,
unsignedshortint*usip,
unsignedlongint*ulip);
voidf7(char&cr,int&ir,float&fr,double&dr);
voidf8(shortint&sir,longint&lir,
longdouble&ldr);
voidf9(unsignedchar&ucr,unsignedint&uir,
unsignedshortint&usir,
unsignedlongint&ulir);
intmain(){}///:~
Lospunterosylasreferenciasentranenjuegotambiéncuandosepasanobjetosdentroyfueradelasfunciones;aprenderásobreelloenuncapítuloposterior.Hayotrotipoquefuncionaconpunteros:void.Siseestablecequeunpunteroesunvoid*,signicaquecualquiertipodedirecciónsepuedeasignaraesepuntero(encambiositieneunint*,sólopuedeasignarladireccióndeunavariableintaesepuntero).Porejemplo:
//:C03:VoidPointer.cpp
intmain(){
void*vp;
charc;
inti;
floatf;
85
i
i
“Volumen1”—2012/1/12—13:52—page86—#124i
i
i
i
i
i
Capítulo3.CenC++
doubled;
//TheaddressofANYtypecanbe
//assignedtoavoidpointer:
vp=&c;
vp=&i;
vp=&f;
vp=&d;
}///:~
Unavezqueseasignaaunvoid*sepierdecualquierinformaciónsobreeltipodelavariables.Estosignicaqueantesdequesepuedautilizarelpuntero,sedebemoldearaltipocorrecto:
//:C03:CastFromVoidPointer.cpp
intmain(){
inti=99;
void*vp=&i;
//Can'tdereferenceavoidpointer:
//*vp=3;//Compile-timeerror
//Mustcastbacktointbeforedereferencing:
*((int*)vp)=3;
}///:~
Elmolde(int*)vptomaelvoid*yledicealcompiladorquelotratecomounint*,ydeesemodosepuededereferenciarcorrectamente.Puedeobservarqueestasintaxiseshorrible,yloes,peroespeorqueeso-elvoid*introduceunagujeroenelsistemadetiposdellenguaje.Esosignica,quepermite,oinclusopromueve,eltratamientodeuntipocomosifueraotrotipo.Enelejemploanterior,setrataunintcomounintmedianteelmoldeadodevpaint*,peronohaynadaqueindiquequenoselopuedemoldearachar*odouble*,loquemodicaríaunacantidaddiferentedeespacioquehasidoasignadaalint,loqueposiblementeprovocaráqueelprogramafalle..Engeneral,lospunterosvoiddeberíanserevitados,yutilizadosúnicamenteenrarasocasiones,quenosepodránconsiderarhastabastantemásadelanteenellibro.Nosepuedetenerunareferenciavoid,porrazonesqueseexplicaránenelcapí-tulo11.3.5.AlcanceLasreglasdeámbitosdicencuandoesválidaunavariable,dóndesecrea,ycuán-dosedestruye(esdecir,saledeámbito).Elámbitodeunavariableseextiendedesdeelpuntodondesedenehastalaprimerallavequeemparejaconlallavedeaperturaantesdequelavariablefuesedenida.Esoquieredecirqueunámbitosedeneporsujuegodellaves«máscercanas».Parailustrarlo:
//:C03:Scope.cpp
//Howvariablesarescoped
intmain(){
intscp1;
//scp1visiblehere
{
86
i
i
“Volumen1”—2012/1/12—13:52—page87—#125i
i
i
i
i
i
3.5.Alcance
//scp1stillvisiblehere
//.....
intscp2;
//scp2visiblehere
//.....
{
//scp1&scp2stillvisiblehere
//..
intscp3;
//scp1,scp2&scp3visiblehere
//...
}//scp3destroyedhere
//scp3notavailablehere
//scp1&scp2stillvisiblehere
//...
}//scp2destroyedhere
//scp3&scp2notavailablehere
//scp1stillvisiblehere
//..
}//scp1destroyedhere
///:~
Elejemploanteriormuestracuándolasvariablessonvisiblesycuandodejandeestardisponibles(esdecir,cuandosalendelámbito).Unavariablesepuedeutilizarsólocuandoseestádentrodesuámbito.Losámbitospuedenestaranidados,indica-dosporparejasdellavesdentrodeotrasparejasdellaves.Elanidadosignicaquesepuedeaccederaunavariableenunámbitoqueincluyeelámbitoenelqueseestá.Enelejemploanterior,lavariablescp1estádisponibledentrodetodoslosdemásámbitos,mientrasquescp3sóloestádisponibleenelámbitomásinterno.3.5.1.Denicióndevariables«alvuelo»Comosehamencionadoantesenestecapítulo,hayunadiferenciaimportan-teentreCyC++aldenirvariables.Amboslenguajesrequierenquelasvariablesesténdenidasantesdeutilizarse,peroC(ymuchosotroslenguajesproceduralestradicionales)fuerzanaquesedenantodaslasvariablesalprincipiodelbloque,demodoquecuandoelcompiladorcreaunbloquepuedecrearespacioparaesasvariables.CuandounoleecódigoC,normalmenteloprimeroqueencuentracuandoempie-zaunámbito,esunbloquededenicionesdevariables.Declarartodaslasvariablesalcomienzodeunbloquerequierequeelprogramadorescribadeunmodoparticu-lardebidoalosdetallesdeimplementacióndellenguaje.Lamayoríadelaspersonasnoconocentodaslasvariablesquevanautilizarantesdeescribirelcódigo,demodoquesiempreestánvolviendoalprincipiodelbloqueparainsertarnuevasvariables,locualresultapesadoycausaerrores.Normalmenteestasdenicionesdevariablesnosignicandemasiadoparaellector,ydehechotiendenaserconfusasporqueaparecenseparadasdelcontextoenelcualseutilizan.C++(peronoC)permitedenirvariablesencualquiersitiodentrodeunámbito,demodoquesepuededenirunavariablejustoantesdeusarla.Además,sepuedeinicializarlavariableenelmomentodeladenición,loqueprevieneciertotipodeerrores.Denirlasvariablesdeestemodohaceelcódigomásfácildeescribiryreduceloserroresqueprovocaestarforzadoavolveratrásyadelantedentrode
87
i
i
“Volumen1”—2012/1/12—13:52—page90—#128i
i
i
i
i
i
Capítulo3.CenC++
//:C03:Global2.cpp{O}
//Accessingexternalglobalvariables
externintglobe;
//(Thelinkerresolvesthereference)
voidfunc(){
globe=47;
}///:~
ElespacioparalavariableglobesecreamedianteladeniciónenGlobal.cpp,yesamismavariableesaccedidaporelcódigodeGlobal2.cpp.YaqueelcódigodeGlobal2.cppsecompilaseparadodelcódigodeGlobal.cpp,sedebeinformaralcompiladordequelavariableexisteenotrositiomedianteladeclaración
externintglobe;
Cuandoejecuteelprograma,observaráquelallamadafun()afectaefectiva-mentealaúnicainstanciaglobaldeglobe.EnGlobal.cpp,sepuedeverelcomentarioconunamarcaespecial(queesdi-señomío):
//{L}Global2
Esoindicaqueparacrearelprogramanal,elcheroobjetoconelnombreGlobal2debeestarenlazado(nohayextensiónyaquelosnombresdelasextensionesdeloscherosobjetodierendeunsistemaaotro).EnGlobal2.cpp,laprimeralí-neatieneotramarcaespecial{O},quesignica«Nointentarcrearunejecutabledeestechero,secompilaparaquepuedaenlazarseconotrochero».Elprogra-maExtractCode.cppenelVolumen2deestelibro(quesepuededescargardewww.BruceEckel.com)leeestasmarcasycreaelmakefileapropiadodemodoquetodosecompilacorrectamente(aprenderásobremakelesalnaldeestecapítulo).3.6.2.VariableslocalesLasvariableslocalessonlasqueseencuentrandentrodeunámbito;son«loca-les»aunafunción.Amenudoselasllamavariablesautomáticasporqueaparecenautomáticamentecuandoseentraenunámbitoydesaparecencuandoelámbitoseacaba.Lapalabrareservadaautoloenfatiza,perolasvariableslocalessonautopordefecto,demodoquenuncasenecesitarealmentedeclararalgocomoauto.VariablesregistroUnavariableregistroesuntipodevariablelocal.Lapalabrareservadaregis-terindicaalcompilador«Hazquelosaccesosaestavariableseanlomásrápidosposible».Aumentarlavelocidaddeaccesodependedelaimplementación,pero,talcomosugiereelnombre,amenudosehacesituandolavariableenunregistrodelmicroprocesador.Nohaygarantíaalgunadequelavariablepuedaserubicadaenunregistroytampocodequelavelocidaddeaccesoaumente.Esunaayudaparaelcompilador.Hayrestriccionesalahoradeutilizarvariablesregistro.Nosepuedeconsularocalcularladireccióndeunavariableregistro.Unavariableregistrosólosepuede
90
i
i
“Volumen1”—2012/1/12—13:52—page93—#131i
i
i
i
i
i
3.6.Especicarlaubicacióndelespaciodealmacenamiento
Cuandoelcompiladorencuentraladeclaraciónexternintisabequelade-niciónparaidebeexistirenalgúnsitiocomounavariableglobal.Cuandoelcom-piladoralcanzaladenicióndei,ningunaotradeclaraciónesvisible,demodoquesabequehaencontradolamismaideclaradaanteriormenteenelchero.Sisehu-bieradenidoicomostatic,estaríaindicandoalcompiladorqueisedeneglo-balmente(porextern),perotambiénquetieneelámbitodechero(porstatic),demodoqueelcompiladorgeneraráunerror.EnlazadoParacomprenderelcomportamientodelosprogramasCyC++,esnecesariosabersobreenlazado.Enunprogramaenejecución,unidenticadorserepresentaconespacioenmemoriaquealojaunavariableouncuerpodefuncióncompilada.Elenlazadodescribeesteespaciotalcomoloveelenlazador.Haydosformasdeenlazado:enlaceinternoyenlaceexterno.Enlaceinternosignicaqueelespaciosepidepararepresentarelidenticadorsó-lodurantelacompilacióndelchero.Otroscherospuedenutilizarelmismonom-bredeidenticadorconunenlaceinterno,oparaunavariableglobal,yelenlazadornoencontraríaconictos-sepideunespacioseparadoparacadaidenticador.ElenlaceinternoseespecicamediantelapalabrareservadastaticenCyC++.Enlaceexternosignicaquesepidesólounespaciopararepresentarelidenti-cadorparatodosloscherosqueseesténcompilando.Elespaciosepideunavez,yelenlazadordeberesolvertodaslasdemásreferenciasaesaubicación.Lasvariablesglobalesylosnombresdefuncióntienenenlaceexterno.Sonaccesiblesdesdeotroscherosdeclarándolasconlapalabrareservadaextern.Pordefecto,lasvariablesdenidasfueradetodaslasfunciones(conlaexcepcióndeconstenC++)ylasde-nicionesdelasfuncionesimplicanenlaceexterno.Sepuedenforzarespecícamenteatenerenlaceinternoutilizandostatic.Sepuedeestablecerexplícitamentequeunidenticadortieneenlaceexternodeniéndolocomoextern.Noesnecesariode-nirunavariableounafuncióncomoexternenC,peroavecesesnecesarioparaconstenC++.Lasvariablesautomáticas(locales)existensólotemporalmente,enlapila,mien-trasseestáejecutandounafunción.Elenlazadornoentiendedevariablesautomáti-cas,demodoquenotienenenlazado.3.6.5.ConstantesEnelantiguoC(pre-Estándar),sisedeseabacrearunaconstante,sedebíautilizarelpreprocesador:
#definePI3.14159
EncualquiersitioenelqueutilizasePI,elpreprocesadorlosubstituíaporelvalor3.14159(aúnsepuedeutilizarestemétodoenCyC++).Cuandoseutilizaelpreprocesadorparacrearconstantes,sucontrolquedafueradelámbitodelcompilador.NoexisteningunacomprobacióndetipoynosepuedeobtenerladireccióndePI(demodoquenosepuedepasarunpunteroounare-ferenciaaPI).PInopuedeserunavariabledeuntipodenidoporelusuario.ElsignicadodePIduradesdeelpuntoenqueesdenida,hastaelnaldelchero;elpreprocesadornoentiendedeámbitos.
93
i
i
“Volumen1”—2012/1/12—13:52—page94—#132i
i
i
i
i
i
Capítulo3.CenC++
C++introduceelconceptodeconstantesconnombrequeeslomismoqueva-riable,exceptoquesuvalornopuedecambiar.Elmodicadorconstleindicaalcompiladorqueelnombrerepresentaunaconstante.Cualquiertipodedatosprede-nidoodenidoporelusuario,puedeserdenidocomoconst.Sisedenealgocomoconstyluegoseintentamodicar,elcompiladorgeneraráunerror.Sedebeespecicareltipodeunconst,deestemodo:
constintx=10;
EnCyC++Estándar,sepuedeusarunaconstanteenunalistadeargumentos,inclusosielargumentoqueocupaesunpunteroounareferencia(p.e,sepuedeobtenerladireccióndeunaconstante).Lasconstantestienenámbito,aligualqueunavariableordinaria,demodoquesepuede«esconder»unaconstantedentrodeunafunciónyestarsegurodequeesenombrenoafectaráalrestodelprograma.consthasidotomadodeC++eincorporadoalCEstándarperounmodounpocodistinto.EnC,elcompiladortrataaconstdelmismomodoqueaunavariablequetuvieraasociadounaetiquetaquedice«Nomecambies».CuandosedeneunconstenC,elcompiladorpideespacioparaél,demodoquesisedenemásdeunconstconelmismonombreendoscherosdistintos(oseubicaladeniciónenuncherodecabeceras),elenlazadorgenerarámensajesdeerrorsobredelconicto.ElconceptodeconstenCesdiferentedesuutilizaciónenC++(enresumen,esmásbonitoenC++).ValoresconstantesEnC++,unaconstantedebetenersiempreunvalorinicial(EnC,esonoescierto).Losvaloresdelasconstantesparatipospredenidosseexpresanendecimal,octal,hexadecimal,onúmerosconpuntootante(desgraciadamente,noseconsideróquelosbinariosfuesenimportantes),ocomocaracteres.Afaltadecualquierotrapista,elcompiladorassumequeelvalordeunacons-tanteesunnúmerodecimal.Losnúmeros47,0y1101setratancomonúmerosdeci-males.Unvalorconstanteconunceroalprincipiosetratacomounnúmerooctal(base8).Losnúmerosconbase8puedencontenerúnicamentedígitosdel0al7;elcompi-ladorinterpretaotrosdígitoscomounerror.Unnúmerooctallegítimoes017(15enbase10).Unvalorconstantecon0xalprincipiosetratacomounnúmerohexadecimal(base16).Losnúmerosconbase16puedencontenerdígitosdel0al9yletrasdelaaalafoAaF.Unnúmerohexadecimallegítimoes0x1fe(510enbase10).Losnúmerosenpuntootantepuedencontenercomasdecimalesypotenciasexponenciales(representadasmediantee,loquesignica«10elevadoa»).Tantoelpuntodecimalcomolaesonopcionales.Siseasignaunaconstanteaunavariabledepuntootante,elcompiladortomaráelvalordelaconstanteylaconvertiráaunnúmeroenpuntootante(esteprocesoesunaformadeloqueseconocecomoconversiónimplícitadetipo).Detodosmodos,esunabuenaideaelusarelpuntodecimalounaepararecordarallectorqueestáutilizandounnúmeroenpuntootante;algunoscompiladoresinclusonecesitanestapista.Algunosvaloresválidosparaunaconstanteenpuntootanteson:1e4,1.0001,47.0,0.0y1.159e-77.Sepuedenañadirsujosparaforzareltipodenúmerodepun-tootante:foFfuerzaqueseaoat,Lolfuerzaqueseaunlongdouble;delo
94
i
i
“Volumen1”—2012/1/12—13:52—page97—#135i
i
i
i
i
i
3.7.Losoperadoresysuuso
Losrvaluesdetodaslasasignacionespuedenser,porsupuesto,muchomascom-plejos.IntroducciónalasmacrosdelpreprocesadorObserveelusodelamacroPRINT()paraahorrarlíneas(yerroresdesintaxis!).Lasmacrosdepreprocesadorsenombrantradicionalmentecontodassusletrasenmayúsculasparaqueseafácildistinguirlas-aprenderámásadelantequelasmacrospuedenserpeligrosas(ytambiénpuedensermuyútiles).Losargumentosdedelalistaentreparéntesisquesiguealnombredelamacrosonsustituidosentodoelcódigoquesiguealparéntesisdecierre.ElpreprocesadoreliminaelnombrePRINTysustituyeelcódigodondeseinvocalamacro,demodoqueelcompiladornopuedegenerarningúnmensajedeerroralutilizarelnombredelamacro,ynorealizaningunacomprobacióndesintaxissobrelosargumentos(estoloúltimopuedeserbenecioso,comosemuestraenlasmacrosdedepuraciónalnaldelcapítulo).3.7.3.OperadoresrelacionalesLosoperadoresrelacionalesestablecenunarelaciónentreelvalordelosoperan-dos.Producenunvalorbooleano(especicadoconlapalabrareservadaboolenC++)truesilarelaciónesverdadera,yfalsesilarelaciónesfalsa.Losoperadoresre-lacionalesson:menorque(),mayorque(&#x]TJ/;ཇ ; .96;& T; 5.;ॷ ;� Td;&#x [00;),menoroiguala(),mayoroiguala(&#x=]TJ;&#x/F47;&#x 9.9;ئ ;&#xTf 1;.95; 0 ;&#xTd [;=),equivalente(==),ydistinto(!=).SepuedenutilizarcontodoslostiposdedatospredenidosenCyC++.SepuedendardenicionesespecialesparatiposdenidosporelusuarioenC++(aprenderámássobreeltemaenelCapítulo12,quecubrelasobrecargadeoperadores).3.7.4.OperadoreslógicosLosoperadoreslógicosand(&&)yor(||)producentrueofalsebasándoseenlarelaciónlógicadesusargumentos.RecuerdequeenCyC++,unacondiciónesciertasitieneunvalordiferentedecero,yfalsasivalecero.Siseimprimeunbool,porlogeneralveráun1'paratruey0parafalse.Esteejemploutilizalosoperadoresrelacionalesylógicos:
//:C03:Boolean.cpp
//Relationalandlogicaloperators.
#include&#xiost;&#xream;
usingnamespacestd;
intmain(){
inti,j;
cout"Enteraninteger:";
cin��i;
cout"Enteranotherinteger:";
cin��j;
cout"i&#x-600;jis"(i&#x-600;j)endl;
cout"ijis"(ij)endl;
cout"i&#x-600;=jis"(i&#x-600;=j)endl;
cout"ijis"(ij)endl;
cout"i==jis"(i==j)endl;
cout"i!=jis"(i!=j)endl;
97
i
i
“Volumen1”—2012/1/12—13:52—page101—#139i
i
i
i
i
i
3.7.Losoperadoresysuuso
if(val&1)//Checkthelowbit
lowbit=1;
else
lowbit=0;
val��=1;//Rightshiftbyoneposition
//Rotatethelowbitontothetop:
val|=(lowbit7);
returnval;
}///:~
AlintentarutilizarestasfuncionesenBitwise.cpp,adviertaquelasdenicio-nes(ocuandomenoslasdeclaraciones)derol()yror()debenservistasporelcompiladorenBitwise.cppantesdequesepuedanutilizar.Lasfuncionesdetratamientodebitssonporlogeneralextremadamenteecien-tesyaquetraducendirectamentelassentenciasalenguajeensamblador.AvecesunasentenciadeCoC++generaráunaúnicalíneadecódigoensamblador.3.7.7.OperadoresunariosElnotnoeselúnicooperadordebitsquetomasólounargumento.Sucompañero,elnotlógico(!),tomaunvalortrueyproduceunvalorfalse.Elmenosunario(-)yelmásunario(+)sonlosmismosoperadoresquelosbinariosmenosymás;elcompiladordeducequeusoselepretendedarporelmodoenelqueseescribelaexpresión.Dehecho,lasentencia:
x=-a;
tieneunsignicadoobvio.Elcompiladorpuedededucir:
x=a*-b;
peroellectorsepuedeconfundir,demodoqueesmásseguroescribir:
x=a*(-b);
Elmenosunarioproduceelvalornegativo.Elmásunarioofrecesimetríaconelmenosunario,aunqueenrealidadnohacenada.Losoperadoresdeincrementoydecremento(++y--)secomentaronyaenestecapítulo.Sonlosúnicosoperadores,ademásdelosqueinvolucranasignación,quetienenefectoscolaterales.Estosoperadoresincrementanodecrementanlavariableenunaunidad,aunque«unidad»puedetenerdiferentessignicadosdependiendodeltipodedato-estoesespecialmenteimportanteenelcasodelospunteros.Losúltimosoperadoresunariossondirección-de(&),indirección(*y�-),losoperadoresdemoldeadoenCyC++,ynewydeleteenC++.Ladirección-deylaindirecciónseutilizanconlospunteros,descriptosenestecapítulo.Elmoldeadosedescribemasadelanteenestecapítulo,ynewydeleteseintroducenenelCapítulo4.
101
i
i
“Volumen1”—2012/1/12—13:52—page102—#140i
i
i
i
i
i
Capítulo3.CenC++
3.7.8.EloperadorternarioElif-elseternarioesinusualporquetienetresoperandos.Realmenteesunoperadorporqueproduceunvalor,alcontrariodelasentenciaordinariaif-else.Constadetresexpresiones:silaprimeraexpresión(seguidadeun?)seevalúacomocierto,sedevuelveelresultadodeevaluarlaexpresiónquesigueal?.Silaprimeraexpresiónesfalsa,seejecutalaterceraexpresión(quesiguea:)ysuresultadoseconvierteenelvalorproducidoporeloperador.Eloperadorcondicionalsepuedeusarporsusefectoscolateralesoporelvalorqueproduce.Acontinuación,unfragmentodecódigoquedemuestraambascosas:
a=--b?b:(b=-99);
Aquí,elcondicionalproduceelrvalue.Aaseleasignaelvalordebsielresultadodedecrementarbesdiferentedecero.Sibsequedaacero,aybsonambasasignadasa-99.bsiempresedecrementa,peroseasignaa-99sólosieldecrementoprovocaquebvalga0.Sepuedeutilizarunsentenciasimilarsinela=sóloporsusefectoscolaterales:
--b?b:(b=-99);
Aquílasegundabessuperua,yaquenoseutilizaelvalorproducidoporeloperador.Serequiereunaexpresiónentreel?y:.Enestecaso,laexpresiónpuedesersimplementeunaconstante,loqueharíaqueelcódigoseejecuteunpocomásrápido.3.7.9.EloperadorcomaLacomanoselimitaasepararnombresdevariablesendenicionesmúltiples,talescomo
inti,j,k;
Porsupuesto,tambiénseusaenlistasdeargumentosdefunciones.Detodosmo-dos,tambiénsepuedeutilizarcomounoperadorparasepararexpresiones-enestecasoproduceelvalordelaúltimaexpresión.Elrestodeexpresionesenlalistasepa-radaporcomasseevalúasóloporsusefectoscolaterales.Esteejemploincrementaunalistadevariablesyusalaúltimacomoelrvalue:
//:C03:CommaOperator.cpp
#include&#xiost;&#xream;
usingnamespacestd;
intmain(){
inta=0,b=1,c=2,d=3,e=4;
a=(b++,c++,d++,e++);
cout"a="aendl;
//Theparenthesesarecriticalhere.Without
//them,thestatementwillevaluateto:
(a=b++),c++,d++,e++;
cout"a="aendl;
}///:~
102
i
i
“Volumen1”—2012/1/12—13:52—page104—#142i
i
i
i
i
i
Capítulo3.CenC++
unsignedlonga=(unsignedlongint)b;
}///:~
Elmoldeadoespoderoso,peropuedecausardoloresdecabezaporqueenalgu-nassituacionesfuerzaalcompiladoratratardatoscomosifuesen(porejemplo)máslargosdeloquerealmenteson,demodoqueocuparámásespacioenmemoria;loquepuedeafectaraotrosdatos.Estoocurreamenudocuandosemoldeanpunteros,nocuandosehacenmoldessimplescomolosquehavistoanteriormente.C++tieneunasintaxisadicionalparamoldes,quesiguealasintaxisdellamadaafunciones.Estasintaxisponelosparéntesisalrededordelargumento,comoenunallamadaafunción,enlugardealosladosdeltipo:
//:C03:FunctionCallCast.cpp
intmain(){
floata=float(200);
//Thisisequivalentto:
floatb=(float)200;
}///:~
Porsupuesto,enelcasoanterior,enrealidadnosenecesitaríaunmolde;sim-plementesepuededecir200.fo200.0f(enefecto,esoestípicamenteloqueelcompiladorharáparalaexpresiónanterior).Losmoldesnormalmenteseutilizanconvariables,enlugardeconconstantes.3.7.12.LosmoldesexplícitosdeC++Losmoldessedebenutilizarconcuidado,porqueloqueestáhaciendoenreali-dadesdeciralcompilador«Olvidalacomprobacióndetipo-trátalocomosifuesedeesteotrotipo.»Estosignica,queestáintroduciendounagujeroenelsistemadetiposdeC++yevitandoqueelcompiladorinformedequeestáhaciendoalgoerró-neoconuntipo.Loqueespeor,elcompiladorlocreeimplícitamenteynorealizaningunaotracomprobaciónparabuscarerrores.Unavezhacomenzadoamoldear,estáexpuestoatodotipodeproblemas.Dehecho,cualquierprogramaqueutilicemuchosmoldessedeberevisarcondetenimiento,noimportacuantohayadadoporsentadoquesimplemente«debe»hacersedeestamanera.Engeneral,losmoldesdebenserpocosyaisladosparasolucionarproblemasespecícos.Unavezsehaentendidoestoysepresenteunprogramaconerrores,laprimeraimpresiónpuedequeseamirarlosmoldescomosifuesenlosculpables.Pero,¿cómoencontrarlosmoldesestiloC?Sonsimplementenombresdetiposentreparéntesis,ysiseempiezaabuscarestascosasdescubriráqueamenudoesdifícildistinguirlosdelrestodelcódigo.ElC++EstándarincluyeunasintaxisexplícitademoldequesepuedeutilizarparareemplazarcompletamentelosmoldesdelestiloantiguodeC(porsupuesto,losmoldesdeestiloCnosepuedenprohibirsinromperelcódigo,perolosescritoresdecompiladorespuedenadvertirfácilmenteacercadelosmoldesantiguos).Lasintaxisexplícitademoldesestápensadaparaqueseafácilencontrarlos,talcomosepuedeobservarporsusnombres:Losprimerostresmoldesexplícitossedescribiráncompletamenteenlassiguien-tessecciones,mientrasquelosúltimosseexplicarándespuésdequehayaaprendido
104
i
i
“Volumen1”—2012/1/12—13:52—page105—#143i
i
i
i
i
i
3.7.Losoperadoresysuuso
static_cast
Paramoldesquesecomportanbienorazonablementebien,incluyendocosasquesepodríanhacersinunmolde(comounaconversiónautomáticadetipo).
const_cast
Paramoldearconsty/ovolatile
reinterpret_cast
Paramoldearaunsignicadocompletamentediferente.Laclaveesquesenecesitarávolveramoldearaltipooriginalparapoderlousarconseguridad.Eltipoalquemoldeeseusatípicamentesóloparajugarunpocooalgúnotropropósitomisterioso.Ésteeselmáspeligrosodetodoslosmoldes.
dynamic_cast
Pararealizarundowncastingseguro(estemoldesedescribeenelCapítulo15).
Cuadro3.2:MoldesexplícitosdeC++másenelCapítulo15.static_castElstatic_castseutilizaparatodaslasconversionesqueestánbiendeni-das.Estoincluyeconversiones«seguras»queelcompiladorpermitiríasinutilizarunmolde,yconversionesmenossegurasqueestánsinembargobiendenidas.Lostiposdeconversionesquecubrestatic_castincluyenlasconversionestípicassinmolde,conversionesdeestrechamiento(pérdidadeinformación),forzarunaconver-sióndeunvoid*,conversionesdetipoimplícitas,ynavegaciónestáticadejerarquíasdeclases(yaquenosehanvistoaúnclasesniherencias,esteúltimoapartadoseposponehastaelCapítulo15):
//:C03:static_cast.cpp
voidfunc(int){}
intmain(){
inti=0x7fff;//Maxposvalue=32767
longl;
floatf;
//(1)Typicalcastlessconversions:
l=i;
f=i;
//Alsoworks:
l=static_castlong&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;(i);
f=static_castfloat&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;(i);
//(2)Narrowingconversions:
i=l;//Maylosedigits
i=f;//Mayloseinfo
//Says"Iknow,"eliminateswarnings:
i=static_castint&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;(l);
i=static_castint&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;(f);
charc=static_castchar&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;(i);
105
i
i
“Volumen1”—2012/1/12—13:52—page106—#144i
i
i
i
i
i
Capítulo3.CenC++
//(3)Forcingaconversionfromvoid*:
void*vp=&i;
//Oldwayproducesadangerousconversion:
float*fp=(float*)vp;
//Thenewwayisequallydangerous:
fp=static_castfloat*&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;(vp);
//(4)Implicittypeconversions,normally
//performedbythecompiler:
doubled=0.0;
intx=d;//Automatictypeconversion
x=static_castint&#x]TJ/;ྖ ;.96;d T; 5.;͹ ;� Td;&#x [00;(d);//Moreexplicit
func(d);//Automatictypeconversion
func(static_castint&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;(d));//Moreexplicit
}///:~
Enlasección(FIXME:xref:1),sepuedenvertiposdeconversionesqueeranusua-lesenC,conosinunmolde.Promoverunintalongooatnoesunproblemaporqueelúltimopuedealbergarsiemprecualquiervalorqueunintpuedacontener.Aunqueesinnecesario,sepuedeutilizarstatic_castpararemarcarestaspromociones.Semuestraen(2)comoseconviertealrevés.Aquí,sepuedeperderinforma-ciónporqueunintnoestan«ancho»comounlongounoat;noalojanúmerosdelmismotamaño.Decualquiermodo,estetipodeconversiónsellamaconversióndeestrechamiento.Elcompiladornoimpediráqueocurran,peronormalmenteda-ráunaadvertencia.Sepuedeeliminarestaadvertenciaeindicarquerealmentesepretendíaestoutilizandounmolde.Tomarelvalordeunvoid*noestápermitidoenC++amenosqueuseunmolde(alcontrariodeC),comosepuedeveren(3).Estoespeligrosoyrequierequelosprogramadoressepanloqueestánhaciendo.Elstatic_cast,almenos,esmasfácildelocalizarquelosmoldesantiguoscuandosetratadecazarfallos.Lasección(FIXME:xref:4)delprogramamuestralasconversionesdetipoimplí-citasquenormalmenteserealizandemaneraautomáticaporelcompilador.Sonautomáticasynorequierenmolde,peroelutilizarstatic_castacentúadichaac-ciónencasodequesequierareejarclaramentequéestáocurriendo,parapoderlocalizarlodespués.const_castSiquiereconvertirdeunconstaunno-constodeunvolatileaunno-v-olatile,seutilizaconst_cast.Eslaúnicaconversiónpermitidaconconst_c-ast;siestáinvolucradaalgunaconversiónadicionalsedebehacerutilizandounaexpresiónseparadaoseobtendráunerrorentiempodecompilación.
//:C03:const_cast.cpp
intmain(){
constinti=0;
int*j=(int*)&i;//Deprecatedform
j=const_castint*&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;(&i);//Preferred
//Can'tdosimultaneousadditionalcasting:
//!long*l=const_castlong*�(&i);//Error
volatileintk=0;
int*u=const_castint*&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;(&k);
106
i
i
“Volumen1”—2012/1/12—13:52—page107—#145i
i
i
i
i
i
3.7.Losoperadoresysuuso
}///:~
Sitomaladireccióndeunobjetoconst,produceunpunteroaconst,éstenosepuedeasignaraunpunteroquenoseaconstsinunmolde.Elmoldealestiloantiguolopuedehacer,peroelconst_casteselmásapropiadoenestecaso.Lomismoocurreconvolatile.reinterpret_castEsteeselmenossegurodelosmecanismosdemolde,yelmássusceptibledecrearfallos.Unreinterpret_castsuponequeunobjetoesunpatróndebitsquesepuedetratar(paraalgúnoscuropropósito)comosifuesedeuntipototalmentedistinto.EseeseljugueteodebitsabajonivelporelcualCesfamoso.Prácticamentesiemprenecesitaráhacerreinterpret_castparavolveraltipooriginal(odelocontrariotrataralavariablecomosutipooriginal)antesdehacernadamásconella.
//:C03:reinterpret_cast.cpp
#include&#xiost;&#xream;
usingnamespacestd;
constintsz=100;
structX{inta[sz];};
voidprint(X*x){
for(inti=0;isz;i++)
coutx&#x-600;-a[i]'';
coutendl"--------------------"endl;
}
intmain(){
Xx;
print(&x);
int*xp=reinterpret_castint*&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;(&x);
for(int*i=xp;ixp+sz;i++)
*i=0;
//Can'tusexpasanX*atthispoint
//unlessyoucastitback:
print(reinterpret_castX*�(xp));
//Inthisexample,youcanalsojustuse
//theoriginalidentifier:
print(&x);
}///:~
Enesteejemplo,structXcontieneunarraydeint,perocuandosecreaunoenlapilacomoenXx,losvaloresdecadaunodelosintstienenbasura(estosedemuestrautilizandolafunciónprint()paramostrarloscontenidosdestruct).Parainicializarlas,ladireccióndelXsetomaysemoldeaaunpunteroint,queesluegoiteradoatravésdelarrayparainicializarcadaintacero.Fíjesecomoellímitesuperiordeisecalcula«añadiendo»szaxp;elcompiladorsabequeloqueustedquiererealmentesonlasdireccionesdeszmayoresquexpyélrealizaelcálculoaritméticoporusted.FIXME(Comprobarloquediceestepárrafodeacuerdoconelcódigo)
107
i
i
“Volumen1”—2012/1/12—13:52—page111—#149i
i
i
i
i
i
3.8.Creacióndetiposcompuestos
UnacosaatenerencuentaeslatorpezadeusarStructure1(comosaltaalavista,esosóloserequiereenC,ynoenC++).EnC,nosepuedeponerStructure1cuandosedenenvariables,sedebeponerstructStructure1.AquíesdondetypedefsevuelveespecialmenteútilenC:
//:C03:SimpleStruct2.cpp
//Usingtypedefwithstruct
typedefstruct{
charc;
inti;
floatf;
doubled;
}Structure2;
intmain(){
Structure2s1,s2;
s1.c='a';
s1.i=1;
s1.f=3.14;
s1.d=0.00093;
s2.c='a';
s2.i=1;
s2.f=3.14;
s2.d=0.00093;
}///:~
Usandotypedefdeestemodo,sepuedesimular(enC;intentareliminarelt-ypedefparaC++)queStructure2esuntipopredenido,comointooat,cuandodenes1ys2(perosehadetenerencuentadequesólotieneinformación-carac-terísticas-ynoincluyecomportamiento,queesloqueseobtieneconobjetosrealesenC++).Observequeelstructsehadeclaradoalprincipio,porqueelobjetivoescreareltypedef.Sinembargo,hayvecesenlasqueseríanecesarioreferirseast-ructdurantesudenición.Enesoscasos,sepuederepetirelnombredelstructcomotalycomotypedef.
//:C03:SelfReferential.cpp
//Allowingastructtorefertoitself
typedefstructSelfReferential{
inti;
SelfReferential*sr;//Headspinningyet?
}SelfReferential;
intmain(){
SelfReferentialsr1,sr2;
sr1.sr=&sr2;
sr2.sr=&sr1;
sr1.i=47;
sr2.i=1024;
}///:~
Siloobservadetenidamente,puedeverquesr1ysr2apuntanelunoalotro,guardandocadaunounapartedelainformación.
111
i
i
“Volumen1”—2012/1/12—13:52—page117—#155i
i
i
i
i
i
3.8.Creacióndetiposcompuestos
(long)&a[i]endl;
}///:~
Cuandoseejecutaesteprograma,sevequecadaelementoestáseparadoporeltamañodeunintdelanterior.Estosignica,queestáncolocadosunoacontinuacióndelotro.PunterosyarraysElidenticadordeunvectoresdiferentedelosidenticadoresdelasvariablescomunes.Unidenticadordeunvectornoesunlvalue;noselepuedeasignarnada.EnrealidadesFIXME:ganchodentrodelasintaxisdecorchetes,ycuandoseusaelnombredeunvector,sinloscorchetes,loqueseobtieneesladireccióninicialdelvector:
//:C03:ArrayIdentifier.cpp
#include&#xiost;&#xream;
usingnamespacestd;
intmain(){
inta[10];
cout"a="aendl;
cout"&a[0]="&a[0]endl;
}///:~
Cuandoseejecutaesteprograma,sevequelasdosdirecciones(queseimprimenenhexadecimal,yaquenosemoldeaalong)sonlasmisma.Demodoqueunamaneradeverelidenticadordeunvectorescomounpunterodesólolecturaalprincipiodeéste.Yaunquenosepuedahacerqueelidenticadordelvectorapunteacualquierotrositio,sepuedecrearotropunteroyutilizarloparamoversedentrodelvector.Dehecho,lasintaxisdecorchetestambiénfuncionaconpunterosconvencionales:
//:C03:PointersAndBrackets.cpp
intmain(){
inta[10];
int*ip=a;
for(inti=0;i10;i++)
ip[i]=i*10;
}///:~
Elhechodequeelnombredeunvectorproduzcasudireccióndeinicioresultabastanteimportantecuandohayquepasarunvectoraunafunción.Sideclaraunvectorcomounargumentodeunafunción,loquerealmenteestádeclarandoesunpuntero.Demodoqueenelsiguienteejemplo,fun1()yfunc2()tienenlamismalistadeargumentos:
//:C03:ArrayArguments.cpp
#include&#xiost;&#xream;
#include&#xstri;&#xng00;
usingnamespacestd;
117
i
i
“Volumen1”—2012/1/12—13:52—page120—#158i
i
i
i
i
i
Capítulo3.CenC++
#include&#xiost;&#xream;
#include std;&#xlib0;
usingnamespacestd;
intmain(intargc,char*argv[]){
for(inti=1;iargc;i++)
coutatoi(argv[i])endl;
}///:~
Enesteprograma,sepuedeponercualquiernúmerodeargumentosenlalíneadecomandos.Fíjesequeelbucleforcomienzaenelvalor1parasaltarelnombredelprogramaenargv[0].También,siseponeunnúmerodecimalquecontengaunpuntodecimalenlalíneadecomandos,atoi()sólotomalosdígitoshastaelpuntodecimal.Siponevaloresnonuméricosenlalíneadecomandos,atoi()losdevuelvecomoceros.ElformatodepuntootanteLafunciónprintBinary()presentadaanteriormenteenestecapítuloesútilparaindagarenlaestructurainternadevariostiposdedatos.Elmásinteresanteeselformatodepunto-otantequepermiteaCyC++almacenarnúmerosquere-presentanvaloresmuygrandesymuypequeñosenunespaciolimitado.Aunquelosdetallesnosepuedenexponercompletamenteexpuestos,losbitsdentrodelosoatsydoublesestándivididosentresregiones:elexponente,lamantisa,yelbitdesigno;asíalmacenalosvaloresutilizandonotacióncientíca.Elsiguienteprogra-mapermitejugarconelloimprimiendolospatronesbinariosdevariosnúmerosenpunto-otantedemodoqueustedmismopuedadeducirelesquemadelformatodepuntootantedesucompilador(normalmenteeselestándarIEEEparanúmerosenpunto-otante,perosucompiladorpuedenoseguirlo):
//:C03:FloatingAsBinary.cpp
//{L}printBinary
//{T}3.14159
#include"printBinary.h"
#include std;&#xlib0;
#include&#xiost;&#xream;
usingnamespacestd;
intmain(intargc,char*argv[]){
if(argc!=2){
cout"Mustprovideanumber"endl;
exit(1);
}
doubled=atof(argv[1]);
unsignedchar*cp=
reinterpret_castunsignedchar*&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;(&d);
for(inti=sizeof(double)-1;i�=0;i-=2){
printBinary(cp[i-1]);
printBinary(cp[i]);
}
}///:~
120
i
i
“Volumen1”—2012/1/12—13:52—page122—#160i
i
i
i
i
i
Capítulo3.CenC++
Paraunaejecuciónenmimáquina,lasalidaes:
ip=6684124
ip=6684128
dp=6684044
dp=6684052
Lointeresanteaquíesqueaunquelaoperación++parecelamismatantoparaelint*comoparaeldouble*,sepuedecomprobarqueelpunterodeint*hacambiado4bytesmientrasqueparaeldouble*hacambiado8.Noescoincidencia,queestosseanlostamañosdeintydoubleenestamáquina.Yeseeseltrucodelaaritméticadepunteros:elcompiladorcalculalacantidadapropiadaparacambiarelpunterodemodoqueapuntealsiguienteelementoenelvector(laaritméticadepunterossólotienesentidodentrodelosvectores).Estofuncionainclusoconvectoresdestructs:
//:C03:PointerIncrement2.cpp
#include&#xiost;&#xream;
usingnamespacestd;
typedefstruct{
charc;
shorts;
inti;
longl;
floatf;
doubled;
longdoubleld;
}Primitives;
intmain(){
Primitivesp[10];
Primitives*pp=p;
cout"sizeof(Primitives)="
sizeof(Primitives)endl;
cout"pp="(long)ppendl;
pp++;
cout"pp="(long)ppendl;
}///:~
Lasalidaenestamáquinaes:
sizeof(Primitives)=40
pp=6683764
pp=6683804
Comopuedever,elcompiladortambiénhaceloadecuadoparapunterosastr-ucts(yconclassyunion).Laaritméticadepunterostambiénfuncionaconlosoperadores--,+y-,perolosdosúltimosestánlimitados:nosepuedesumardospunteros,ysiserestanpun-teroselresultadoeselnúmerodeelementosentrelosdospunteros.Sinembargo,sepuedesumarorestarunvalorenteroyunpuntero.Acontinuación,unejemplodemostrandoelusodelaaritméticadepunteros:
122
i
i
“Volumen1”—2012/1/12—13:52—page124—#162i
i
i
i
i
i
Capítulo3.CenC++
3.9.ConsejosparadepuraciónEnunentornoideal,habráundepuradorexcelentedisponiblequeharáqueelcomportamientodesuprogramaseatransparenteypodrádescubrircualquiererrorrápidamente.Sinembargo,muchosdepuradorestienenpuntosdébiles,yesopuederequerirtengaqueañadirtrozosdecódigoasuprogramaqueleayudenaentenderqueestápasando.Además,puedequeparalaplataformaparalaqueestédesarro-llando(porejemploensistemasempotrados,conloqueyotuvequetratardurantemisañosdeformación)nohayaningúndepuradordisponible,yquizátengaunare-alimentaciónmuylimitada(porejemplo,undisplaydeLEDsdeunalínea).Enesoscasosdebesercreativoalahoradedescubriryrepresentarinformaciónacercadelaejecucióndesuprograma.Estasecciónsugierealgunastécnicasparaconseguirlo.3.9.1.BanderasparadepuraciónSicolocaelcódigodedepuraciónmezcladoconunprograma,tendráproblemas.Empezaráatenerdemasiadainformación,queharáqueloserroresseandifícilesdeaislar.Cuandocreequehaencontradoelerrorempiezaaquitarelcódigodedepu-ración,sóloparadarsecuentaquenecesitaponerlodenuevo.Puederesolverestosproblemascondostiposdebanderas:banderasdedepuracióndelpreprocesadorybanderasdedepuraciónenejecución.BanderasdedepuraciónparaelpreprocesadorUsandoelpreprocesadorparadenir(con#define)unaomásbanderasdedepuración(preferiblementeenuncherodecabecera),puedeprobarunabande-rausandounasentencia#ifdefeincluircondicionalmentecódigodedepuración.Cuandocreaqueladepuraciónhaterminado,simplementeutilice#undeflaban-derayelcódigoquedaráeliminadoautomáticamente(yreduciráeltamañoysobre-cargadelcheroejecutable).Esmejordecidirlosnombresdelasbanderasdedepuraciónantesdeempezaracontruirelproyectoparaquelosnombresseanconsistentes.Lasbanderasdelpre-procesadortradicionalmentesedistinguendelasvariablesporqueseescribentodoenmayúsculas.UnnombrehabitualessimplementeDEBUG(perotengacuidadodenousarNDEBUG,queestáreservadoenC).Lasecuenciadesentenciaspodríasser:
#defineDEBUG//Probablyinaheaderfile
//...
#ifdefDEBUG//Checktoseeifflagisdefined
/*debuggingcodehere*/
#endif//DEBUG
LamayoríadelasimplementacionesdeCyC++tambiénlepermitirándeniryeliminarbanderas(con#definey#undef)desdelíneadecomandos,ydeesemodopuederecompilarcódigoeinsertarinformacióndedepuraciónconunúnicocomando(preferiblementeconunmakefile,unaherramientaqueserádescritaenbreve).Compruebeladocumentacióndesuentornosinecesitamásdetalles.BanderasparadepuraciónentiempodeejecuciónEnalgunassituacionesesmásconvenienteactivarydesactivarlasbanderasdedepuracióndurantelaejecucióndelprograma,especialmentecuandoelprogramaseejecutausandolalíneadecomandos.Conprogramasgrandesresultapesadore-
124
i
i
“Volumen1”—2012/1/12—13:52—page126—#164i
i
i
i
i
i
Capítulo3.CenC++
3.9.2.ConvertirvariablesyexpresionesencadenasCuandoseescribecódigodedepuración,resultapesadoescribirexpresionesprintqueconsistenenunacadenaquecontieneelnombredeunavariable,segui-dodeelvalordelavariable.Afortunadamente,elCestándarincluyeeloperadordeFIXMEcadenización#,queyaseusóantesenestemismocapítulo.Cuandosecolocaun#antesdeunaargumentosenunamacro,elpreprocesadorconvierteeseargumentosenunacadena.Esto,combinadoconelhechodequelascadenasnoin-dexadascolocadasunaacontinuacióndelaotraseconcatenan,permitecrearmacrosmuyadecuadasparaimprimirlosvaloresdelasvariablesduranteladepuración:
#definePR(x)cout#x"="x"\n";
SiseimprimelavariableainvocandoPR(a),tendráelmismoefectoqueestecódigo:
cout"a="a"\n";
Estemismoprocesofuncionaconexpresionescompletas.Elsiguienteprogramausaunamacroparacrearunatajoqueimprimelaexpresióncadenizadasydespuésevalúalaexpresióneimprimeelresultado:
//:C03:StringizingExpressions.cpp
#include&#xiost;&#xream;
usingnamespacestd;
#defineP(A)cout#A":"(A)endl;
intmain(){
inta=1,b=2,c=3;
P(a);P(b);P(c);
P(a+b);
P((c-a)/b);
}///:~
Puedecomprobarcómounatécnicacomoestasepuedeconvertirrápidamenteenalgoindispensable,especialmentesinotienedepurador(odebeusarmúltiplesentornosdedesarrollo).Tambiénpuedeinsertarun#ifdefparaconseguirqueP-(A)sedenacomo«nada»cuandoquieraeliminarelcódigodedepuración.3.9.3.LamacroCassert()Enelcherodecabeceraestándar&#x-500;cassertapareceassert(),queesunamacrodedepuración.Cuandoseutilizaassert(),seledebedarunargumentoqueesunaexpresiónqueustedestá«aseverando».Elpreprocesadorgeneracódigoquecompruebalaaserción.Silaaserciónnoescierta,elprogramapararádespuésdeimprimirunmensajedeerrorinformandoquelaaserciónfalló.Esteesunejemplotrivial:
//:C03:Assert.cpp
//Useoftheassert()debuggingmacro
#includeÊss;rt0;//Containsthemacro
126
i
i
“Volumen1”—2012/1/12—13:52—page131—#169i
i
i
i
i
i
3.11.Make:cómohacercompilaciónseparada
correspondientes,ysiunacherofuenteesmásrecientequesucheroresultan-te,makerecompilaesecherofuente.makesólorecompilaloscherosfuentequehancambiado,ycualquierotrocheroqueestéafectadoporelcheromodicado.Usandomakenotendráquerecompilartodosloscherosdesuproyectocadavezquehagauncambio,nitendráquecomprobarsitodoseconstruyeadecuadamen-te.Elmakefilecontienetodaslasinstruccionesparamontarelproyecto.Aprenderausarmakelepermitiráahorrarmuchotiempoyfrustraciones.Tambiéndescubri-ráquemakeeselmétodotípicoparainstalarsoftwarenuevoenmáquinasGNUoUnix7(aunqueesosmakefilestienenasermuchomáscomplicadosquelosqueaparecenenestelibro,yamenudopodrágenerarautomáticamenteunmakefileparasumáquinaparticularcomopartedelprocesodeinstalación).Comomakeestádisponibledealgúnmodoparaprácticamentetodosloscompi-ladoresdeC++(inclusosinoloestá,puedeusarmakeslibresconcualquiercompi-lador),serálaherramientausadaenestelibro.Sinembargo,losfabricantesdecom-piladorescreantambiénsuspropiasherramientasparaconstruirproyectos.Estásherramientaspreguntanquécheroshayenelproyectoydeterminanlasrelacionesentreellos.Estasherramientasutilizanalgosimilaraunmakefile,normalmentellamadocherodeproyecto,peroelentornodeprogramaciónmantieneestecheroparaqueelprogramadornotengaquepreocuparsedeél.Laconguraciónyusodeloscherosdeproyectovaríadeunentornodedesarrolloaotro,demodoqueten-dráquebuscarladocumentaciónapropiadaencadacaso(aunqueesasherramientasproporcionadasporelfabricantenormalmentesontansimplesdeusarqueesfácilaprenderausarlasjugandounpococonellas-mimétodoeducativofavorito).Losmakefilesqueacompañanaestelibrodeberíanfuncionarbieninclusositambiénusaunaherramientaespecícaparaconstruccióndeproyectos.3.11.1.LasactividadesdeMakeCuandoescribemake(ocualquieraqueseaelnombredelsuprogramamake),makebuscauncherollamadomakefileoMakefileeneldirectorioactual,queustedhabrácreadoparasuproyecto.Estecherocontieneunalistadedependenciasentrecherosfuente,makecompruebalasfechasdeloscheros.Siuncherotieneunafechamásantiguaqueelcherodelquedepende,makeejecutalareglaindicadadespuésdeladependencia.Todosloscomentariosdelosmakefilesempiezanconun#ycontinúanhastaelnadelalínea.Comounejemplosencillo,elmakefileparaunaprogramallamado«hello»po-dríacontener:
#Acomment
hello.exe:hello.cpp
mycompilerhello.cpp
Estodicequehello.exe(elobjetivo)dependedehello.cpp.Cuandohello.cpptieneunafechamásrecientequehello.exe,makeejecutala«regla»mycom-pilerhello.cpp.Puedehabermúltiplesdependenciasymúltiplesreglas.Muchasim-plementacionesdemakerequierenquetodaslasreglasempiecenconuntabulador.
7(N.deT.)Elmétododelquehablaelautorsereerenormalmenteasoftwareinstaladoapartirdesucódigofuente.LainstalacióndepaquetesbinariosesmuchomássimpleyautomatizadaenlamayoríadelasvariantesactualesdelsistemaoperativoGNU.
131
i
i
“Volumen1”—2012/1/12—13:52—page133—#171i
i
i
i
i
i
3.11.Make:cómohacercompilaciónseparada
indicanporquetieneunsignicadoespecialparaestemakefileenparticular.Losiguientequeapareceeslaregladesujo.cpp.exe,quedice«cómoconvertircual-quiercheroconextensión.cppaunoconextensión.exe»(cuandoelchero.cppesmásrecientequeelchero..exe).Comoantes,seusalamacro$(CPP),peroaquíaparecealgonuevo:$.Comoempiezaconun$esqueesunamacro,peroestaesunadelasmacrosespecialespredenidaspormake.El$sepuedeusarsóloenreglasdesujoysignica«cualquierprerrequisitoquedisparelaregla»(avecesllamadodependencia),queenestecasosereereal«chero.cppquenecesitasercompilado».Unaverquelasreglasdesujosehanjado,puedeindicarporejemploalgotansimplecomomakeUnion.exeyseaplicarálareglasujo,inclusoaunquenosemencione«Union»enningunapartedelmakefile.ObjetivospredeterminadosDespuésdelasmacrosylasreglasdesujo,makebuscalaprimero«regla»delchero,ylaejecuta,amenosqueseespecicaunaregladiferente.Asíquepareelsiguientemakefile:
CPP=mycompiler
.SUFFIXES:.exe.cpp
.cpp.exe:
$(CPP)$
target1.exe:
target2.exe:
Siejecutasimplementemake,seconstruirátarget1.exe(usandolaregladesu-jopredeterminada)porqueeseeselprimerobjetivoquemakevaaencontrar.Paraconstruirtarget2.exesedebeindicarexplícitamentediciendomaketarget2.exe.Estopuederesultartediosodemodoquenormalmentesecreaunobjetivo«dummy»pordefectoquedependedelrestodeobjetivos,comoéste:
CPP=mycompiler
.SUFFIXES:.exe.cpp
.cpp.exe:
$(CPP)$
all:target1.exetarget2.exe
Aquí,allnoexisteynohayningúncherollamadaall,asíquecadavezqueejecutemake,elprogramaveráquealleselprimerobjetivodelalista(yportantoelobjetivopordefecto),entoncescomprobaráqueallnoexisteyanalizarásusdepen-dencias.Compruebatarget1.exey(usandolaregladesujo)comprobará(1)quetarget1.exeexistey(2)quetarget1.cppesmásrecientequetarget1.exe,ysiesasíejecutarálaregla(siproporcionaunareglaexplícitaparaunobjetivoconcre-to,seusaráesareglaensulugar).Despuéspasaaanalizarelsiguientecherodelalistadeobjetivospordefecto.Deestemodo,breandounalistadeobjetivospordefec-to(típicamentellamadaallporconvenio,aunquesepuedetenercualquiernombre)puedeconseguirqueseconstruyantodoslosejecutablesdesuproyectosimplemen-teescribiendomake.Además,puedetenerotraslistasdeobjetivosparahacerotrascosas-porejemplo,podríahacerqueescribiendomakedebugsereconstruyerantodosloscherosperoincluyendoinformacióndedepuración.
133
i
i
“Volumen1”—2012/1/12—13:52—page137—#175i
i
i
i
i
i
3.13.Ejercicios
sistemaoperativo,ocompilador,intenteesteexperimentocontantasvariacio-nescomopuedamanejar.7.Creedosfunciones,unaquetomeunstring*yunaquetomeunstring&.Cadaunadeestasfuncionesdeberíamodicarelobjetoexternoasumanera.Enm-ain(),creeeinicialiceunobjetostring,imprímalo,despuéspáseloacadaunadelasdosfunciones,imprimiendolosresultados.8.Escribaunprogramaqueusetodoslostrígrafosparaversisucompiladorlossoporta.9.CompileyejecuteStatic.cpp.Eliminelapalabrareservadastaticdelcó-digo,compileyejecútelodenuevo,yexpliqueloqueocurre.10.IntentecompilaryenlazarFileStatic.cppconFileStatic2.cpp.¿Quésignicanlosmensajesdeerrorqueaparecen?11.ModiqueBoolean.cppparaquefuncioneconvaloresdoubleenlugardeint.12.ModiqueBoolean.cppyBitwise.cppdemodoqueusenlosoperadoresexplícitos(sisucompiladoresconformealEstándarC++lossoportará).13.ModiqueBitwise.cppparausarlasfuncionesdeRotation.cpp.Asegú-resedequemuestralosresultadosquedejeclaroquéocurredurantelasrota-ciones.14.ModiqueIfthen.cppparausareloperadorif-elseternario(?:).15.Creeunastructquecontengadosobjetosstringyunoint.Useuntype-defparaelnombredelastruct.Creeunainstanciadelastruct,inicialicelostresvaloresdelainstancia,ymuestrelosenpantalla.Tomeladireccióndesuinstanciayasígnelaaunpunteroatipodelastruct.Usandoelpuntero,Cambielostresvaloresdelainstanciaymuestrelos.16.Creeunprogramaqueuseunenumeradodecolores.Creeunavariabledeestetipoenumy,utilizandounbucle,muestretodoslosnúmerosquecorrespondenalosnombresdeloscolores.17.ExperimenteconUnion.cppeliminandovarioselementosdelaunionparaverelefectoquecausaeneltamañodelaunionresultante.Intenteasignarunelemento(portantountipo)delaunionymuéstrelopormediodeunelementodiferente(portanto,untipodiferente)paraverqueocurre.18.Creeunprogramaquedenadosarraysdeint,unoacontinuacióndelotro.Indexeelprimerarraymásalládesutamañoparacaersobreelsegundo,hagaunaasignación.Muestreelsegundoarrayparaverloscambiosqueesohacausado.Ahoraintentedenirunavariablecharentrelasdenicionesdelosarrays,yrepitaelexperimento.Quizáquieracrearunafunciónparaimprimirarraysyasísimplicarelcódigo.19.ModiqueArrayAddresses.cppparaquefuncioneconlostiposdedatoschar,longint,oatydouble.20.ApliquelatécnicadeArrayAddresses.cppparamostrareltamañodelastructylasdireccionesdeloselementosdelarraydeStructArray.cpp.21.Creeunarraydeobjetosstringyasigneunacadenaacadaelemento.Muestreelarrayusandounbuclefor.
137
i
i
“Volumen1”—2012/1/12—13:52—page139—#177i
i
i
i
i
i
3.13.Ejercicios
33.Declareunpunteroaunfunciónquetomaunargumentointyretornaunpun-teroaunafunciónquetomaunargumentocharyretornaunoat.34.ModiqueFunctionTable.cppparaquecadafunciónretorneunstring(enlugardemostrarunmensaje)demodoqueestevalorseimprimaenmain().35.Creeunmakefileparaunodelosejerciciosprevios(asuelección)queleper-mitaescribirmakeparaconstruirunaversiónenproduccióndelprogramaymakedebugparaconstruirunaversióndelprogramaqueincluyeinformacióndedepuración.
139
i
i
“Volumen1”—2012/1/12—13:52—page142—#180i
i
i
i
i
i
Capítulo4.AbstraccióndeDatos
4.1.UnalibreríapequeñaalestiloCAunquemuchasveces,unalibreríacomienzacomounacoleccióndefunciones,sihausadoalgunalibreríaCdeterceroshabráobservadoquelacosanoterminaahíporquehaymásquecomportamiento,accionesyfunciones.Tambiénhaycaracterís-ticas(azul,libras,textura,luminiscencia),lascualesestánrepresentadaspordatos.EnC,cuandodebemosrepresentarcaracterísticas,esmuyconvenienteagruparlastodasjuntasenunaestructura,especialmentecuandoqueremosrepresentarmásdeuntipodecosaenelproblema.Así,sepuedetrabajarconunavariabledeestaes-tructuraspararepresentarcadacosa.Poreso,lamayoríadelaslibreríasenCestánformadasporunconjuntodees-tructurasyfuncionesqueactúansobrelasprimeras.Comoejemplodeestatécnica,considereunaherramientadeprogramaciónquesecomportacomounarray,perocuyotamañosepuedejarentiempodeejecución,enelmomentodesucreación.LallamaremosCStash1.AunqueestáescritoenC++,tieneelestiloclásicodeunalibreríaescritaenC:
//:C04:CLib.h
//HeaderfileforaC-likelibrary
//Anarray-likeentitycreatedatruntime
typedefstructCStashTag{
intsize;//Sizeofeachspace
intquantity;//Numberofstoragespaces
intnext;//Nextemptyspace
//Dynamicallyallocatedarrayofbytes:
unsignedchar*storage;
}CStash;
voidinitialize(CStash*s,intsize);
voidcleanup(CStash*s);
intadd(CStash*s,constvoid*element);
void*fetch(CStash*s,intindex);
intcount(CStash*s);
voidinflate(CStash*s,intincrease);
///:~
Normalmenteseutilizaun«rótulo»comoCStashTagenaquellasestructurasquenecesitanreferenciarsedentrodesimismas.Eseeselcasodeunalistaenlazada(cadaelementodelalistacontieneunpunteroalsiguienteelemento)senecesitaunpun-teroalasiguientevariableestructura,osea,unamaneradeidenticareltipodeesepunterodentrodelcuerpodelapropiaestructura.Enladeclaracióndelasestructu-rasdeunalibreríaescritaenCtambiénesmuycomúnverelusodetypedefcomoeldelejemploanterior.Estopermitealprogramadortratarlasestructurascomounnuevotipodedatoyasídenirnuevasvariables(deesaestructura)delsiguientemodo:
CStashA,B,C;
Elpunterostorageesununsignedchar*.Ununsignedchareslamenorpieza
1NdeT:«Stash»sepodríatraducircomo«Acumulador».
142
i
i
“Volumen1”—2012/1/12—13:52—page144—#182i
i
i
i
i
i
Capítulo4.AbstraccióndeDatos
intnewQuantity=s�-quantity+increase;
intnewBytes=newQuantity*s�-size;
intoldBytes=s�-quantity*s�-size;
unsignedchar*b=newunsignedchar[newBytes];
for(inti=0;ioldBytes;i++)
b[i]=s�-storage[i];//Copyoldtonew
delete[](s�-storage);//Oldstorage
s�-storage=b;//Pointtonewmemory
s�-quantity=newQuantity;
}
voidcleanup(CStash*s){
if(s�-storage!=0){
cout"freeingstorage"endl;
delete[]s�-storage;
}
}///:~
initialize()realizalasoperacionesinicialesnecesariasparalastructCStash,poniendolosvaloresapropiadosenlasvariablesinternas.Inicialmente,elpunterostoragetieneuncerodadoqueaúnnosehaalmacenadonada.Lafunciónadd()insertaunelementoenelsiguientelugardisponibledelaCS-tash.Paralograrlo,primerovericaquehayasucienteespaciodisponible.Sinolohay,expandeelespaciodealmacenamiento(storage)usandolafuncióninflat-e()quesedescribedespués.Comoelcompiladornoconoceeltipoespecícodelavariablequeestásiendoalmacenada(todoloqueobtienelafunciónesunvoid*),nosepuedehacerunaasignaciónsimple,queseríalomásconveniente.Enlugardeeso,lavariablesecopiabyteabyte.Lamaneramásdirectadehacerloesutilizandoelindexadodearrays.Lohabitualesqueenstorageyahayabytesalmacenados,locualesindicadoporelvalordenext.Paraobtenerlaposicióndeinsercióncorrectaenelarray,semultiplicanextporeltamañodecadaelemento(enbytes)locualproduceelvalordestart-Bytes.Luegoelargumentoelementsemoldeaaunsignedchar*paraquesepuedadireccionarycopiarbyteabyteenelespaciodisponibledestorage.Seincrementanextdemodoqueindiqueelsiguientelugardealmacenamientodisponibleyel«índice»enelquehaalmacenadoelelementoparaqueelvalorsepuederecuperarutilizandoelíndiceconfetch().fetch()vericaqueelíndicetengaunvalorcorrectoydevuelveladireccióndelavariabledeseada,quesecalculaenfuncióndelargumentoindex.DadoqueindexesundesplazamientodesdeelprincipioenlaCStash,sedebemultiplicarporeltamañoenbytesqueocupacadaelementoparaobtenerdichodesplazamientoenbytes.Cuandoutilizamosestedesplazamientocomoíndicedelarraystorageloqueobtenemosnoesladirección,sinoelbytealmacenado.Loquehacemosentoncesesutilizareloperadordirección-de&.count()puedeparecerunpocoextrañaalosprogramadoresexperimentadosenC.Podríaparecerdemasiadocomplicadaparaunatareaqueprobablementeseamuchomásfácildehaceramano.Porejemplo,sitenemosunaCStashllamadai-ntStash,esmuchomásdirectopreguntarporlacantidaddeelementosutilizandointStash.next,quellamaraunafunción(queimplicasobrecarga),comocoun-t(&intStash).Sinembargo,lacantidaddeelementossecalculaenfuncióntantodelpunteronextcomodeltamañoenbytesdecadaelementodelaCStash;poresolainterfazdelafuncióncount()permitelaexibilidadnecesariaparanotener
144
i
i
“Volumen1”—2012/1/12—13:52—page145—#183i
i
i
i
i
i
4.1.UnalibreríapequeñaalestiloC
quepreocuparnosporestascosas.Pero,¡ay!,lamayoríadelosprogramadoresnosepreocuparánpordescubrirloqueparanosotrosesel«mejor»diseñoparalalibrería.Probablementeloqueharánesmirardentrodelaestructurayobtenerelvalordenextdirectamente.Peoraún,podríaninclusocambiarelvalordenextsinnuestropermiso.¡Sihubieraalgunaformaquepermitieraaldiseñadordelalibreríatenerunmejorcontrolsobreestetipodecosas!(Sí,estoesunpresagio).4.1.1.AsignacióndinámicadememoriaNuncasepuedesaberlacantidadmáximadealmacenamientoquesenecesitaráparaunaCStash,poresolamemoriaalaqueapuntanloselementosdestorageseasignadesdeelmontículo(heap)2.Elmontículoesungranbloquedememoriaqueseutilizaparaasignarenpequeñostrozosentiempodeejecución.Seusaelheapcuandonoseconocedeantemanolacantidaddememoriaquenecesitaráelprogra-maqueestáescribiendo.Porejemplo,esoocurreenunprogramaenelquesóloenelmomentodelaejecuciónsesabesisenecesiamemoriapara200variablesAviónopara20.EnCEstándar,lasfuncionesparaasignacióndinámicadememoriainclu-yenmalloc(),calloc(),realloc()yfree().Enlugardellamadasalibrerías,C++cuentaconunatécnicamássosticada(yporlotantomásfácildeusar)paratratarlamemoriadinámica.Estatécnicaestáintegradaenellenguajepormediodelaspalabrasreservadasnewydelete.Lafuncióninflate()usanewparaobtenermásmemoriaparalaCStash.Enestecasoelespaciodememoriasóloseampliaynuncasereduce.assert()garan-tizaquenosepaseunnúmeronegativocomoargumentoainflate()comovalordeincremento.Lanuevacantidaddeelmentosquesepodránalmacenar(unavezsehayaterminadoinflate())sedeterminaenlavariablenewQuantityquesemultiplicaporelnúmerodebytesqueocupacadaelemento,paraobtenerelnuevonúmerototaldebytesdelaasignaciónenlavariablenewBytes.Dadoquesesa-becuántosbyteshayquecopiardesdelaubicaciónanterior,oldBytessecalculausandolacantidadantiguadebytes(quantity).Lapeticióndememoriaocurrerealmenteenlaexpresión-newqueinvolucralapalabrareservadanew:
newunsignedchar[newBytes];
Laformageneraldeunaexpresión-newes:
newTipo;
dondeTipodescribeeltipodevariableparalacualsesolicitamemoriaenelmon-tículo.Dadoqueenestecaso,sedeseaasignarmemoriaparaunarraydeunsignedchardenewByteselementos,esoesloqueaparececomoTipo.Delmismomodo,sepuedeasignarmemoriaparaalgomássimplecomounintconlaexpresión:
newint;
yaunqueestoseutilizamuypoco,demuestraquelasintaxisesconsistente.Unaexpresión-newdevuelveunpunteroaunobjetodeltipoexactoqueselepidió.
2N.deT.:heapsesueletraduciralcastellanocomo«montón»o«montículo».
145
i
i
“Volumen1”—2012/1/12—13:52—page149—#187i
i
i
i
i
i
4.2.¿Quétienedemalo?
llamamosafunc()conunintcomoargumento,elcompiladorsabráquedeberáconvertirelintaoatantesdepasarleelvaloralafunción(aestoselellamapromo-cióndetipos).Sinladeclaración,elcompiladorasumiríaquelafuncióntienelaformafunc(int),norealizaríalapromociónypasaría,porlotanto,datosincorrectosalafunción.Paracadaunidaddetraducción,elcompiladorcreaunarchivoobjeto,deexten-sión.o,.objoalgoporelestilo.Estosarchivosobjeto,juntoconalgodecódigodearranqueseunensporelenlazador(linker)paracrearelprogramaejecutable.Todaslasreferenciasexternassedebenresolverenlafasedeenlazado.EnarchivoscomoCLibTest.cpp,sedeclaranfuncionescomoinitialize()yfetch()(osea,seleinformaalcompiladorquéformatienenestasfunciones),peronosedenen.Es-tándenidasenotrolugar,enestecasoenelarchivoCLib.cpp.Deesemodo,lasllamadasquesehacenenCLibTest.cppaestasfuncionessonreferenciasexternas.Cuandoseunenlosarchivosobjetoparaformarelprogramaejecutable,elenlazadordebe,paracadareferenciaexternanoresuelta,encontrarladirecciónalaquehacereferenciayreemplazarcadareferenciaexternaconsudireccióncorrespondiente.EsimportanteseñalarqueenC,estasreferenciasexternasqueelenlazadorbuscasonsimplesnombresdefunciones,generalmenteprecedidosporunguiónbajo.Deestaforma,laúnicatareadelenlazadoreshacercorresponderelnombredelafun-ciónquesellama,conelcuerpo(denición,código)delafuncióndelarchivoobjeto,enellugarexactodelallamadaadichafunción.Si,porejemplo,accidentalmentehacemosunallamadaaunafunciónqueelcompiladorinterpretecomofunc(int)yexisteunadenicióndefunciónparafunc(float)enalgúnarchivoobjeto,elenlazadorverá_funcenunlugary_funcenotro,porloquepensaráquetodoestábien.Enlallamadaafunc()sepasaráunintenlapilaperoelcuerpodelafunciónfunc()esperaráquelapilatengaunoat.Silafunciónsóloleeelvalordeestedatoynoloescribe,lapilanosufrirádatos.Dehecho,elsupuestooatleídodelapilapuedeteneralgodesentido:lafunciónseguiráfuncionandoaunquesobrebasura,yesporesoquelosfallosoriginadasporestaclasedeerroressonmuydifícilesdeencontrar.4.2.¿Quétienedemalo?Somosseresrealmentedestinadosalaadaptación,inclusoalasquequizánode-beríamosadaptarnos.ElestilodelalibreríaCStashhasidounmodeloaseguirparalosprogramadoresenCdurantemuchotiempo.Sinembargo,sinosponemosaexa-minarlaporunmomento,nosdaremoscuentadequeutilizarestalibreríapuederesultarincómodo.Cuandolausamosdebemos,porejemplo,pasarladireccióndelaestructuraacadafuncióndelalibrería.Poreso,cuandoleemoselcódigo,losme-canismosdelalibreríasemezclanconelsignicadodelasllamadasalasfunciones,locualdicultalacomprecsióndelprograma.Sinembargo,unodelosmayoresobstáculosaltrabajarconlibreríasenCeselproblemallamadoconictodenombres(nameclashes).Ctrabajaconunúnicoespaciodenombresdefunciones.Estosignicaque,cuandoelenlazadorbuscaporelnom-bredeunafunción,lohaceenunaúnicalistadenombresmaestra.Además,cuandoelcompiladortrabajasobreunaunidaddetraducción,unnombredefunciónsólopuedehacerreferenciaaunaúnicafunciónconesenombre.Supongamosquecompramosdoslibreríasdediferentesproveedoresyquecadalibreríaconstadeunaestructuraquedebeinicializarydestruir.Supongamosqueca-daproveedorhadecididonombraradichasoperacionesinitialize()yclean-up().¿Cómosecomportaríaelcompiladorsiincluyéramoslosarchivosdecabecera
149
i
i
“Volumen1”—2012/1/12—13:52—page152—#190i
i
i
i
i
i
Capítulo4.AbstraccióndeDatos
storage=0;
next=0;
}
intStash::add(constvoid*element){
if(next�=quantity)//Enoughspaceleft?
inflate(increment);
//Copyelementintostorage,
//startingatnextemptyspace:
intstartBytes=next*size;
unsignedchar*e=(unsignedchar*)element;
for(inti=0;isize;i++)
storage[startBytes+i]=e[i];
next++;
return(next-1);//Indexnumber
}
void*Stash::fetch(intindex){
//Checkindexboundaries:
assert(0index);
if(index�=next)
return0;//Toindicatetheend
//Producepointertodesiredelement:
return&(storage[index*size]);
}
intStash::count(){
returnnext;//NumberofelementsinCStash
}
voidStash::inflate(intincrease){
assert(increase�0);
intnewQuantity=quantity+increase;
intnewBytes=newQuantity*size;
intoldBytes=quantity*size;
unsignedchar*b=newunsignedchar[newBytes];
for(inti=0;ioldBytes;i++)
b[i]=storage[i];//Copyoldtonew
delete[]storage;//Oldstorage
storage=b;//Pointtonewmemory
quantity=newQuantity;
}
voidStash::cleanup(){
if(storage!=0){
cout"freeingstorage"endl;
delete[]storage;
}
}///:~
HaymuchasotrascosasquedieresentreCyC++.Paraempezar,elcompila-dorrequierequedeclarelasfuncionesenlosarchivosdecabecera:enC++nopodrállamaraunafunciónsinhaberladeclaradoantesysinosecumpleestareglaelcom-piladordaráunerror.Estaesunaformaimportantedeasegurarquelasllamadasaunafunciónsonconsistentesentreelpuntoenquesellamayelpuntoenquesedene.Alforzaradeclararunafunciónantesdeusarla,elcompiladordeC++prác-
152
i
i
“Volumen1”—2012/1/12—13:52—page153—#191i
i
i
i
i
i
4.3.Elobjetobásico
ticamenteseaseguradequerealizaráesadeclaraciónpormediodelainclusióndeuncherodecabecera.Además,sitambiénincluyeelmismocherodecabeceraenelmismolugardondesedeneslasfunciones,elcompiladorvericaráquelasde-claracionesdelarchivocabeceraylasdenicionescoinciden.Puededecirseentoncesque,dealgúnmodo,loscherosdecabecerasevuelvenunrepositoriodevalidacióndefuncionesypermitenasegurarquelasfuncionesseusandemodoconsistenteentodaslasunidadesdetraduccióndelproyecto.Obviamente,lasfuncionesglobalessepuedenseguirdeclarandoamanoenaque-lloslugaresenlasquesedenenyusan(Sinembargo,estaprácticaestantediosaqueestáendesuso.)Decualquiermodo,lasestructurassiempresedebendeclararantesdeserusadasyelmejorlugarparaestoesuncherodecabecera,exceptuandoaquellasquequeremosesconderintencionalmenteenotrochero.Sepuedeverquetodaslasfuncionesmiembro(métodos)tienencasilamismaformaquesusversionesrespectivasenC.Lasúnicasdiferenciassonsuámbitoderesoluciónyelhechodequeelprimerargumentoyanoapareceexplícitoenelpro-totipodelafunción.Porsupuestoquesigueahíyaquelafuncióndebesercapazdetrabajarsobreunavariablestructenparticular.Sinembargo,fíjesetambiénque,dentrodelmétodo,laseleccióndeestaestructuraenparticulartambiénhadesapa-recido!Así,enlugardedecir�s-size=sz;ahoradicesize=sz;eliminandoeltedioso�s-queenrealidadnoaportabanadaalsignicadosemánticodeloqueestabaescribiendo.Aparentemente,elcompiladordeC++estárealizandoestasta-reasporelprogramador.Dehecho,estátomandoelprimerargumento«secreto»(ladireccióndelaestructuraqueantesteníaquepasaramano)yaplicándoleelselectordemiembro�(-)siemprequeescribeelnombredeunodelosdatosmiembro.Esosignicaque,siempreycuandoestédentrodeladenicióndeunamétododeunaestructurapuedehacerreferenciaacualquierotromiembro(incluyendootrométo-do)simplementedandosunombre.Elcompiladorbuscaráprimeroenlosnombreslocalesdelaestructuraantesdebuscarenversionesmásglobalesdedichosnom-bres.Ellectorpodrádescubrirqueestacaracterísticanosóloagilizalaescrituradelcódigo,sinoquetambiénhacelalecturadelmismomuchomássencilla.Peroquépasaríasi,poralgunarazón,quisierahacerreferenciaaladireccióndememoriadelaestructura.EnlaversiónenCdelalibreríaéstasepodíaobtenerfácilmentedelprimerargumentodecualquierfunción.EnC++lacosaesmáscon-sistente:existelapalabrareservadathisqueproduceladireccióndelavariablestructactual.EselequivalentealaexpresiónsdelaversiónenCdelalibrería.Demodoque,podremosvolveralestilodeCescribiendo
this�-size=Size;
Elcódigogeneradoporelcompiladorseráexactamenteelmismoporloquenoesnecesariousarthisenestoscasos.Ocasionalmente,podráverporahícódigodóndelagenteusathisentodossitiossinagregarnadaalsignicadodelcódigo(estaprácticaesindiciodeprogramadoresinexpertos).Porlogeneral,thisnoseusamuyamenudopero,cuandosenecesitesiempreestaráallí(enejemplosposterioresdellibroverámássobresuuso).Quedaaúnunúltimotemaquetocar.EnC,sepuedeasignarunvoid*acualquierotropuntero,algocomoesto:
inti=10;
void*vp=&i;//OKtantoenCcomoenC++
int*ip=vp;//óSloaceptableenC
153
i
i
“Volumen1”—2012/1/12—13:52—page154—#192i
i
i
i
i
i
Capítulo4.AbstraccióndeDatos
ynohabráningúntipodequejaporpartedecompilador.Sinembargo,enC++,loanteriornoestápermitido.¿Porqué?PorqueCnoestanestrictoconlostiposdedatosypermiteasignarunpunterosinuntipoespecícoaunpunterodeuntipobiendeterminado.NoasíC++,enelcuallavericacióndetiposescríticayelcom-piladorsedetendráquejándoseencualquierconictodetipos.Estosiemprehasidoimportante,peroesespecialmenteimportanteenC++yaquedentrodelasestructu-raspuedehacermétodos.SienC++estuvierapermitidopasarpunterosaestructurasconimpunidadencuantoaconictodetipos,¡podríaterminarllamandoaunméto-dodeunaestructuraenlacualnoexistieradichafunciónmiembro!Unaverdaderafórmulaparaeldesastre.Así,mientrasC++sídejaasignarcualquierpunteroaunvoid*(enrealidadesteeselpropósitooriginaldelpunteroavoid:queseasucien-tementelargocomoparaapuntaracualquiertipo)nopermiteasignarunvoid*acualquierotrotipodepuntero.Paraelloserequiereunmoldequeleindiquetantoallectorcomoalcompiladorquerealmentequieretratarlocomoelpunterodestino.Yestonosllevaadiscutirunasuntointeresante.UnodelosobjetivosimportantesdeC++espodercompilarlamayorcantidadposibledecódigoCparaasí,permi-tirunafáciltransiciónalnuevolenguaje.Sinembargo,esonosignica,comosehavistoquecualquiersegmentodecódigoqueseaválidoenC,serápermitidoautomá-ticamenteenC++.HayvariascosasqueuncompiladordeCpermitehacerquesonpotencialmentepeligrosasypropensasagenerarerrores(veráejemplosdealolargodelibro).ElcompiladordeC++generaerroresyavisosenestetipodesituacionesycomoveráesoesmásunaventajaqueunobstáculoapesardesunaturalezares-trictiva.¡Dehecho,existenmuchassituacionesenlascualestratarádedetectarsinéxitounerrorenCycuandorecompileselprogramaconuncompiladordeC++ésteavisaexactamentedelacausadelproblema!.EnC,muyamenudoocurrequeparaqueunprogramafuncionecorrectamente,ademásdecompilarlo,luegodebehacerqueande.¡EnC++,porelcontrario,veráquemuchasvecessiunprogramacompilacorrectamenteesprobablequefuncionebien!Estosedebeaqueesteúltimolenguajeesmuchomásestrictorespectoalacomprobacióndetipos.EnelsiguienteprogramadepruebapodráapreciarcosasnuevasconrespectoacómoseutilizalanuevaversióndelaStash:
//:C04:CppLibTest.cpp
//{L}CppLib
//TestofC++library
#include"CppLib.h"
#include"../require.h"
#includestr;êm0;
#include&#xiost;&#xream;
#include&#xstri;&#xng00;
usingnamespacestd;
intmain(){
StashintStash;
intStash.initialize(sizeof(int));
for(inti=0;i100;i++)
intStash.add(&i);
for(intj=0;jintStash.count();j++)
cout"intStash.fetch("j")="
*(int*)intStash.fetch(j)
endl;
//Holds80-characterstrings:
StashstringStash;
constintbufsize=80;
154
i
i
“Volumen1”—2012/1/12—13:52—page157—#195i
i
i
i
i
i
4.6.Detallesdelobjeto
Sinembargo,notaráinmediatamenteunadiferenciaenlaformaenqueserea-lizanlasoperacionessobrelosobjetos.Sehaceobjeto.funciónMiembro(list-aArgumentos)osea,«sellamaaunmétododeunobjeto».Peroenlajergadelaorientaciónaobjetos,esotambiénsedenomina«enviarunmensajeaunobjeto».DemodoqueparaunaStashs,enestajergalasentencias.add(&i)le«envíaunmensajeas»diciéndole«añadete(add())esto».Dehecho,laprogramaciónorien-tadaaobjetossepuederesumirenlasiguientefrase:enviarmensajesaobjetos.Real-mente,¿esoestodoloquesehace?crearunmontóndeobjetosyenviarlesmensajes.Eltruco,obviamente,esentenderquésonennuestroproblemalosobjetosylosmen-sajes,perounavezquesehacumplidoesaetapa,laimplementaciónenC++serásorprendentementedirecta.4.6.DetallesdelobjetoUnapreguntaquesurgeamenudoenseminarioses«¿Cómodegrandeesunobjetoyquépintatiene?»Larespuestaes«másomenosloqueesperasdeunst-ructenC».Dehecho,elcódigoqueproduceelcompiladordeCparaunstructC(sinadornosC++)normalmenteesexactamenteelmismoqueelproducidoporuncompiladorC++.EsotranquilizaaaquellosprogramadoresCquedependandelosdetallesdetamañoydistribucióndesucódigo,yqueporalgunarazónaccedandirectamentealosbytesdelaestructuraenlugardeusaridenticadores(conarenuntamañoydistribuciónparticularparaunaestructuranoesportable).Eltamañodeunastructeslacombinacióndelostamañosdetodossusmiem-bros.Avecescuandoelcompiladorcreaunastruct,añadebytesextraparahacerqueloslímitesencajenlimpiamente-esopuedeincrementarlaecienciadelaejecu-ción.EnelCapítulo14,verácómoenalgunoscasosseañadenpunteros«secretos»alaestructura,peronotienequepreocuparsedeesoahora.Puededeterminareltamañodeunastructusandoeloperadorsizeof.Aquítieneunpequeñoejemplo:
//:C04:Sizeof.cpp
//Sizesofstructs
#include"CLib.h"
#include"CppLib.h"
#include&#xiost;&#xream;
usingnamespacestd;
structA{
inti[100];
};
structB{
voidf();
};
voidB::f(){}
intmain(){
cout"sizeofstructA="sizeof(A)
"bytes"endl;
cout"sizeofstructB="sizeof(B)
"bytes"endl;
cout"sizeofCStashinC="
157
i
i
“Volumen1”—2012/1/12—13:52—page158—#196i
i
i
i
i
i
Capítulo4.AbstraccióndeDatos
sizeof(CStash)"bytes"endl;
cout"sizeofStashinC++="
sizeof(Stash)"bytes"endl;
}///:~
Enmimáquina(losresultadospuedenvariar)elprimerresultadoproduce200porquecadaintocupa2bytes.LastructBesalgoanómaloporqueesunastructsinatributos.EnC,esoesilegal,peroenC++necesitamoslaposibilidaddecrearunastructcuyaúnicatareaesofrecerunámbitoanombresdefunciones,poresoestápermitido.Aúnasi,elsegundoresultadoesunsorprendentevalordistintodecero.Enversionesanterioresdellenguage,eltamañoeracero,peroapareciaunasituaciónincómodacuandosecreabanestosobjetos:teníanlamismadirecciónqueelobjetocreadoantesqueél,yeranindistinguibles.Unadelasreglasfundamentalesdelosobjetosesquecadaobjetodebetenerunadirecciónúnica,asíquelasestructurassinatributossiempretendrántamañomínimodistintodecero.LasdosúltimassentenciassizeofmuestranqueeltamañodelaestructuraenC++eselmismoqueenlaversiónenC.C++intentanoañadirningunasobrecargainnecesaria.4.7.ConvecionesparaloscherosdecabeceraCuandosecreaunastructquecontienefuncionesmiembro,seestácreandounnuevotipodedato.Engeneral,seintentaqueesetiposeafácilmenteaccesible.Enresumen,sequierequelainterfaz(ladeclaración)estéseparadadelaimplmentación(ladenicióndelosmétodos)demodoquelaimplementaciónpuedacambiarsinobligararecompilarelsistemacompleto.Esoseconsigueponiendoladeclaracióndelnuevotipoenuncherodecabecera.CuandoyoaprendíaprogramarenC,elcherodecabeceraeraunmisterioparami.MuchoslibrosdeCnohacenhincapié,yelcompiladornoobligaahacerlade-claracióndelasfunciones,asíqueparecíaalgoopcionallamayorpartedelasveces,exceptocuandosedeclarabanestrucutras.EnC++elusodeloscherosdecabecerasevuelveclarocomoelcristal.Sonprácticamenteobligatoriosparaeldesarrollodeprogramassencillos,yenellospodráinformaciónmuyespecíca:declaraciones.Elcherodecabecerainformaalcompiladordeloquehaydisponibleenlalibrería.Puedeusarlalibreríainclusosisólosedisponedelcherodecabecerayelcheroobjetooelcherodelibrería;nonecesitadisponerdelcódigofuentedelcherocpp.Enelcherodecabeceraesdondeseguardalaespecicacióndelainterfaz.Aunqueelcompiladornoloobliga,elmejormododeconstruirgrandesproyec-tosenCesusarlibrerías;coleccionesdefuncionesasociadasenunmismomóduloobjetoolibrería,yusaruncherodecabeceraparacolocartodaslasdeclaracionesdelasfunciones.EsderigorenC++,PodríametercualquierfunciónenunalibreríaC,peroeltipoabstractodedatoC++determinalasfuncionesqueestánasociadaspormediodelaccesocomúnalosdatosdeunastruct.Cualquierfunciónmiembrodebeserdeclaradaenladeclaracióndelastruct;nopuedeponerseenotrolugar.ElusodelibreríasdefuncionesfuefomentadoenCyinstitucionalizadoenC++.4.7.1.ImportanciadeloscherosdecabeceraCuandoseusafuncióndeunalibrería,Clepermitelaposibilidaddeignorarelcherodecabeceraysimplementedeclararlafunciónamano.Enelpasado,lagente
158
i
i
“Volumen1”—2012/1/12—13:52—page161—#199i
i
i
i
i
i
4.7.Convecionesparaloscherosdecabecera
#defineFLAG
opuededarleunvalor(queeslamanerahabitualenCparadenirunaconstan-te):
#definePI3.14159
Encualquiercaso,ahoraelpreprocesadorpuedecomprobarsilaetiquetahasidodenida:
#ifdefFLAG
Estoproduciráunresultadoverdadero,yelcódigoquesigueal#ifdefsein-cluiráenelpaquetequeseenvíaalcompilador.Estainclusiónacabacuandoelpre-procesadorencuentralasentencia:
#endif
o
#endif//FLAG
Cualquiercosadespuésde#endifenlamismalíneaquenoseauncomentarioesilegal,inclusoaunquealgunoscompiladoresloacepten.Lospares#ifdef/#en-difsepuedenanidar.Elcomplementariode#definees#undef(abreviaciónde«un-dene»queharáqueunasentencia#ifdefqueuselamismavariableproduzcaunresultadofalso.#undeftambiéncausaráqueelpreprocesadordejedeusarunamacro.Elcomple-mentariode#ifdefes#ifndef,queproduciráverdaderosilaetiquetanohasidodenida(ésteeselqueusaremosenloscherosdecabecera).HayotrascaracterísticasútilesenelpreprocesadordeC.Consulteladocumen-tacióndesupreprocesadorparavertodasellas.4.7.4.UnestándarparaloscherosdecabeceraEncadacherodecabeceraquecontieneunaestructura,primerodeberíacom-probarsiesecheroyahasidoincludoenestecherocppparticular.Hágalocom-probandounabanderadelpreprocesador.Silabanderanoestádenida,elcheronosehaincluidoaún,ysedeberíadenirlabandera(demodoquelaestructuranosepuedaredeclarar)ydeclararlaestructura.Silabanderaestabadenidaentonceseltipoyahasidodeclaradodemodoquedeberíaignorarelcódigoqueladeclara.Asíescomodeberíaseruncherodecabecera:
#ifndefHEADER_FLAG
#defineHEADER_FLAG
//Escribalaódeclaraciníaqu...
#endif//HEADER_FLAG
161
i
i
“Volumen1”—2012/1/12—13:52—page164—#202i
i
i
i
i
i
Capítulo4.AbstraccióndeDatos
responsabledeladestruccióndelobjetoapuntadopordata.)Lafunciónpeak()tambiéndevuelveunpunterodataalacimadelapila,perodejaelelementoenlaStack.Aquísemuestranlasdenicionesdelosmétodos:
//:C04:Stack.cpp{O}
//Linkedlistwithnesting
#include"Stack.h"
#include"../require.h"
usingnamespacestd;
void
Stack::Link::initialize(void*dat,Link*nxt){
data=dat;
next=nxt;
}
voidStack::initialize(){head=0;}
voidStack::push(void*dat){
Link*newLink=newLink;
newLink�-initialize(dat,head);
head=newLink;
}
void*Stack::peek(){
require(head!=0,"Stackempty");
returnhead�-data;
}
void*Stack::pop(){
if(head==0)return0;
void*result=head�-data;
Link*oldHead=head;
head=head�-next;
deleteoldHead;
returnresult;
}
voidStack::cleanup(){
require(head==0,"Stacknotempty");
}///:~
Laprimeradeniciónesparticularmenteinteresanteporquemuestracómosede-neunmiembrodeunaestructuraanidada.Simplementeseusaunniveladicionalderesolucióndeámbitoparaespecicarelnombredelastructinterna.Stack:-:Link::initialize()tomadosargumentosylosasignaasusatributos.Stack::initialize()asginaceroahead,demodoqueelobjetosabequetieneunalistavacía.Stack::push()tomaelargumento,queesunpunteroalavariablealaquesequiereseguirlapista,ylaapilaenlaStack.Primero,usanewparapediralojamientoparaelLinkqueseinsertaráenlacima.Entoncesllamaalafuncióninitialize()paraasignarlosvaloresapropiadosalosmiembresdelLink.Fijesequeelsiguientepunteroseasignaalheadactual;entoncesheadseasignaalnuevopunteroLink.
164
i
i
“Volumen1”—2012/1/12—13:52—page168—#206i
i
i
i
i
i
Capítulo4.AbstraccióndeDatos
enlaceelprogramausandoelcompiladorC,yveaqueocurrecuandoseejecu-ta.Expliqueelcomportamiento.10.AverigüecómogenerarlenguajeensambladorconsucompiladorCyC++.Es-cribaunafunciónenCyunastructconunúnicomiembroenC++.Generelasalidaenlenguajeensambladorparacadaunadeellasyencuentrelosnombresdeambasfunciones,demodoquepuedaverquétipode«decoración»aplicaelcompiladoradichosnombres.11.Escribaunprogramaconcódigocondicionalmente-compiladoenmain(),pa-raquecuandosedenaunvalordelpreprocesador,semuestreunmensaje,perocuandonosedena,seimprimaotramensajedistinto.Compileesteex-perimentandoconun#defineenelprograma,despuésaverigüelaformadeindicaralcompiladordenicionesdepreprocesadorenlalíneadecomandosyexperimenteconello.12.Escribaunprogramaqueuseassert()conunargumentoquesiempreseafalso(cero)yveaqueocurrecuandoloejecuta.Ahoracompílelocon#defineNDEBUGyejecútelodenuevoparaverladiferencia.13.Creeuntipoabstractodedatoquerepresenteuncintadevídeoenunatiendadealquiler.ConsideretodoslosdatosyoperacionesqueseríannecesariasparaqueeltipoVideofuncioneconelsistemadegestióndelatienda.Incluyaunmétodoprint()quemuestreinformaciónsobreelVideo14.CreeunobjetoPilaquealmaceneobjetosVideodelEjercicio13.CreevariosobjetosVideo,guárdelosenlaStackyentoncesmuéstrelosusandoVideo-::print().15.Escribaunprogramaquemuestretodoslostamañosdelostiposdedatosfun-damentalesdesucomputadorausandosizeof.16.ModiqueStashparausar har;vectorcomoestructuradedatossubya-cente.17.Creedinámicamenteespaciodealmacenamientoparalossiguientetiposusan-donew:int,long,unarrayde100char,unarrayde100oat.Muestresusdireccionesylibérelosusandodelete.18.Escribaunafunciónquetomeunargumentochar*.Usandonew,pidaaloja-mientodinámicoparaunarraydecharconuntamañoigualalargumentopasadoalafunción.Usandoindexacióndearray,copieloscaracteresdelargu-mentoalarraydinámico(noolvideelterminadornulo)ydevuelvaelpunteroalacopia.Ensumain(),pruebelafunciónpasandounacadenaestáticaentrecomillas,despuéstomeelresultadoypáselodenuevoalafunción.Muestreambascadenasypunterosparapoderverquetienendistintaubicación.Me-diantedeleteliberetodoelalmacenamientodinámico.19.Hagaunejemplodeestructuradeclaradaconotraestructuradentro(unes-tructuraanidada).Declareatributosenambasstructs,ydeclareydenamétodosenambasstructs.Escribaunmain()quepruebelosnuevostipos.20.¿Cómodegrandeesunaestructura?Escribauntrozodecódigoquemuestreeltamañodevariasestructuras.Creeestructurasquetengansóloatributosyotrasquetenganatributosymétodos.Despuéscreeunaestructuraquenoten-ganingúnmiembro.Muestrelostamañosdetodasellas.Expliqueelmotivodeltamañodelaestructuraquenotieneningúnmiembro.
168
i
i
“Volumen1”—2012/1/12—13:52—page170—#208i
i
i
i
i
i
i
i
“Volumen1”—2012/1/12—13:52—page171—#209i
i
i
i
i
i
5:OcultarlaimplementaciónUnalibreríaCtípicacontieneunaestructurayunaseriedefun-cionesqueactúansobreesaestructura.Hastaahorahemosvistocó-moC++tomafuncionesconceptualmenteasociadasylasasociali-teralmenteponiendoladeclaracióndelafuncióndentrodeldominiodelaes-tructura,cambiandolaformaenqueseinvocaalasfuncionesdesdelasestructuras,eliminandoelpasodeladireccióndelaestructuracomoprimerparámetro,yaña-diendounnuevotipoalprograma(deesemodonoesnecesariocrearuntypedefparalaestructura).Todoestosonmejoras,leayudaaorganizarsucódigohaciéndolomásfácildeescribiryleer.Sinembargo,hayotrosaspectosimportantesalahoradehacerquelaslibreríasseanmássencillasenC++,especialmentelosaspectosdeseguridadycontrol.Estecapítulosecentraeneltemadelafronteradelasestructuras.5.1.EstablecerloslímitesEntodarelaciónesimportantetenerfronterasquetodaslaspartesrespeten.Cuandocreaunalibrería,estableceunarelaciónconelprogramadorclientequelausaparacrearunprogramauotralibrería.EnunaestructuradeC,comocasitodoenC,nohayreglas.Losprogramadoresclientepuedenhacerloquequieranconesaestructura,ynohayformadeforzaruncomportamientoparticular.Porejemplo,aunquevioenelcapítuloanteriorlaimportanciadelasfuncionesllamadasinitialize()ycleanup(),elprograma-dorclientetienelaopcióndenollamarlas.(Veremosunaformamejordehacerloenelcapítulosiguiente.)Inclusosirealmentepreerequeelprogramadorclientenomanipuledirectamentealgunosmiembrosdesuestructura,enCnohayformadeevitarlo.Todoestáexpuestoaltodoelmundo.Haydosrazonesparacontrolarelaccesoalosmiembros.Laprimeraesnode-jarqueelprogramadorclientepongalasmanossobreherramientasquenodeberíatocar,herramientasquesonnecesariasparalosentresijosdeltipodenido,peronopartedelinterfazqueelprogramadorclientenecesitapararesolversusproblemasparticulares.Estoesrealmenteunaventajaparalosprogramadoresclienteporqueasípuedenverloqueesrealmenteimportanteparaelloseignorarelresto.Lasegundarazónparaelcontroldeaccesoespermitiraldiseñadordelalibreríacambiarsufuncionamientointernosinpreocuparsedecomoafectaraalprograma-dorcliente.EnelejemploStackdelcapítuloanterior,podríaquerersolicitarespaciodealmacenamientoengrandestrozos,paraconseguirmayorvelocidad,envezdecrearunnuevoespaciocadavezqueunelementoesañadido.Silainterfazylaim-plementaciónestánclaramenteseparadasyprotegidas,puedehacerloyforzaral
171
i
i
“Volumen1”—2012/1/12—13:52—page174—#212i
i
i
i
i
i
Capítulo5.Ocultarlaimplementación
unanuevaclaseydecir,«Hola,soyfrienddeBob»yesperarverlosmiembrosprivateyprotecteddeBob.Puededeclararunafunciónglobalcomofriend,tambiénpuededeclararunmétododeotraestructura,oinclusounaestructuracompleta,comofriend.Aquíhayunejemplo:
//:C05:Friend.cpp
//Friendallowsspecialaccess
//Declaration(incompletetypespecification):
structX;
structY{
voidf(X*);
};
structX{//Definition
private:
inti;
public:
voidinitialize();
friendvoidg(X*,int);//Globalfriend
friendvoidY::f(X*);//Structmemberfriend
friendstructZ;//Entirestructisafriend
friendvoidh();
};
voidX::initialize(){
i=0;
}
voidg(X*x,inti){
x�-i=i;
}
voidY::f(X*x){
x�-i=47;
}
structZ{
private:
intj;
public:
voidinitialize();
voidg(X*x);
};
voidZ::initialize(){
j=99;
}
voidZ::g(X*x){
x�-i+=j;
}
voidh(){
Xx;
174
i
i
“Volumen1”—2012/1/12—13:52—page175—#213i
i
i
i
i
i
5.3.Amigos(friends)
x.i=100;//Directdatamanipulation
}
intmain(){
Xx;
Zz;
z.g(&x);
}///:~
structYtieneunmétodof()quemodicaunobjetodetipoX.AquíhayunpocodelíopuesenC++elcompiladornecesitaqueusteddeclaretodoantesdepoderhacerreferenciaaello,asístructYdebeestardeclaradoantesdequesumétodoY::f(X*)puedaserdeclaradocomofriendenstructX.PeroparadeclararY::f(X*),structXdebeestardeclaradaantes!Aquívemoslasolución.DesecuentadequeY::f(X*)tomacomoargumentoladireccióndeunobjetodetipoX.Estoesfundamentalpueselcompiladorsiempresabecómopasarunadirección,queesdeuntamañojosinimportareltipo,aunquenotengainformacióndeltamañoreal.Siintentapasarelobjetocompleto,elcompi-ladornecesitaverladenicióncompletadeX,parasabereltamañodeloquequierepasarycómopasarlo,antesdequelepermitadeclararunafuncióncomoY::g(X).PasandoladireccióndeunX,elcompiladorlepermitehacerunaidenticacióndetipoincompletadeXantesdedeclararY::f(X*).Estoseconsigueconladeclaración:
structX;
Estadeclaraciónsimplementeledicealcompiladorquehayunaestructuraconesenombre,asíqueescorrectoreferirseaellasiemprequesólosenecesiteelnombre.Ahora,enstructX,lafunciónY::f(X*)puedeserdeclaradacomofriendsinproblemas.Siintentadeclararlaantesdequeelcompiladorhayavistolaespeci-cacióncompletadeY,habríadadounerror.Estoesunarestricciónparaasegurarconsistenciayeliminarerrores.Fíjeseenlasotrasdosfuncionesfriend.Laprimeradeclaraunafunciónglobalordinariag()comofriend.Perog()nohasidodeclaradaantescomoglobal!.Sepuedeusarfrienddeestaformaparadeclararlafunciónydarleelestadodefriendsimultáneamente.Estoseextiendeaestructurascompletas:
friendstructZ;
esunaespecicaciónincompletadeltipoZ,ydaatodalaestructuraelestadodefriend.5.3.1.AmigasanidadasHacerunaestructuraanidadanoledaaccesoalosmiembrosprivados.Paracon-seguiresto,sedebe:primero,declarar(sindenir)laestructuraanidada,despuésdeclararlacomofriend,ynalmentedenirlaestructura.Ladenicióndelaes-tructuradebeestarseparadadesudeclaracióncomofriend,sinoelcompiladorlaveríacomonomiembro.Aquíhayunejemplo:
175
i
i
“Volumen1”—2012/1/12—13:52—page178—#216i
i
i
i
i
i
Capítulo5.Ocultarlaimplementación
quetodoelmundopuedaverqueesaesunadelasfuncionesprivilegiadas.C++esunlenguajeorientadoaobjetoshíbrido,noespuro,yfriendfueañadi-doparasolucionaralgunosproblemasquesepresentabanenlapráctica.Esbuenoapuntarqueestohaceallenguajemenos«puro»,puesC++fuediseñadoparaserpragmático,noparaaspiraraunidealabstracto.5.4.CapadeobjetosEnelcapítulo4sedijoqueunastructescritaparauncompiladorCymástardecompiladaenunodeC++nocambiaría.Sereferíabásicamentealaestructurainternadelobjetoquesurgedelastruct,esdecir,laposiciónrelativaenmemoriadondeseguardanlosvaloresdelasdiferentesvariables.SielcompiladorC++cambiaseestaestructurainterna,entonceselcódigoescritoenCquehicieseusodelconocimientodelasposicionesdelasvariablesfallaría.Cuandoseempiezanausarlosespecicadoresdeacceso,secambiaaluniversodelC++,ylascosascambianunpoco.Dentrodeun«bloquedeacceso»(ungru-podedeclaracionesdelimitadoporespecicadoresdeacceso),segarantizaquelasvariablesseencontrarancontiguas,comoenC.Sinembargo,losbloquesdeaccesopuedennoaparecerenelobjetoenelmismoordenenquesedeclaran.Aunqueelcompiladornormalmentecolocarálosbloquescomolosdenió,nohayreglasso-breesto,puesunaarquitecturahardwareespecicay/ounsistemaoperativopuedetenersoporteespecicoparaprivateyprotectedquepuederequerirquees-tosbloquessecoloquenenlugaresespecícosdelamemoria.Laespecicacióndellenguajenoquiereimpedirestetipodeventajas.Losespecicadoresdeaccesosonpartedelaestructuraynoafectanalosob-jetoscreadosdesdeésta.Todalainformacióndeaccesosdesapareceantesdequeelprogramaseejecute;engeneralocurredurantelacompilación.Enunprogramaenejecución,losobjetosson«zonasdealmacenamiento»ynadamás.Sirealmentequiere,puederompertodaslasreglasyaccederalamemoriadirectamente,comoenC.C++noestádiseñadoparaprohibirhacercosassalvajes.Sololeproporcionaunaalternativamuchomásfácil,ydeseable.Engeneral,noesunabuenaideahacerusodenadaquedependadelaimple-mentacióncuandoseescribeunprograma.Cuandonecesitehacerlo,encapsúleloenunaestructura,asíencasodetenerqueportarlosepodráconcentrarenella.5.5.LaclaseElcontroldeaccesosesuelellamartambiénocultacióndelaimplementación.Incluirfuncionesdentrodelasestructuras(amenudollamadoencapsulación1)produceti-posdedatoconcaracterísticasycomportamiento,peroelcontroldeaccesoponefronterasenesostipos,pordosrazonesimportantes.Laprimeraesparaestablecerloqueelprogramadorclientepuedeynopuedehacer.Puedeconstruirlosmecanis-mosinternosdelaestructurasinpreocuparsedequeelprogramadorclientepuedapensarquesonpartedelainterfazquedebeusar.Estonosllevadirectamentealasegundarazón,queessepararlainterfazdelaimplementación.Silaestructuraseusaenunaseriedeprogramas,yelprogramadorclientenopuedehacermásquemandarmensajesalainterfazpública,ustedpuedecambiarcualquiercosaprivadasinquesedebamodicarcódigocliente.
1Comosedijoanteriormente,aveceselcontroldeaccesosellamatambiénencapsulación
178
i
i
“Volumen1”—2012/1/12—13:52—page179—#217i
i
i
i
i
i
5.5.Laclase
Laencapsulaciónyelcontroldeacceso,juntos,creanalgomásqueunaestructuradeC.Estamosahoraenelmundodelaprogramaciónorientadaaobjetos,dondeunaestructuradescribeunaclasedeobjetoscomodescribiríaunaclasedepecesopájaros:Cualquierobjetoquepertenezcaaesaclasecompartiráesascaracterísticasycomportamiento.Enestosehaconvertidoladeclaracióndeunaestructura,enunadescripcióndelaformaenlaquelosobjetosdeestetiposerányactuarán.EnellenguajeOOPoriginal,Simula-67,lapalabraclaveclassfueusadaparadescribirunnuevotipodedato.AparentementeestoinspiroaStroustrupaelegiresamismapalabraenC++,paraenfatizarqueesteeraelpuntoclavedetodoellenguaje:lacreacióndenuevostiposdedatoquesonmásquesoloestructurasdeCconfunciones.Estoparecesucientejusticaciónparaunanuevapalabraclave.Detodasformas,elusodeclassenC++escasiinnecesario.Esidénticoas-tructentodoslosaspectosexceptoenuno:classponepordefectoprivate,mientrasquestructlohaceapublic.Estassondosformasdedecirlomismo:
//:C05:Class.cpp
//Similarityofstructandclass
structA{
private:
inti,j,k;
public:
intf();
voidg();
};
intA::f(){
returni+j+k;
}
voidA::g(){
i=j=k=0;
}
//Identicalresultsareproducedwith:
classB{
inti,j,k;
public:
intf();
voidg();
};
intB::f(){
returni+j+k;
}
voidB::g(){
i=j=k=0;
}
intmain(){
Aa;
Bb;
a.f();a.g();
b.f();b.g();
179
i
i
“Volumen1”—2012/1/12—13:52—page180—#218i
i
i
i
i
i
Capítulo5.Ocultarlaimplementación
}///:~
Laclase(class)enunconceptoOOPfundamentalenC++.Esunadelapalabrasclavequenosepondránennegritaenestelibro-esincomodopuesserepitemucho.ElcambioaclasesestanimportantequesospechoqueStroustruphubiesepreferidoeliminarcompletamentestruct,perolanecesidaddecompatibilidadconCnolohubiesepermitido.Muchagentepreerecrearclasesalamanerastructenvezdealamáneraclass,puessustituyeel«por-defecto-private»declassempezandoconlosele-mentospublic:
classX{
public:
voidmiembro_de_interfaz();
private:
voidmiembro_privado();
intrepresentacion_interna;
};
Elporquédeestoesquetienemássentidoverprimeroloquemásinteresa,elpro-gramadorclientepuedeignorartodoloquediceprivate.Dehecho,laúnicarazóndequetodoslosmiembrosdebanserdeclaradosenlaclaseesqueelcompiladorse-pacomodegrandesonlosobjetosypuedacolocarloscorrectamente,garantizandoasílaconsistencia.Detodasformas,losejemplosenestelibropondránlosmiembrosprivadospri-mero,así:
classX{
voidprivate_function();
intinternal_representation;
public:
voidinterface_function();
};
Algunagenteinclusodecorasusnombresprivados
classY{
public:
voidf();
private:
intmX;//"Self-decorated"name
};
ComomXestayaocultoparaY,lam(de«miembro»)esinnecesaria.Detodasformas,enproyectosconmuchasvariablesglobales(algoquedebeevitaratodacos-ta,aunqueavecesinevitableenproyectosexistentes),esdeayudapoderdistinguirvariablesglobalesdeatributosenladenicióndelosmétodos.
180
i
i
“Volumen1”—2012/1/12—13:52—page183—#221i
i
i
i
i
i
5.6.Manejodeclases
5.6.2.ReducirlarecompilaciónSuentornodeprogramaciónprovocaráunarecompilacióndeuncherosiestesemodica,osisemodicaotrocherodelquedepende,esdecir,unarchivodecabeceraquesehayaincluido.Estosignicaquecadavezquesehagauncambioenunaclase,yaseaalainterfazpúblicaoalasdeclaracionesdelosmiembrosprivados,seprovocaráunarecompilacióndetodoloqueincluyaesearchivodecabecera.Esteefectoseconoceusualmentecomoelproblemadelaclase-basefrágil.Paraunproyectograndeensuscomienzosestopuedeserungranproblemapueslaimplementaciónsuelecambiaramenudo;sielproyectoesmuygrande,eltiempodelascompilacio-nespuedellegaraserungranproblema.Latécnicapararesolverestosellamaavecesclasesmanejadoroel«gatodeChe-sire»2-todalainformaciónsobrelaimplementacióndesapareceexceptoporunpuntero,la"sonrisa".Elpunteroapuntaaunaestructuracuyadeniciónseencuen-traenelcherodeimplementaciónjuntocontodaslasdenicionesdelasfuncionesmiembro.Así,siemprequelainterfaznosecambie,elarchivodecabeceraperma-neceinalterado.Laimplementaciónpuedecambiarasugusto,ysóloelcherodeimplementacióndeberáserrecompiladoyreenlazadoconelproyecto.Aquíhayunejemploquedemuestracomousarestatécnica.Elarchivodecabece-racontienesololainterfazpublicayunpunterodeunaclaseespecicadadeformaincompleta:
//:C05:Handle.h
//Handleclasses
#ifndefHANDLE_H
#defineHANDLE_H
classHandle{
structCheshire;//Classdeclarationonly
Cheshire*smile;
public:
voidinitialize();
voidcleanup();
intread();
voidchange(int);
};
#endif//HANDLE_H///:~
Estoestodoloqueelprogramadorclientepuedever.Lalinea
structCheshire;
esunaespecicacióndetipoincompletaounadeclaracióndeclase(unadenicióndeclasedebeincluirelcuerpodelaclase).LedicealcompiladorqueChesireeselnombredeunaestructura,peronodetallessobreella.Estaesinformaciónsucienteparacrearunpunteroalaestructura;nopuedecrearunobjetohastaqueelcuerpodelaestructuraquededenido.Enestatécnica,elcuerpodelaestructuraestáescondidoenelcherodeimplementación:
2EstenombreseleatribuyeaJohnCarolan,unodelospionerosdelC++,yporsupuesto,LewisCarroll.Estatécnicasepuedevertambiéncomounaformadeltipodediseño«puente»,descritoenelsegundovolumen.
183
i
i
“Volumen1”—2012/1/12—13:52—page184—#222i
i
i
i
i
i
Capítulo5.Ocultarlaimplementación
//:C05:Handle.cpp{O}
//Handleimplementation
#include"Handle.h"
#include"../require.h"
//DefineHandle'simplementation:
structHandle::Cheshire{
inti;
};
voidHandle::initialize(){
smile=newCheshire;
smile�-i=0;
}
voidHandle::cleanup(){
deletesmile;
}
intHandle::read(){
returnsmile�-i;
}
voidHandle::change(intx){
smile�-i=x;
}///:~
Chesireesunaestructuraanidada,asíquesedebeserdenidoconresolucióndeámbito:
structHandle::Cheshire{
EnHandle::initialize(),sesolicitaespaciodealmacenamientoparaunaestructuraChesire,yenHandle::cleanup()seliberaeseespacio.Esteespacioseusaparaalmacenartodoslosdatosqueestaríannormalmenteenlasecciónpriva-dadelaclase.CuandocompileHandle.cpp,estadenicióndelaestructuraestaráescondidaenelcheroobjetodondenadiepuedeverla.SicambialoselementosdeChesire,elúnicoarchivoquedebeserrecompiladoesHandle.cpppueselarchivodecabecerapermaneceinalterado.ElusodeHandleescomoelusodecualquierclase:incluirlacabecera,crearobjetos,ymandarmensajes.
//:C05:UseHandle.cpp
//{L}Handle
//UsetheHandleclass
#include"Handle.h"
intmain(){
Handleu;
u.initialize();
u.read();
u.change(1);
u.cleanup();
}///:~
184
i
i
“Volumen1”—2012/1/12—13:52—page185—#223i
i
i
i
i
i
5.7.Resumen
Laúnicacosaalaqueelprogramadorclientepuedeaccederesalainterfazpu-blica,asíquemientraslaimplementaciónsealoúnicoquecambie,elcheroanteriornonecesitarecompilarse.Así,aunqueestonoesocultacióndeimplementaciónper-fecta,esunagranmejora.5.7.ResumenElcontroldeaccesoenC++ofreceungrancontrolalcreadordelaclase.Losusuariosdelaclasepuedenverclaramenteloquepuedenusaryquépuedeignorar.Másimportanteaúneslaposibilidaddeasegurarqueningúnprogramadorclien-tedependedeningunapartedelaimplementacióninternadelaclase.Sisabeestocomocreadordelaclase,puedecambiarlaimplementaciónsubyacenteconlasegu-ridaddequeningúnprogramadorclienteseveráafectadoporloscambios,puesnopuedenaccederaesapartedelaclase.Cuandotengalaposibilidaddecambiarlaimplementaciónsubyacente,nosolopodrámejorarsudiseñomástarde,tambiéntienelalibertaddecometererrores.Noimportaconquécuidadoplaneesudiseño,cometeráerrores.Sabiendoqueesrelativamenteseguroquecometeráesoserrores,experimentarámás,aprenderámásrápido,yacabarásuproyectoantes.Lainterfazpúblicadeunaclaseesloquerealmenteveelprogramadorcliente,asíqueeslapartedelaclasemásimportanteduranteelanálisisydiseño.Peroinclusoestoledejaalgodelibertadparaelcambio.Sinoconsiguelainterfazcorrectaalapri-mera,puedeañadirmásfunciones,mientrasnoquiteningunaqueelprogramadorclienteyahayausadoensucódigo.5.8.EjerciciosLassolucionesalosejerciciossepuedenencontrareneldocumentoelectróni-cotitulado«TheThinkinginC++AnnotatedSolutionGuide»,disponibleporpocodineroenwww.BruceEckel.com.1.Creeunaclaseconatributosymétodospublic,privateyprotected.Creeunobjetodeestaclaseyveaquémensajesdecompilaciónobtienecuandoin-tentaaccederalosdiferentesmiembrosdelaclase.2.EscribaunaestructurallamadaLibquecontengatresobjetosstringa,byc.Enmain()creeunobjetoLibllamadoxyasígneloax.a,x.byx.c.Imprimaporpantallasusvalores.Ahorareemplacea,bycconunarraydecadenass[3].Desecuentadequesufunciónmain()dejadefuncionarco-moresultadodelcambio.Ahoracreeunaclase,llámelaLibccontrescadenascomodatosmiembroprivadosa,byc,ymétodosseta(),geta(),setb(-),getb(),setc()ygetc()paraestableceryrecuperarlosdistintosvalores.Escribaunafunciónmain()comoantes.Ahoracambielascadenasprivadasa,bycporunarraydecadenasprivados[3].Veaqueahoramain()siguefuncionando.3.Creeunaclaseyunafunciónfriendglobalquemanipulelosdatosprivadosdelaclase.
185
i
i
“Volumen1”—2012/1/12—13:52—page187—#225i
i
i
i
i
i
6:InicializaciónylimpiezaElcapitulo4constituyeunamejorasignicativaenelusodeli-breríastomandolosdiversoscomponentesdeunalibreríaCtípicayencapsulándolosenunaestructura(untipoabstractodedato,llama-doclaseapartirdeahora).Estonosólopermitedisponerdeunúnicopuntodeentradaenuncomponentedelibrería,tambiénocultalosnombresdelasfuncionesconelnombredelaclase.Estoledaaldiseñadordelaclaselaposibilidaddeestablecerlímitesclarosquedeterminanquécosaspuedehacerelprogramadorclienteyquéquedafueradesuslímites.Esosignicaquelosmecanismosinternosdelasoperacionessobrelostiposdedatosestánbajoelcontrolyladiscrecióndeldiseñadordelaclase,ydejaclaroaquémiembrospuedeydebeprestaratenciónelprogramadorcliente.Juntos,laencapsulaciónyelcontroldeaccesorepresentanunpasosignicativoparaaumentarlasencillezdeusodelaslibrerías.Elconceptode«nuevotipodedato»queofrecenesmejorenalgunossentidosquelostiposdedatosqueincorporaC.ElcompiladorC++ahorapuedeofrecergarantíasdecomprobacióndetiposparaesostiposdedatosyasíaseguraunniveldeseguridadcuandoseusanesostiposdedatos.Apartedelaseguridad,elcompiladorpuedehacermuchomáspornosotrosdeloqueofreceC.EnésteyenpróximoscapítulosveráposibilidadesadicionalesquesehanincluidoenC++yquehacenqueloserroresensusprogramascasisaltendelprogramayleagarren,avecesantesinclusodecompilarelprograma,peronor-malmenteenformadeadvertenciasyerroresenelprocesodecompilación.Porestemotivo,prontoseacostumbraráalaextrañasituaciónenqueunprogramaC++quecompila,funcionaalaprimera.Dosdeesascuestionesdeseguridadsonlainicializaciónylalimpieza.Granpar-tedeloserroresdeCsedebenaqueelprogramadorolvidainicializaroliberarunavariable.EstosucedeespecialmenteconlaslibreríasC,cuandoelprogramadorclientenosabecomoinicializarunaestructura,oinclusosidebehacerlo.(Amenudolaslibreríasnoincluyenunafuncióndeinicialización,demodoqueelprogramadorclienteseveforzadoainicializarlaestructuraamano).LalimpiezaesunproblemaespecialporquelosprogramadoresCseolvidandelasvariablesunavezquehanterminado,demodoqueomitencualquierlimpiezaquepudierasernecesariaenalgunaestructuradelalibrería.EnC++.elconceptodeinicializaciónylimpiezaesesencialparafacilitarelusodelaslibreríasyeliminarmuchosdeloserroressutilesqueocurrencuandoelpro-gramadorclienteolvidacumplirconsusactividades.Estecapítuloexaminalasposi-bilidadesdeC++queayudanagarantizarunainicializaciónylimpiezaapropiadas.
187
i
i
“Volumen1”—2012/1/12—13:52—page189—#227i
i
i
i
i
i
6.2.Limpiezagarantizadaporeldestructor
árbol,entoncesdebecrearunobjetoárbolcomoéste:
Treet(12)//árbolde12metros
SiTree(int)eselúnicoconstructor,elcompiladornolepermitirácrearunobjetodeotromodo.(Enelpróximocapítuloveremoscómocrearmúltiplescons-tructoresydiferentesmanerasparainvocarlos.)Yrealmenteunconstructornoesmásqueeso;esunafunciónconunnombrees-pecialqueseinvocaautomáticamenteporelcompiladorparacadaobjetoenelmo-mentodesucreación.Apesardesusimplicidad,tieneunvalorexcepcionalporqueevitaunagrancantidaddeproblemasyhacequeelcódigoseamásfácildeescribiryleer.Enelfragmentodecódigoanterior,porejemplo,nohayunallamadaexplícitaaningunafuncióninitilize()que,conceptualmenteesunafunciónseparadadeladenición.EnC++,ladenicióneinicializaciónsonconceptosunicados-nosepuedetenerelunosielotro.Constructorydestructorsontiposdefuncionesmuyinusuales:notienenvalorderetorno.Estoesdistintodetenervalorderetornovoid,queindicaríaquelafun-ciónnoretornanadaperoteniendolaposibilidaddehacerotracosa.Constructoresydestructoresnoretornannadaynohayotraposibilidad.Elactodetraerunobjetoalprograma,osacarlodeélesalgoespecial,comoelnacimientoolamuerte,yelcompiladorsiemprehacequelafunciónsellameasimisma,paraasegurarsedequeocurrerealmente.Sihubieraunvalorderetorno,yustedpudieraelegirunopropio,elcompiladornotendríaformadesaberquéhacerconelvalorretornado,oelpro-gramadorclientetendríaquedisponerdeunainvocaciónexplícitadelconstructorodestructor,loqueeliminaríalaseguridad.6.2.LimpiezagarantizadaporeldestructorComounprogramadorC,amenudopensarásobreloimportantedelainicializa-ción,peroraravezpiensaenlalimpieza.Despuésdetodo,¿quéhayquelimpiardeunint?Simplemente,olvidarlo.Sinembargo,conlaslibrerías,«dejarlopasar»enunobjetocuandoyanolonecesitanoesseguro.Quéocurresieseobjetomodicaalgoenelhardware,oescribealgoenpantalla,otieneasociadoespacioenelmontícu-lo(heap).Sisimplementepasadeél,suobjetonuncalograrásalirdeestemundo.EnC++,lalimpiezaestanimportantecomolainicializaciónyporesoestágarantizadaporeldestructor.Lasintaxisdeldestructoressimilaraladelconstructor:seusaelnombredelaclasecomonombreparalafunción.Sinembargo,eldestructorsedistinguedelconstructorporquevaprecedidodeunavirgulilla(~).Además,eldestructornuncatieneargumentosporqueladestrucciónnuncanecesitaningunaopción.Aquíhayunadeclaracióndeundestructor:
classY{
public:
~Y();
};
Eldestructorseinvocaautomáticamenteporelcompiladorcuandoelobjetosaledelámbito.Puedeverdóndeseinvocaalconstructorporelpuntodeladenicióndelobjeto,perolaúnicaevidenciadequeeldestructorfueinvocadoeslallavedecierredelámbitoalqueperteneceelobjeto.Elconstructorseinvocainclusoaunque
189
i
i
“Volumen1”—2012/1/12—13:52—page190—#228i
i
i
i
i
i
Capítulo6.Inicializaciónylimpieza
utilicegotoparasaltarfueradeldelámbito(gotosigueexistiendoenC++porcompatibilidadconC.)Deberíanotarqueungotono-local,implementadoconlasfuncionessetjmpylongjmp()delalibreríaestándardeC,evitanqueeldestructorseainvocado.(Esoeslaespecicación,inclusosisucompiladornoloimplementadeesamanera.Conarununacaracterísticaquenoestáenlaespecicaciónsignicaquesucódigonoseráportable).Acontinuación,unejemploquedemuestralascaracterísticasdeconstructoresydestructoresquesehanmostradohastaelmomento.
//:C06:Constructor1.cpp
//Constructors&destructors
#include&#xiost;&#xream;
usingnamespacestd;
classTree{
intheight;
public:
Tree(intinitialHeight);//Constructor
~Tree();//Destructor
voidgrow(intyears);
voidprintsize();
};
Tree::Tree(intinitialHeight){
height=initialHeight;
}
Tree::~Tree(){
cout"insideTreedestructor"endl;
printsize();
}
voidTree::grow(intyears){
height+=years;
}
voidTree::printsize(){
cout"Treeheightis"heightendl;
}
intmain(){
cout"beforeopeningbrace"endl;
{
Treet(12);
cout"afterTreecreation"endl;
t.printsize();
t.grow(4);
cout"beforeclosingbrace"endl;
}
cout"afterclosingbrace"endl;
}///:~
Yestaseríalasalidadelprogramaanterior:
antesdelallavedeaperturaé
190
i
i
“Volumen1”—2012/1/12—13:52—page191—#229i
i
i
i
i
i
6.3.Eliminacióndelbloquededeniciones
despusdelaócreacindeTree
laalturadelárboles12
antesdelallavedecierre
dentrodeldestructordeTree
laalturadelárboles16é
despusdelallavedecierre
Puedeverqueeldestructorsellamaautomáticamentealacabarelámbito(llavedecierre)enelqueestádenidoelobjeto.6.3.EliminacióndelbloquededenicionesEnC,siempresedenentodaslasvariablesalprincipiodecadabloque,justodespuésdelallavedeapertura.Éseesunrequisitohabitualenloslenguajesdepro-gramación,ylarazónquesedaamenudoesqueseconsidera«buenasprácticasdeprogramación».Enestetema,yotengomissospechas.Esosiempremeparecióuninconveniente,comoprogramador,volveralprincipiodelbloquecadavezquene-cesitabadenirunanuevavariable.Tambiénencuentromáslegibleelcódigocuandoladenicióndelavariableestácertadelpuntodondeseusa.Quizáesosargumentossonestilísticos.EnC++,sinembargo,existeunproblemasignicativosisefuerzaadenirtodoslosobjetosalcomienzounámbito.Siexisteunconstructor,debeinvocarsecuandoelobjetosecrea.Sinembargo,sielconstruc-tortomaunoomásargumentos,¿cómosaberquesedisponedelainformacióndeinicializaciónalcomienzodelámbito?Generalmentenosedisponedeesainforma-ción.DadoqueCnotieneelconceptodeprivado,laseparaciónentredenicióneinicializaciónnoesunproblema.Además,C++garantizaquecuandosecreaunobjeto,esinicializadosimultáneamente.Estoaseguraquenosetendránobjetosnoinicializadosejecutándoseenelsistema.Cnotienecuidado,dehecho,Cpromueveestaprácticayaqueobligaaquesedenanlasvariablesalcomienzodeunbloque,antesdedisponerdelainformacióndeinicializaciónnecesaria2.Engeneral,C++nopermitecrearunobjetoantesdetenerlainformacióndeini-cializaciónparaelconstructor.Poreso,ellenguajenoseríafactiblesituvieraquedenirvariablesalcomienzodeunbloque.Dehecho,elestilodellenguajeparecepromoverladenicióndeunobjetotancercacomoseaposibledelpuntoenelqueseusa.EnC++,cualquierreglaqueseaplicaaun«objeto»automáticamentetambiénsereereaunobjetodeuntipobásico.Estosignicaquecualquierclasedeobjetoovariabledeuntipobásicotambiénsepuededenirencualquierpuntodelbloque.Esotambiénsignicaquepuedeesperarhastadisponerdelainformaciónparaunavariableantesdedenirla,demodoquesiemprepuededenireinicializaralmismotiempo:
//:C06:DefineInitialize.cpp
//Definingvariablesanywhere
#include"../require.h"
#include&#xiost;&#xream;
#include&#xstri;&#xng00;
usingnamespacestd;
classG{
inti;
public:
G(intii);
2C99,laversiónactualdelEstándardeC,permitedenirvariablesencualquierpuntodelbloque,comoC++
191
i
i
“Volumen1”—2012/1/12—13:52—page194—#232i
i
i
i
i
i
Capítulo6.Inicializaciónylimpieza
}///:~
Enelcódigoanterior,tantoelgotocomoelswitchpuedensaltarlasentenciaenlaqueseinvocaunconstructor.Eseobjetocorrespondealámbitoinclusosinoseinvocaelconstructor,demodoqueelcompiladordaráunmensajedeerror.Estogarantizadenuevoqueunobjetonosepuedecrearsinoseinicializa.Todoelespaciodealmacenamientonecesarioseasignaenlapila,porsupuesto.Eseespaciolofacilizaelcompiladormoviendoelpunterodepila«haciaabajo»(de-pendiendodelamáquinaimplicaincrementarodecrementarelvalordelpunterodepila).Losobjetostambiénsepuedenalojarenelmontículousandonew,algoqueseveráenelcapítulo13.(FIXME:RefC13)6.4.StashconconstructoresydestructoresLosejemplosdeloscapítulosanteriorestienenfuncionesquetienencorrespon-denciadirectaconconstructoresydestructores:initialize()ycleanup().ÉsteeselcherodecabeceradeStash,utilizandoconstructorydestructor:
//:C06:Stash2.h
//Withconstructors&destructors
#ifndefSTASH2_H
#defineSTASH2_H
classStash{
intsize;//Sizeofeachspace
intquantity;//Numberofstoragespaces
intnext;//Nextemptyspace
//Dynamicallyallocatedarrayofbytes:
unsignedchar*storage;
voidinflate(intincrease);
public:
Stash(intsize);
~Stash();
intadd(void*element);
void*fetch(intindex);
intcount();
};
#endif//STASH2_H///:~
Lasúnicasdenicionesdemétodosquehancambiadosoninitialize()yc-leanup(),quehansidoreemplazadasconunconstructoryundestructor.
//:C06:Stash2.cpp{O}
//Constructors&destructors
#include"Stash2.h"
#include"../require.h"
#include&#xiost;&#xream;
#includeÊss;rt0;
usingnamespacestd;
constintincrement=100;
Stash::Stash(intsz){
194
i
i
“Volumen1”—2012/1/12—13:52—page195—#233i
i
i
i
i
i
6.4.Stashconconstructoresydestructores
size=sz;
quantity=0;
storage=0;
next=0;
}
intStash::add(void*element){
if(next�=quantity)//Enoughspaceleft?
inflate(increment);
//Copyelementintostorage,
//startingatnextemptyspace:
intstartBytes=next*size;
unsignedchar*e=(unsignedchar*)element;
for(inti=0;isize;i++)
storage[startBytes+i]=e[i];
next++;
return(next-1);//Indexnumber
}
void*Stash::fetch(intindex){
require(0index,"Stash::fetch(-)index");
if(index�=next)
return0;//Toindicatetheend
//Producepointertodesiredelement:
return&(storage[index*size]);
}
intStash::count(){
returnnext;//NumberofelementsinCStash
}
voidStash::inflate(intincrease){
require(increase�0,
"Stash::inflatezeroornegativeincrease");
intnewQuantity=quantity+increase;
intnewBytes=newQuantity*size;
intoldBytes=quantity*size;
unsignedchar*b=newunsignedchar[newBytes];
for(inti=0;ioldBytes;i++)
b[i]=storage[i];//Copyoldtonew
delete[](storage);//Oldstorage
storage=b;//Pointtonewmemory
quantity=newQuantity;
}
Stash::~Stash(){
if(storage!=0){
cout"freeingstorage"endl;
delete[]storage;
}
}///:~
Puedeverquelasfuncionesderequire.hseusanparavigilarerroresdelpro-gramador,enlugardeassert().Lasalidadeunassert()fallidonoestanútilcomolasfuncionesderequire.h(queseveránmásadelanteenellibro).
195
i
i
“Volumen1”—2012/1/12—13:52—page196—#234i
i
i
i
i
i
Capítulo6.Inicializaciónylimpieza
Dadoqueinflate()esprivado,elúnicomodoenquerequire()podríafa-llarseríasiunodelosotrosmiembrospasaraaccidentalmenteunvalorincorrectoainflate().Siestásegurodequeesonopuedepasar,deberíaconsiderareliminarelrequire(),perodeberíatenerenmentequehastaquelaclaseseaestable,siem-preexistelaposibilidaddequeelcódigonuevoañadidoalaclasepodríaprovocarerrores.Elcostederequire()esbajo(ypodríasereliminadoautomáticamenteporelpreprocesador)mientrasquelarobustezdelcódigoesalta.FijesecómoenelsiguienteprogramadepruebaladenicióndelosobjetosSt-ashaparecejustoantesdenecesitarse,ycómolainicializaciónaparececomopartedeladenición,enlalistadeargumentosdelconstructor.
//:C06:Stash2Test.cpp
//{L}Stash2
//Constructors&destructors
#include"Stash2.h"
#include"../require.h"
#includestr;êm0;
#include&#xiost;&#xream;
#include&#xstri;&#xng00;
usingnamespacestd;
intmain(){
StashintStash(sizeof(int));
for(inti=0;i100;i++)
intStash.add(&i);
for(intj=0;jintStash.count();j++)
cout"intStash.fetch("j")="
*(int*)intStash.fetch(j)
endl;
constintbufsize=80;
StashstringStash(sizeof(char)*bufsize);
ifstreamin("Stash2Test.cpp");
assure(in,"Stash2Test.cpp");
stringline;
while(getline(in,line))
stringStash.add((char*)line.c_str());
intk=0;
char*cp;
while((cp=(char*)stringStash.fetch(k++))!=0)
cout"stringStash.fetch("k")="
cpendl;
}///:~
Tambiénobservequesehaneliminadollamadasacleanup(),perolosdes-tructoressellamanautomáticamentecuandointStashystringStashsalendelámbito.UnacosadelaquedebeserconscienteenlosejemplosconStash:Tengomu-chocuidadousandosólotiposbásicos;esdecir,aquellossindestructores.SiintentacopiarobjetosdentrodeStash,aparecerántodotipodeproblemasynofuncionarábien.EnrealidadlaLibreríaEstándardeC++puedehacercopiascorrectasdeobjetosensuscontenedores,peroesunprocesobastantesucioycomplicado.EnelsiguienteejemplodeStack,veráqueseutilizanpunterosparaesquivarestacuestión,yenuncapítuloposteriorStashtambiénseconvertiráparaqueusepunteros.
196
i
i
“Volumen1”—2012/1/12—13:52—page197—#235i
i
i
i
i
i
6.5.Stackconconstructoresydestructores
6.5.StackconconstructoresydestructoresReimplementarlalistaenlazada(dentrodeStack)conconstructoresydestruc-toresmuestraclaramentecómocostructoresydestructoresutilizannewydelete.Ésteeselcherodecabeceramodcado:
//:C06:Stack3.h
//Withconstructors/destructors
#ifndefSTACK3_H
#defineSTACK3_H
classStack{
structLink{
void*data;
Link*next;
Link(void*dat,Link*nxt);
~Link();
}*head;
public:
Stack();
~Stack();
voidpush(void*dat);
void*peek();
void*pop();
};
#endif//STACK3_H///:~
NosólohacequeStacktengaunconstructorydestructor,tambiénaparecelaclaseanidadaLink.
//:C06:Stack3.cpp{O}
//Constructors/destructors
#include"Stack3.h"
#include"../require.h"
usingnamespacestd;
Stack::Link::Link(void*dat,Link*nxt){
data=dat;
next=nxt;
}
Stack::Link::~Link(){}
Stack::Stack(){head=0;}
voidStack::push(void*dat){
head=newLink(dat,head);
}
void*Stack::peek(){
require(head!=0,"Stackempty");
returnhead�-data;
}
void*Stack::pop(){
if(head==0)return0;
197
i
i
“Volumen1”—2012/1/12—13:52—page200—#238i
i
i
i
i
i
Capítulo6.Inicializaciónylimpieza
laposibilidaddeintroducirerroresdurantelamodicación.Pero,¿cómodeterminareltamañodelarray?Laexpresiónsizeofc/sizeof*c(eltamañodelarraycompletodivididoentreeltamañodelprimerelemento)esuntrucoquehacequenoseanecesariocambiarlosicambiaeltamañodelarray6:
for(inti=0;isizeofc/sizeof*c;i++)
c[i]++;
Dadoquelasestructurastambiénsonagregados,sepuedeninicializardeunmo-dosimilar.Comoenunaestructuraestilo-Ctodossusmiembrossonpúblicos,sepuedenasignardirectamente:
structX{
inti;
floatf;
charc;
};
Xx1={1,2.2,'c'};
Sitieneunaarraydeesosobjetos,puedeinicializarlosusandounconjuntoanida-dodellavesparacadaelemento:
Xx2[3]={{1,1.1,'a'},{2,2.2,'b'}};
Aquí,eltercerobjetoseinicializóacero.Sialgunodelosatributosesprivado(algoqueocurretípicamenteenelcasodeclasesbiendiseñadasenC++),oinclusositodossonpúblicosperohayuncons-tructor,lascosassondistintas.Enelejemploanterior,losvaloressehanasignadodirectamentealoselementosdelagregado,perolosconstructoressonunamaneradeforzarquelainicializaciónocurrapormediodeunainterfazformal.Aquí,losconstructoresdebenserinvocadospararealizarlainicialización.Demodo,quesitieneunconstructorparecidoaéste,
structY{
floatf;
inti;
Y(inta);
};
Debeindicarlallamadaalconstructor.Lamejoraproximaciónesunaexplícitacomolasiguiente:
Yy1[]={Y(1),Y(2),Y(3)};
Obtendrátresobjetosytresllamadasalconstructor.Siemprequetengauncons-tructor,siesunaestructuracontodossusmiembrospúblicosounaclaseconatri-butosprivados,todalainicializacióndebeocurriratravésdelconstructor,inclusosiestáusandolainicializacióndeagregados.
6Enelsegundovolumendeestelibro(disponiblelibrementeenwww.BruceEckel.com),veráunaformamáscortadecalculareltamañodeunarrayusandoplantillas.
200
i
i
“Volumen1”—2012/1/12—13:52—page201—#239i
i
i
i
i
i
6.7.Constructorespordefecto
Semuestraunsegundoejemploconunconstructorconmúltiplesargumentos.
//:C06:Multiarg.cpp
//Multipleconstructorarguments
//withaggregateinitialization
#include&#xiost;&#xream;
usingnamespacestd;
classZ{
inti,j;
public:
Z(intii,intjj);
voidprint();
};
Z::Z(intii,intjj){
i=ii;
j=jj;
}
voidZ::print(){
cout"i="i",j="jendl;
}
intmain(){
Zzz[]={Z(1,2),Z(3,4),Z(5,6),Z(7,8)};
for(inti=0;isizeofzz/sizeof*zz;i++)
zz[i].print();
}///:~
Fíjeseencómoseinvocaunconstructorexplícitoparacadaobjetodeunarray.6.7.ConstructorespordefectoUnconstructorpordefectoesunoquepuedeserinvocadosinargumentos.Unconstructorpordefectoseusaparacrearun«objetovainilla»7perotambiénesim-portantecuandoelcompiladordebecrearunobjetoperonosedandetalles.Porejemplo,sisetomalastructYdenidapreviamenteyseusaenunadenicióncomoésta,
Yy2[2]={Y(1)};
elcompiladorsequejaráporquenopuedeencontrarunconstructorpordefecto.Elsegundoobjetodelarraysecrearásinargumentos,yesahídondeelcompila-dorbuscaunconstructorpordefecto.Dehecho,sisimplementedeneunarraydeobjetosY,
Yy3[7];
elcompiladorsequejaráporquedeberíahaberunconstructorparainicializarcadaobjetodelarray.
7N.de.T:ParalosanglosajonesVainillaeselsabormás«sencillo»,sinadornosnisosticaciones.
201
i
i
“Volumen1”—2012/1/12—13:52—page202—#240i
i
i
i
i
i
Capítulo6.Inicializaciónylimpieza
Elmismoproblemaocurresicreaunobjetoindividualcomoéste:
Yy4;
Recuerde,sitieneunconstructor,elcompiladoraseguraquesiempreocurrirálaconstrucción,sintenerencuentalasituación.Elconstructorpordefectoestanimportantequesi(ysólosi)unaestructura(st-ructoclase)notieneconstructor,elcompiladorcrearáunoautomáticamente.Porello,losiguientefunciona:
//:C06:AutoDefaultConstructor.cpp
//Automatically-generateddefaultconstructor
classV{
inti;//private
};//Noconstructor
intmain(){
Vv,v2[10];
}///:~
Sisehandenidoconstructores,peronohayconstructorpordefecto,lasinstan-ciasanterioresdeVprovocaránerroresdurantelacompilación.Podríapensarsequeelconstructorsintetizadoporelcompiladordeberíahaceralgunainicializacióninteligente,comoponeracerolamemoriadelobjeto.Peronolohace-añadiríaunasobrecargaquequedaríafueradelcontroldelprogramador.Siquierequelamemoriaseainicializadaacero,deberíahacerloescribiendouncons-tructorpordefectoexplícito.Aunqueelcompiladorcrearáunconstructorpordefecto,elcomportamientodeeseconstructorraramenteharáloqueseespera.Deberíaconsiderarestacaracterís-ticacomounareddeseguridad,peroquedebeusarseconmoderación.Engeneral,deberíadenirsusconstructoresexplicitamenteynopermitirqueelcompiladorlohagaporusted.6.8.ResumenLosmecanismosaparentementeelaboradosproporcionadosporC++deberíandarleunaideadelaimportanciacríticaquetieneenellenguajelainicializaciónylimpieza.ComoStroustrupfuequiendiseñoC++,unadelasprimerasobservacio-nesquehizosobrelaproductividaddeCfuequeunaparteimportantedelosproble-masdeprogramaciónsedebenalainicializacióninapropiadadelasvariables.Estetipodeerroressondifícilesdeencontrar,yotrotantosepuededecirdeunalimpie-zainapropiada.Dadoqueconstructoresydestructoreslepermitengarantizarunainicializaciónylimpiezaapropiada(elcompiladornopermitiráqueunobjetoseacreadoodestruidosinlainvocacióndelconstructorydestructorcorrespondiente),conseguirácontrolyseguridad.Lainicializacióndeagregadosestáincluidadeunmodosimilar-previenedeerroresdeinicializacióntípicosconagregadosdetiposbásicosyhacequeelcódigoseamáscorto.
202
i
i
“Volumen1”—2012/1/12—13:52—page207—#245i
i
i
i
i
i
7.1.Másdecoracióndenombres
ningúnestándardedecoración,podráobtenerresultadosdiferentesdeuncompi-ladoraotro.(Puedeverloquesaldríadiciéndolealcompiladorquegenerecódigofuenteenensamblador).Esto,porsupuesto,causaproblemassideseacomprarunaslibreríascompiladasporuncompiladoryenlazadorparticulares,aunquesiladeco-racióndenombresfueraestándar,habríaotrosobstáculosdebidoalasdiferenciasdegeneracióndecódigomáquinaentrecompiladores.Estoestodoloquehayparalasobrecargadefunciones:puedeutilizarelmismonombredefunciónsiempreycuandolalistadeargumentosseadiferente.Elcompi-ladorutilizaelnombre,elámbitoylalistadeargumentosparagenerarunnombreinternoqueelenlazadorpuedautilizar.7.1.1.SobrecargaenelvalorderetornoEsmuycomúnlapregunta«¿Porquésolamenteelámbitoylalistadeargumen-tos?¿Porquénotambiénelvalorderetorno?».Aprimeravistaparecequetendríasentidoutilizartambiénelvalorderetornoparaladecoracióndelnombreinterno.Deestamanera,tambiénpodríasobrecargarconlosvaloresderetorno:
voidf();
intf();
Estofuncionabiencuandoelcompiladorpuededeterminarsinambigüedadesaquétipodevalorderetornosereere,comoenintx=f();.Noobstante,enCsepuedellamaraunafunciónyhacercasoomisodelvalorderetorno(estoes,puedequererllamaralafuncióndebidoasusefectoslaterales).¿Cómopuedeelcompiladordistinguiraquéfunciónsereereenestecaso?Peoresladicultadquetieneellectordelcódigofuenteparadilucidaraquéfunciónsereere.Lasobrecargamedianteelvalorderetornosolamenteesdemasiadosutil,porloqueC++nolopermite.7.1.2.EnlaceconFIXME:tipossegurosExisteunbenecioañadidoaladecoracióndenombres.EnChayunproblemaparticularmentefastidiosocuandounprogramadorclientedeclaramalunafuncióno,aúnpeor,sellamaaunafunciónsinhabersidopreviamentedeclarada,yelcompi-ladorinereladeclaracióndelafunciónmediantelaformaenquesellama.Algunasvecesladeclaracióndelafunciónescorrecta,perocuandonoloes,sueleresultarenunfallodifícildeencontrar.AcausadequeenC++sedebendeclarartodaslasfuncionesantesdellamarlas,lasprobabilidadesdequeocurraloanteriormenteexpuestosereducendrásticamen-te.ElcompiladordeC++rechazadeclararunafunciónautomáticamente,asíqueesprobablequetengaqueincluirlacabeceraapropiada.Sinembargo,siporalgunarazónselasapañaparadeclararmalunafunción,odeclararlaamanooincluirunacabeceraincorrecta(quizáunaqueseaantigua),ladecoracióndenombrespropor-cionaunaseguridadqueamenudosedenominacomoenlacecontiposseguros.Considereelsiguienteescenario.Enuncheroestáladenicióndeunafunción:
//:C07:Def.cpp{O}
//Functiondefinition
voidf(int){}
///:~
207
i
i
“Volumen1”—2012/1/12—13:52—page210—#248i
i
i
i
i
i
Capítulo7.Sobrecargadefuncionesyargumentospordefecto
intStash::count(){
returnnext;//NumberofelementsinCStash
}
voidStash::inflate(intincrease){
assert(increase�=0);
if(increase==0)return;
intnewQuantity=quantity+increase;
intnewBytes=newQuantity*size;
intoldBytes=quantity*size;
unsignedchar*b=newunsignedchar[newBytes];
for(inti=0;ioldBytes;i++)
b[i]=storage[i];//Copyoldtonew
delete[](storage);//Releaseoldstorage
storage=b;//Pointtonewmemory
quantity=newQuantity;//Adjustthesize
}///:~
Cuandoutilizaelprimerconstructornoseasignamemoriaalgunaparastora-ge.Laasignaciónocurrelaprimeravezquetratadeañadir(conadd())unobjetoyencualquiermomentoenelqueelbloquedememoriaactualseexcedaenadd().Ambosconstructoressepruebanenesteprogramadeejemplo:
//:C07:Stash3Test.cpp
//{L}Stash3
//Functionoverloading
#include"Stash3.h"
#include"../require.h"
#includestr;êm0;
#include&#xiost;&#xream;
#include&#xstri;&#xng00;
usingnamespacestd;
intmain(){
StashintStash(sizeof(int));
for(inti=0;i100;i++)
intStash.add(&i);
for(intj=0;jintStash.count();j++)
cout"intStash.fetch("j")="
*(int*)intStash.fetch(j)
endl;
constintbufsize=80;
StashstringStash(sizeof(char)*bufsize,100);
ifstreamin("Stash3Test.cpp");
assure(in,"Stash3Test.cpp");
stringline;
while(getline(in,line))
stringStash.add((char*)line.c_str());
intk=0;
char*cp;
while((cp=(char*)stringStash.fetch(k++))!=0)
cout"stringStash.fetch("k")="
cpendl;
}///:~
210
i
i
“Volumen1”—2012/1/12—13:52—page212—#250i
i
i
i
i
i
Capítulo7.Sobrecargadefuncionesyargumentospordefecto
elementoequivocadounavezquelauniónsehainicializado.Enelejemploante-rior,podríaescribirX.read_float()inclusoaunqueseainapropiado.Sinembar-go,unaunión«segura»sepuedeencapsularenunaclase.Enelsiguienteejemplo,veacómolaenumeraciónclaricaelcódigo,ycómolasobrecargavienecomoanilloaldedoconlosconstructores:
//:C07:SuperVar.cpp
//Asuper-variable
#include&#xiost;&#xream;
usingnamespacestd;
classSuperVar{
enum{
character,
integer,
floating_point
}vartype;//Defineone
union{//Anonymousunion
charc;
inti;
floatf;
};
public:
SuperVar(charch);
SuperVar(intii);
SuperVar(floatff);
voidprint();
};
SuperVar::SuperVar(charch){
vartype=character;
c=ch;
}
SuperVar::SuperVar(intii){
vartype=integer;
i=ii;
}
SuperVar::SuperVar(floatff){
vartype=floating_point;
f=ff;
}
voidSuperVar::print(){
switch(vartype){
casecharacter:
cout"character:"cendl;
break;
caseinteger:
cout"integer:"iendl;
break;
casefloating_point:
cout"float:"fendl;
break;
}
}
212
i
i
“Volumen1”—2012/1/12—13:52—page216—#254i
i
i
i
i
i
Capítulo7.Sobrecargadefuncionesyargumentospordefecto
ElobjetoMemcontieneunbloquedeoctetosyseaseguradequetienesucientememoria.Elconstructorpordefectonoreservamemoriaperoelsegundoconstruc-torseaseguradequehayszoctetosdememoriaenelobjetoMem.Eldestructorliberalamemoria,msize()ledicecuántosoctetoshayactualmenteenMemypointer()retornaunpunteroalprincipiodelamemoriareservada(Memesunaherramientaabastantebajonivel).Hayunaversiónsobrecargadadepointer()quelosprogra-madoresclientespuedenutilizarparaobtenerunpunteroqueapuntaaunbloquedememoriaconalmenoseltamañominSize,yelmétodoloasegura.Elconstructoryelmétodopointer()utilizanelmétodoprivadoensureMin-Size()paraincrementareltamañodelbloquedememoria(notequenoesseguromantenerelvalorderetornodepointer()sisecambiaeltamañodelbloquedememoria).Heaquílaimplementacióndelaclase:
//:C07:Mem.cpp{O}
#include"Mem.h"
#include str;&#xing0;
usingnamespacestd;
Mem::Mem(){mem=0;size=0;}
Mem::Mem(intsz){
mem=0;
size=0;
ensureMinSize(sz);
}
Mem::~Mem(){delete[]mem;}
intMem::msize(){returnsize;}
voidMem::ensureMinSize(intminSize){
if(sizeminSize){
byte*newmem=newbyte[minSize];
memset(newmem+size,0,minSize-size);
memcpy(newmem,mem,size);
delete[]mem;
mem=newmem;
size=minSize;
}
}
byte*Mem::pointer(){returnmem;}
byte*Mem::pointer(intminSize){
ensureMinSize(minSize);
returnmem;
}///:~
PuedeobservarqueensureMinSize()eslaúnicafunciónresponsabledere-servarmemoriayquelautilizantantoelsegundoconstructorcomolasegundaver-siónsobrecargadadepointer().DentrodeensureSize()nosehacenadasieltamañoeslosucientementegrande.Sisehadereservarmásmemoriaparaqueelbloqueseamásgrande(queeselmismocasocuandoelbloquetienetamañocero
216
i
i
“Volumen1”—2012/1/12—13:52—page217—#255i
i
i
i
i
i
7.5.Elecciónentresobrecargayargumentospordefecto
despuésdelconstructorpordefecto),lanuevaporcióndemásseponeaceroutili-zandolafuncióndelalibreríaestándardeCmemset(),quefuepresentadaenelCapítulo5.LasiguientellamadaesalafuncióndelalibreríaestándardeCmemc-py(),queenestecasocopialosoctetosexistentesdememanewmem(normalmentedeunamaneraecaz).Finalmente,seliberalamemoriaantiguayseasignanalosatributosapropiadoslanuevamemoriaysutamaño.LaclaseMemsehadiseñadoparasuutilizacióncomoherramientadentrodeotrasclasesparasimplicarsugestióndelamemoria(tambiénsepodríautilizarparaocultarunsistemadegestióndememoriamásavanzadaproporcionado,porejem-plo,porelelsistemaoperativo).Estaclasesecompruebaaquíconunasimpleclasedetipostring:
//:C07:MemTest.cpp
//TestingtheMemclass
//{L}Mem
#include"Mem.h"
#include str;&#xing0;
#include&#xiost;&#xream;
usingnamespacestd;
classMyString{
Mem*buf;
public:
MyString();
MyString(char*str);
~MyString();
voidconcat(char*str);
voidprint(ostream&os);
};
MyString::MyString(){buf=0;}
MyString::MyString(char*str){
buf=newMem(strlen(str)+1);
strcpy((char*)buf�-pointer(),str);
}
voidMyString::concat(char*str){
if(!buf)buf=newMem;
strcat((char*)buf�-pointer(
buf�-msize()+strlen(str)+1),str);
}
voidMyString::print(ostream&os){
if(!buf)return;
osbuf&#x-600;-pointer()endl;
}
MyString::~MyString(){deletebuf;}
intmain(){
MyStrings("Myteststring");
s.print(cout);
s.concat("someadditionalstuff");
s.print(cout);
MyStrings2;
s2.concat("Usingdefaultconstructor");
217
i
i
“Volumen1”—2012/1/12—13:52—page218—#256i
i
i
i
i
i
Capítulo7.Sobrecargadefuncionesyargumentospordefecto
s2.print(cout);
}///:~
TodoloquepuedehacerconestaclaseescrearunMyString,concatenartextoeimprimiraunostream.LaclasesólocontieneunpunteroaunMem,peronoteladiferenciaentreelconstructorpordefecto,queponeelpunteroacero,yelse-gundoconstructor,quecreaunMemycopialosdatosdentrodelmismo.Laventajadelconstructorpordefectoesquepuedecrear,porejemplo,unarraygrandedeob-jetosMyStringvacíosconpocosrecursos,pueseltamañodecadaobjetoessólounpunteroylaúnicasobrecargaenelrendimientodelconstructorpordefectoeseldeasignarloacero.ElcostedeunMyStringsóloempiezaaaumentarcuandoconcatenadatos;enesemomentoelobjetoMemsecreasinohasidocreadotodavía.Sinembargo,siutilizaelconstructorpordefectoynuncaconcatenaningúndato,lallamadaaldestructortodavíaesseguraporquecuandosellamaadeleteconunpunteroacero,elcompiladornohacenadaparanocausarproblemas.Simiralosdosconstructores,enprincipio,podríaparecerquesoncandidatospa-rautilizarargumentospordefecto.Sinembargo,sieliminaelconstructorpordefectoyescribeelconstructorquequedaconunargumentopordefecto:
MyString(char*str="");
todofuncionarácorrectamente,peroperderálaecaciaanteriorpuessiempresecrearáelobjetoMem.Paravolveratenerlamismaecaciadeantes,hademodicarelconstructor:
MyString::MyString(char*str){
if(!*str){//Apuntaaunstringívaco
buf=0;
return;
}
buf=newMem(strlen(str)+1);
strcpy((char*)buf�-pointer(),str);
}
Estosignica,enefecto,queelvalorpordefectoesuncasoquehadetratarsese-paradamentedeunvalorquenoloes.Aunqueparecealgoinocenteconunpequeñoconstructorcomoéste,engeneralestaprácticapuedecausarproblemas.Sitienequetratarporseparadoelvalorpordefectoenvezdetratarlocomounvalorordinario,deberíaserunapistaparaquealnalseimplementendosfuncionesdiferentesden-trodeunafunción:unaversiónparaelcasonormalyotraparaelcasopordefecto.Podríapartirloendoscuerposdefuncióndiferentesydejarqueelcompiladorelija.Estoresultaenunligero(peronormalmenteinvisible)incrementodelaecaciapor-queelargumentoextranosepasayportantoelcódigoextradebidoalacondicióncondiciónnoseejecuta.Másimportanteesqueestámanteniendoelcódigoendosfuncionesseparadasenvezdecombinarlasenunautilizandoargumentosporde-fecto,loqueresultaráenunmantenimientomássencillo,sobretodosilasfuncionessonlargas.Porotrolado,considerelaclaseMem.Simiralasdenicionesdelosdosconstruc-toresylasdosfuncionespointer(),puedeverquelautilizacióndeargumentospordefectoenamboscasosnocausaráquelosmétodoscambien.Así,laclasepodríaserfácilmente:
218
i
i
“Volumen1”—2012/1/12—13:52—page219—#257i
i
i
i
i
i
7.6.Resumen
//:C07:Mem2.h
#ifndefMEM2_H
#defineMEM2_H
typedefunsignedcharbyte;
classMem{
byte*mem;
intsize;
voidensureMinSize(intminSize);
public:
Mem(intsz=0);
~Mem();
intmsize();
byte*pointer(intminSize=0);
};
#endif//MEM2_H///:~
NotequelallamadaaensureMinSize(0)siempreserábastanteeciente.Aunqueamboscasossebasanendecisionespormotivosdeecacia,debetenercuidadoparanocaerenlatrampadepensarsóloenlaecacia(siemprefascinante).Lomásimportanteeneldiseñodeunaclaseeslainterfazdelaclase(susmiembrospúblicos,quesonlasqueelprogramadorclientetieneasudisposición).Siseimple-mentaunaclasefácildeutilizaryreutilizar,entonceshatenidoéxito;siemprepuederealizarajustesparamejorarlaecaciaencasonecesario,peroelefectodeunaclasemaldiseñadaporqueelprogramadorestáobsesionadoconlaecaciapuederesul-targrave.Suprimerapreocupacióndeberíaserquelainterfaztengasentidoparaaquéllosquelautilicenyparalosqueleanelcódigo.NotequeenMemTest.cppelusodeMyStringnocambiaindependientementedesiseutilizaelconstructorpordefectoosilaecaciaesbuenaomala.7.6.ResumenComonorma,nodeberíautilizarargumentospordefectosihayqueincluirunacondiciónenelcódigo.Envezdeesodeberíapartirlafunciónendosomásfun-cionessobrecargadassipuede.Unargumentopordefectodeberíaserunvalorquenormalmentepondríaahí.Eselvalorqueesmásprobablequeocurra,paraquelosprogramadoresclientespuedanhacercasoomisodeélosólolopongancuandonoquieranutilizarelvalorpordefecto.Elargumentopordefectoseincluyeparahacermásfácileslasllamadasafunción,especialmentecuandoesasfuncionestienemuchosargumentosconvalorestípicos.Nosóloesmuchomássencilloescribirlasllamadas,sinoqueademássonmássen-cillasdeleer,especialmentesielcreadordelaclaseordenalosargumentosdetalmaneraqueaquéllosquemenoscambianseponenalnaldeltodo.Unautilizaciónespecialmenteimportantedelosargumentospordefectoescuan-doempiezaconunafunciónconunconjuntodeargumentos,ydespuésdeutilizarlaporuntiemposedacuentaquenecesitaañadirmásargumentos.Siponelosnuevosargumentoscomopordefecto,seaseguradequenoserompeelcódigoclientequeutilizalainterfazanterior.
219
i
i
“Volumen1”—2012/1/12—13:52—page220—#258i
i
i
i
i
i
Capítulo7.Sobrecargadefuncionesyargumentospordefecto
7.7.EjerciciosLassolucionesalosejerciciossepuedenencontrareneldocumentoelectróni-cotitulado«TheThinkinginC++AnnotatedSolutionGuide»,disponibleporpocodineroenwww.BruceEckel.com.1.CreeunaclaseTextquecontengaunobjetostringparaqueguardeeltextodeunchero.Póngaledosconstructores:unconstructorpordefectoyuncons-tructorquetomeunargumentodetipostringqueseaelnombredelcheroquesevayaaabrir.Cuandoseutiliceelsegundoconstructor,abraelcheroypongasucontenidoenelatributostring.Añadaunmétodollamadocon-tents()queretorneelstringparaque,porejemplo,sepuedaimprimir.Enmain()abrauncheroutilizandoTexteimprimaelcontenidoenpantalla.2.CreeunaclaseMessageconunconstructorquetomeunsólostringconunvalorpordefecto.Creeunatributoprivadostringyasigneenelconstructorelargumentostringalatributostring.Creedosmétodossobrecargadosllamadosprint():unoquenotomeargumentosyqueimprimasimplementeelmensa-jeguardadoenelobjeto,yelotroquetomeunargumentostring,queimprimaelmensajeinternoademásdelargumento.¿Tienesentidoutilizarestaaproxi-maciónenvezdelautilizadaporelconstructor?3.Descubracómogenerarcódigoensambladorconsucompiladoryhagaexpe-rimentosparadeducirelesquemadedecoracióndenombres.4.Creeunaclasequecontengacuatrométodoscon0,1,2y3argumentosdetipointrespectivamente.Creeunmain()quehagaunobjetodesuclaseyllameacadamétodo.Ahoramodiquelaclaseparaquetengasólounmétodocontodoslosargumentospordefecto.¿Esocambiasumain()?5.Creeunafuncióncondosargumentosyllámeladesdemain().Ahorahagaqueunodelosargumentosseaunargumentoderelleno(sinidenticador)ycompruebesinecesitahacercambiosenmain().6.ModiqueStash3.hyStash3.cppparaqueelconstructorutiliceargumen-tospordefecto.PruebeelconstructorhaciendodosversionesdiferentesdeunobjetoStash.7.CreeunanuevaversióndelaclaseStack(delCapítulo6)quecontengaelconstructorpordefectoaligualqueantes,yunsegundoconstructorquetomecomoargumentosunarraydepunterosaobjetosyeltamañodelarray.Esteconstructordeberíarecorrerelarrayyponercadapunteroenlapila(Stack).Pruebesuclaseconunarraydestring's.8.ModiqueSuperVarparaquehaya#ifdef'squeenglobenelcódigodev-artypetalcomosedescribeenlasecciónsobreenumeraciones.Cambieva-rtypecomounaenumeraciónpública(sinejemplares)ymodiqueprint()paraquerequieraunargumentodetipovartypequeleindiquequétienequéhacer.9.ImplementeMem2.hyasegúresedequelaclasemodicadatodavíafuncionaconMemTest.cpp.10.UtilicelaclaseMemparaimplementarStash.Notequedebidoaquelaim-plementaciónesprivadayportantoocultaalprogramadorcliente,nonecesitamodicarelcódigodeprueba.
220
i
i
“Volumen1”—2012/1/12—13:52—page222—#260i
i
i
i
i
i
i
i
“Volumen1”—2012/1/12—13:52—page223—#261i
i
i
i
i
i
8:ConstantesElconceptodeconstante(expresiónconlapalabrareservadaco-nst)secreóparapermitiralosprogramadoresmarcarladiferenciaentreloquepuedecambiaryloqueno.Estofacilitaelcontrolylaseguridadenunproyectodeprogramación.Desdesuorigen,consthasidoutilizadaparadiferentespropósitos.MientrastantoFIXME:ittrickledbackenellenguajeCenelquesusignicadocambió.Todoestopuedeparecerunpococonfusoalprincipio,yenestecapítuloaprenderácuán-do,porquéycómousarlapalabrareservadaconst.Haciaelnalseexponeunadisertaciónsobrevolatile,queesfamiliadeconst(ambossereerenaloscambios)ysusintaxisesidéntica.Elprimermotivoparalacreacióndeconstparecequefueeliminarelusodeladirectivadelpreprocesador#defineparasustitucióndevalores.Desdeentoncesseusaparapunteros,argumentosdefunciones,tiposderetorno,objetosyfuncionesmiembro.Todosellostienenpequeñasdiferenciasperosusignicadoesconceptual-mentecompatible.Setrataránenlassiguientesseccionesdeestecapítulo.8.1.SustitucióndevaloresCuandoseprogramaenC,seusalibrementeelpreprocesadorparacrearmacrosysustituirvalores.Elpreprocesadorsimplementehaceunreemplazotextualynorealizaningunacomprobacióndetipo.Porello,lasustitucióndevaloresintroducepequeñosproblemasquesepuedenevitarusandovaloresconstantes.Elusomásfrecuentedelpreprocesadoreslasustitucióndevalorespornombres,enCesalgocomo:
#defineBUFSIZE100
BUFSIZEesunnombrequesóloexisteduranteelpreprocesado.Portanto,noocupamemoriaysepuedecolocarenuncherodecabeceraparaofrecerunvalorúnicoatodaslasunidadesqueloutilicen.Esmuyimportanteparaelmantenimientodelcódigoelusodesustitucióndevaloresenlugardelostambiénllamados«núme-rosmágicos».Siusanúmerosmágicosensucódigo.nosolamenteimpediráallectorconocersuprocedenciaosignicadosinoquecomplicaráinnecesariamentelaedi-cióndelcódigosinecesitacambiardichovalor.Lamayorpartedeltiempo,BUFSIZEsecomportarácomounvalorordinario,peronosiempre.Notieneinformacióndetipo.Esopuedeescondererroresdifícilesdelocalizar.C++utilizaconstparaeliminarestosproblemasllevandolasustitución
223
i
i
“Volumen1”—2012/1/12—13:52—page225—#263i
i
i
i
i
i
8.1.Sustitucióndevalores
dealmacenamientoendiferentesmódulos.Entonces,elenlazadorveríalamismadeniciónenmúltiplesarchivosobjeto,loquecausaríaunerrorenelenlace.Comolasconstantesutilizanenlaceinterno,elenlazadornointentaenlazaresasdenicio-nesatravésdelosmódulos,yasínohaycolisiones.Conlostiposbásicos,quesonlosseveninvolucradosenlamayoríadeloscasos,elcompiladorsiemprerealizapropagacióndeconstantes.8.1.2.constantessegurasElusodelasconstantesnoestálimitadoalasustitucióndelos#defineporexpresionesconstantes.Siinicializaunavariableconunvalorqueseproduceentiempodeejecuciónysabequenocambiarádurantelavidadelavariable,esunabuenaprácticadeprogramaciónhacerlaconstanteparaquedeesemodoelcom-piladorproduzcaunmensajedeerrorsiaccidentalmentealguienintentamodicardichavariable.Aquíhayunejemplo:
//:C08:Safecons.cpp
//Usingconstforsafety
#include&#xiost;&#xream;
usingnamespacestd;
constinti=100;//Typicalconstant
constintj=i+10;//Valuefromconstexpr
longaddress=(long)&j;//Forcesstorage
charbuf[j+10];//Stillaconstexpression
intmain(){
cout"typeacharacter&CR:";
constcharc=cin.get();//Can'tchange
constcharc2=c+'a';
coutc2;
//...
}///:~
Puedeverqueiesunaconstanteentiempodecompilación,perojsecalculaapartirdei.Sinembargo,comoiesunaconstante,elvalorcalculadoparajesunaexpresiónconstanteyesensimismootraconstanteentiempodecompilación.Enlasiguientelíneasenecesitaladireccióndejyporlotantoelcompiladorseveobligadoapediralmacenamientoparaj.Nisiquieraesoimpideelusodejparadeterminareltamañodebufporqueelcompiladorsabequejesunaconstanteyquesuvaloresválidoaunqueseasignealmacenamiento,yaqueesosehaceparamantenerelvalorenalgúnpuntoenelprograma.Enmain(),apareceuntipodiferentedeconstanteenelidenticadorc,porqueelvalornopuedeserconocidoentiempodecompilación.Esosignicaqueserequierealmacenamiento,yporesoelcompiladornointentamantenernadaenlatabladesímbolos(elmismocomportamientoqueenC).Lainicializacióndebeocurrir,aúnasí,enelpuntodeladenición,yunavezqueocurrelainicialización,elvaloryanopuedesercambiado.Puedeverquec2secalculaapartirdecyademáslasreglasdeámbitofuncionanparalasconstantesigualqueparacualquierotrotipo,otraventajarespectoalusode#define.Enlapráctica,sipiensaqueunavariablenodeberíacambiar,deberíahacerquefueseunaconstante.Estonosólodaseguridadcontracambiosinadvertidos,también
225
i
i
“Volumen1”—2012/1/12—13:52—page226—#264i
i
i
i
i
i
Capítulo8.Constantes
permitealcompiladorgenerarcódigomásecienteahorrandoespaciodealmacena-mientoylecturasdememoriaenlaejecucióndelprograma.8.1.3.VectoresEsposibleusarconstantesparalosvectores,peroprácticamenteestádandoporhechoqueelcompiladornoserálosucientementesosticadoparamantenerunvectorenlatabladesímbolos,asíqueleasignaráespaciodealmacenamiento.Enestassituaciones,constsignica«unconjuntodedatosenmemoriaquenopuedenmodicarse».Encualquiercaso,susvaloresnopuedeusarseentiempodecompila-ciónporqueelcompiladornoconoceenesemomentoloscontenidosdelasvariablesquetienenespacioasignado.Enelcódigosiguientepuedeveralgunasdeclaracionesincorrectas.
//:C08:Constag.cpp
//Constantsandaggregates
constinti[]={1,2,3,4};
//!floatf[i[3]];//Illegal
structS{inti,j;};
constSs[]={{1,2},{3,4}};
//!doubled[s[1].j];//Illegal
intmain(){}///:~
Enladenicióndeunvector,elcompiladordebesercapazdegenerarcódigoquemuevaelpunterodepilaparadarcabidaalvector.Enlasdenicionesinco-rrectasanteriores,elcompiladorsequejaporquenopuedeencontrarunaexpresiónconstanteenladenicióndeltamañodelvector.8.1.4.DiferenciasconCLasconstantesseintrodujeronenlasprimerasversionesdeC++mientraslaes-pecicacióndelestándarCestabasiendoterminada.AunqueelcomitéacargodeCdecidióentoncesincluirconstenC,poralgunarazón,vinoasignicarparaellos«unavariableordinariaquenopuedecambiarse».EnC,unaconstantesiempreocu-paespaciodealmacenamientoysuámbitoesglobal.ElcompiladorCnopuedetratarconstcomounaconstanteentiempodecompilación.EnC,siescribe:
constintbufsize=100;
charbuf[bufsize];
apareceráunerror,aunqueparezcaalgorazonable.bufsizeestáguardadoenalgúnsitioyelcompiladornoconocesuvalorentiempodecompilación.Opcional-mentepuedeescribir:
constintbufsize;
enC,peronoenC++,yelcompiladorCloaceptacomounadeclaraciónqueindicaquesealmacenaráenalgunaparte.ComoCutilizaenlaceexternoparalasconstantes,esasemánticatienesentido.C++utilizanormalmenteenlaceinterno,asíque,siquierehacerlomismoenC++,debeindicarexpresamentequeseuseenlaceexternousandoextern.
226
i
i
“Volumen1”—2012/1/12—13:52—page228—#266i
i
i
i
i
i
Capítulo8.Constantes
8.2.PunterosLospunterospuedenserconstantes.Elcompiladorpondrámásesfuerzoaúnparaevitarelalmacenamientoyhacerexpansióndeconstantescuandosetratadepunterosconstantes,peroestascaracterísticasparecenmenosútilesenestecaso.Lomásimportanteesqueelcompiladorleavisarásiintentacambiarunpunterocons-tante,loquerepresentaunbuenelementodeseguridad.Cuandoseusaconstconpunterostienedosopciones:sepuedenaplicaraloqueapuntaelpunterooalapro-piadirecciónalmacenadaenelpuntero.Lasintaxisesunpococonfusaalprincipioperosevuelvecómodoconlapráctica.8.2.1.PunteroaconstanteEltrucoconladenicióndeunpuntero,aligualqueconunadenicióncom-plicada,esleerlaempezandoporelidenticadoreiranalizandoladeniciónhaciaafuera.Elespecicadorconstestáligadoalacosa«máscercana».Asíquesisequiereimpedircambiosenelelementoapuntado,escribeunadeniciónparecidaaesta:
constint*u;
Empezandoporelidenticador,selee«uesunpuntero,queapuntaaunenteroconstante».Enestecasonoserequiereinicializaciónporqueestádiciendoqueupue-deapuntaracualquiercosa(esdecir,noesconstante),perolacosaalaqueapuntanopuedecambiar.Ahoravienelaparteconfusa.Podríapensarquehacerelpunteroinalterableensimismo,esdecir,impedircualquiercambioenladirecciónquecontieneu,estansimplecomomoverlapalabraconstalotroladodelapalabraint:
intconst*v;
ypensarqueestodeberíaleerse«vesunpunteroconstanteaunentero».Sinem-bargo,laformadeleerloes«vesunpunteroordinarioaunenteroqueesconstante».Esdecir,lapalabraconstsereeredenuevoalenteroyelefectoeselmismoqueenladeniciónprevia.Elhechodequeestasdenicionesseanequivalentesescon-fuso,paraevitarestaconfusiónporpartedellectordelcódigo,deberíaceñirsealaprimeraforma.8.2.2.PunteroconstanteParaconseguirqueelpunteroseainalterable,debecolocarelespecicadorcon-staladerechadel*:
intd=1;
int*constw=&d;
Ahora,selee«wesunpunteroconstate,yapuntaaunentero».Comoelpunteroensíesahoraunaconstante,elcompiladorobligaadarleunvalorinicialquenopodráalterarsedurantelavidadelpuntero.Encualquiercaso,puedecambiarelvalordeloqueapuntaelpunteroconalgocomo:
228
i
i
“Volumen1”—2012/1/12—13:52—page234—#272i
i
i
i
i
i
Capítulo8.Constantes
Aunquesonaceptablesparaelcompilador,enrealidadsonproblemáticas.f5()devuelveunobjetodeclaseX,yparaqueelcompiladorpuedasatisfacerlasexpresio-nesanterioresdebecrearuntemporarioparaalbergarelvalorderetorno.Demodoqueenambasexpresioneselobjetotemporariosemodicaytanprontocomolaexpresiónesevaluadaeltemporarioseelimina.Comoresultado,lasmodicacionessepierden,asíqueprobablementeestecódigoeserróneo,aunqueelcompiladornodiganadaalrespecto.Lasexpresionescomoéstassonsucientementesimplescomoparadetectarelproblema,perocuandolascosassonmáscomplejasloserroressonmásdifícilesdelocalizar.Laformadepreservarlaconstanciadelosobjetossemuestramásadelanteenestecapítulo.8.3.3.PasoyretornodedireccionesSipasaoretornaunadirección(yaseaunpunteroounareferencia),elprograma-dorclientepuederecogerymodicarelvaloralqueapunta.Sihacequeelpunterooreferenciaseaconstante,impediráqueestosuceda,loquepuedeahorrarleproble-mas.Dehecho,cadavezquesepasaunadireccióncomoparámetroaunafunción,deberíahacerlaconstantesiemprequeseaposible.Sinolohace,estáexcluyendolaposibilidaddeusarlafunciónconconstantes.Laopcióndedevolverunpunterooreferenciaconstantedependedeloquequie-rapermitirhaceralprogramadorcliente.Aquísemuestraunejemploquedemuestraelusodepunterosconstantescomoargumentosdefuncionesyvaloresderetorno.
//:C08:ConstPointer.cpp
//Constantpointerarg/return
voidt(int*){}
voidu(constint*cip){
//!*cip=2;//Illegal--modifiesvalue
inti=*cip;//OK--copiesvalue
//!int*ip2=cip;//Illegal:non-const
}
constchar*v(){
//Returnsaddressofstaticcharacterarray:
return"resultoffunctionv()";
}
constint*constw(){
staticinti;
return&i;
}
intmain(){
intx=0;
int*ip=&x;
constint*cip=&x;
t(ip);//OK
//!t(cip);//NotOK
u(ip);//OK
u(cip);//AlsoOK
//!char*cp=v();//NotOK
234
i
i
“Volumen1”—2012/1/12—13:52—page235—#273i
i
i
i
i
i
8.3.Argumentosdefuncionesyvaloresderetorno
constchar*ccp=v();//OK
//!int*ip2=w();//NotOK
constint*constccip=w();//OK
constint*cip2=w();//OK
//!*w()=1;//NotOK
}///:~
Lafunciónt()tomaunpunterono-constanteordinariocomoargumento,yu()tomaunpunteroconstante.Enelcuerpodeu()puedeverunintentodemodicarelvalordeunpunteroconstante,algoincorrecto,peropuedecopiarsuvalorenunavariablenoconstante.Elcompiladortambiénimpidecrearunpunteronoconstanteyalmacenarenélladireccióncontenidaenunpunteroconstante.Lasfuncionesv()yw()pruebanlassemánticasderetornodevalores.v()de-vuelveunconstchar*quesecreaapartirdeunliteraldecadena.Estasentenciaenrealidadgeneraladireccióndelliteralunavezqueelcompiladorlocreayalmace-naenáreadealmacenamientoestática.Comosehadichoantes,técnicamenteestevectordecaracteresesunaconstante,comobienindicaeltipoderetornodev().Elvalorderetornodew()requierequetantoelpunterocomoloqueapuntaseanconstantes.Comoenv(),elvalordevueltoporw()esvalidounavezterminadalafunciónsoloporqueesestático.Nuncadebedevolverunpunteroaunavariablelocalpuessealmacenanenlapilayalterminarlafunciónlosdatosdelapiladesaparecen.Loquesipuedehaceresdevolverpunterosqueapuntanadatosalmacenadosenelmontón(heap),puessiguensiendovalidosdespuésdeterminarlafunción.Enmain()sepruebanlasfuncionesconvariosargumentos.Puedeverquet()aceptarácomoargumentounpunteroordinario,perosiintentapasarleunpunteroaunaconstante,nohaygarantíadequenovayaamodicarseelvalordelavariableapuntada;porelloelcompiladorloindicaconunmensajedeerror.u()tomaunpunteroaconstante,asíquepuedeaceptarlosdostiposdeargumentos.Poresounafunciónqueaceptaunpunteroaconstanteesmásgeneralqueunaqueaceptaunpunteroordinario.Comoeslógico,elvalorderetornodev()sólosepuedeasignaraunpunteroaconstante.Tambiéneradeesperarqueelcompiladorrehuseasignarelvalordevuel-toporw()aunpunteroordinario,yquesíacepteunconstint*const,peropodríasorprenderunpocoquetambiénaceptaunconstint*,quenoesexactamenteeltipoderetornodeclaradoenlafunción.Denuevo,comoelvalor(queesladireccióncon-tenidaenelpuntero)secopia,elrequisitodequelavariableoriginalpermanezcainalterablesecumpleautomáticamente.Poreso,elsegundoconstenladeclara-ciónconstint*constsóloseaplicacuandolousecomorecipiente,encuyocasoelcompiladorloimpediría.CriteriodepasodeargumentosEnCesmuycomúnelpasoporvalor,ycuandosequierepasarunadirecciónlaúnicaposibilidadesusarunpuntero3.Sinembargo,ningunodeestosmodoseselpreferidoenC++.Ensulugar,laprimeraopcióncuandosepasaunparámetroeshacerloporreferenciaomejoraún,porreferenciaconstante.Paraelclientedelafunción,lasintaxisesidénticaqueenelpasoporvalor,deesemodonohaycon-fusiónposibleconlospunteros,nohayquepensarentérminosdepunteros.Para
3AlgunosautoresdicenquetodoenCsepasaporvalor,yaquecuandosepasaunpunterosehacetambiénunacopia(demodoqueelpunterosepasaporvalor).Encualquiercaso,hacerestaprecisiónpuede,enrealidad,confundirlacuestión.
235
i
i
“Volumen1”—2012/1/12—13:52—page236—#274i
i
i
i
i
i
Capítulo8.Constantes
elcreadordeunafunción,pasarunadirecciónessiempremásecientequepasarunobjetocompleto,ysipasaporreferenciaconstantesignicaquelafunciónnopodrácambiarloalmacenadoenesadirección,asíqueelefectodesdeelpuntodevistadelprogramadorclienteeslomismoqueelpasoporvalor(sinembargoesmáseciente).Acausadelasintaxisdelasreferencias(paraelclienteesigualqueelpasoporvalor)esposiblepasarunobjetotemporarioaunafunciónquetomaunareferenciaconstante,mientrasquenuncapuedepasarseunobjetotemporarioaunafunciónquetomaunpuntero(conunpuntero,ladireccióndebedarseexplícitamente).AsíqueconelpasoporreferenciaseproduceunanuevasituaciónquenuncaocurreenC:untemporario,queessiempreconstante,puedepasarsudirecciónaunafunción(unafunciónpuedetomarporargumentoladireccióndeuntemporario).Estoesasíporque,parapermitirquelostemporariossepasenporreferencia,elargumentodebeserunareferenciaconstante.Elsiguienteejemplolodemuestra:
//:C08:ConstTemporary.cpp
//Temporariesareconst
classX{};
Xf(){returnX();}//Returnbyvalue
voidg1(X&){}//Passbynon-constreference
voidg2(constX&){}//Passbyconstreference
intmain(){
//Error:consttemporarycreatedbyf():
//!g1(f());
//OK:g2takesaconstreference:
g2(f());
}///:~
f()retornaunobjetodelaclaseXporvalor.Estosignicaquecuandotomeelvalorderetornoylopaseinmediatamenteaotrafuncióncomoenlasllamadasag1()yg2(),secreauntemporarioylostemporariossonsiempreconstantes.Poreso,lallamadaag1()esunerrorpuesg1()noaceptaunareferenciaconstante,mientrasquelallamadaag2()síescorrecta.8.4.ClasesEstasecciónmuestralaformaenlaquesepuedeusarelespecicadorconstconlasclases.Puedeserinteresantecrearunaconstantelocalaunaclaseparausarlaenexpresionesconstantesqueseránevaluadasentiempodecompilación.Sinembargo,elsignicadodelespecicadorconstesdiferenteparalasclases4,demodoquedebecomprenderlasopcionesadecuadasparacrearmiembrosconstantesenunaclase.Tambiénsepuedehacerqueunobjetocompletoseaconstante(ycomosehavis-to,elcompiladorsiemprehaceconstanteslosobjetostemporarios).Peropreservarlaconsistenciadeunobjetoconstanteesmáscomplicado.Elcompiladorpuedeasegu-rarlaconsistenciadelasvariablesdelostiposdellenguajeperonopuedevigilarla
4N.delT.:Estoseconocecomopolisemiadellenguaje
236
i
i
“Volumen1”—2012/1/12—13:52—page243—#281i
i
i
i
i
i
8.4.Clases
noseanconstpordefectoresultadesafortunado).Unmétodoquenomodicanin-gúnatributosedeberíaescribircomoconstanteyasísepodríausardesdeobjetosconstantes.Aquísemuestraunejemploquecomparamétodosconstymétodosordinarios:
//:C08:Quoter.cpp
//Randomquoteselection
#include&#xiost;&#xream;
#include std;&#xlib0;//Randomnumbergenerator
#include tim;//Toseedrandomgenerator
usingnamespacestd;
classQuoter{
intlastquote;
public:
Quoter();
intlastQuote()const;
constchar*quote();
};
Quoter::Quoter(){
lastquote=-1;
srand(time(0));//Seedrandomnumbergenerator
}
intQuoter::lastQuote()const{
returnlastquote;
}
constchar*Quoter::quote(){
staticconstchar*quotes[]={
"Arewehavingfunyet?",
"Doctorsalwaysknowbest",
"Isit...Atomic?",
"Fearisobscene",
"Thereisnoscientificevidence"
"tosupporttheidea"
"thatlifeisserious",
"Thingsthatmakeushappy,makeuswise",
};
constintqsize=sizeofquotes/sizeof*quotes;
intqnum=rand()%qsize;
while(lastquote�=0&&qnum==lastquote)
qnum=rand()%qsize;
returnquotes[lastquote=qnum];
}
intmain(){
Quoterq;
constQuotercq;
cq.lastQuote();//OK
//!cq.quote();//NotOK;nonconstfunction
for(inti=0;i20;i++)
coutq.quote()endl;
}///:~
243
i
i
“Volumen1”—2012/1/12—13:52—page244—#282i
i
i
i
i
i
Capítulo8.Constantes
Nilosconstructoresnilosdestructorespuedensermétodosconstantesporqueprácticamentesiemprerealizanalgunamodicaciónenelobjetodurantelainiciali-zaciónolaterminación.Elmiembroquote()tampocopuedeserconstanteporquemodicaelatributolastquote(verlasentenciaderetorno).Porotrapartelas-tQuote()nohacemodicacionesyporesopuedeserconstypuedeserllamadodeformaseguraporelobjetoconstantecq.mutable:constanciabinariavs.lógica¿Quéocurresiquierecrearunmétodoconstante,peronecesitacambiaralgúnatributodelobjeto?Estoseaplicaavecesaladiferenciaentreconstantebinaria(bit-wise)yconstantelógica(llamadotambiénconstantememberwise).Constantebinariasignicaquetodoslosbitsdelobjetosonpermanentes,asíquelaimagenbinariadelobjetonuncacambia.Constantelógicasignicaque,aunqueelobjetocompletoesconceptualmenteconstantepuedehabercambiosaniveldemiembro.Siseinformaalcompiladorqueunobjetoesconstante,cuidarácelosamenteelobjetoparaasegu-rarconstanciabinaria.Paraconseguirconstancialógica,haydosformasdecambiarlosatributosconunmétodoconstante.Laprimerasolucióneslatradicionalysellamaconstanciacastingaway.Estosehacedeunmodobastanteraro.Setomathis(lapalabraqueinidicaladireccióndelobjetoactual)ysemoldeaelpunteroaunpunteroaobjetodelaclaseactual.Parecequethisyaesunpunteroválido.Sinembargo,dentrodeunmétodoconstante,th-isesenrealidadunpunteroconstante,asíquemoldeándoloaunpunteroordinarioseeliminalaconstanciadelobjetoparaestaoperación.Aquíhayunejemplo:
//:C08:Castaway.cpp
//"Castingaway"constness
classY{
inti;
public:
Y();
voidf()const;
};
Y::Y(){i=0;}
voidY::f()const{
//!i++;//Error--constmemberfunction
((Y*)this�)-i++;//OK:castawayconst-ness
//Better:useC++explicitcastsyntax:
(const_castY*�(this�))-i++;
}
intmain(){
constYyy;
yy.f();//Actuallychangesit!
}///:~
Estaaproximaciónfuncionaypuedeverseencódigocorrecto,peronoeslatéc-nicaideal.Elproblemaesqueestafaltadeconstanciaestáocultaenladenicióndeunmétodoynohayningúnindicioenlainterfazdelaclasequehagasospecharqueesedatosemodicaamenosquepuedeaccedersealcódigofuente(buscandoelmolde).Paraponertodoaldescubiertosedebeusarlapalabramutableenlade-
244
i
i
“Volumen1”—2012/1/12—13:52—page245—#283i
i
i
i
i
i
8.5.Volatile
claracióndelaclaseparaindicarqueunatributodeterminadosepuedecambiaraúnperteneciendoaunobjetoconstante.
//:C08:Mutable.cpp
//The"mutable"keyword
classZ{
inti;
mutableintj;
public:
Z();
voidf()const;
};
Z::Z():i(0),j(0){}
voidZ::f()const{
//!i++;//Error--constmemberfunction
j++;//OK:mutable
}
intmain(){
constZzz;
zz.f();//Actuallychangesit!
}///:~
Deestemodoelusuariodelaclasepuedeverenladeclaraciónquémiembrostienenposibilidaddesermodicadosporunmétodo.ROMabilitySiunobjetosedenecomoconstanteesuncandidatoparaseralmacenadoenmemoriadesólolectura(ROM),queamenudoesunaconsideraciónimportanteenprogramacióndesistemasempotrados.Paraconseguirlonoessucienteconqueelobjetoseaconstante,losrequisitossonmuchamásestrictos.Porsupuesto,elobjetodebeserunaconstantebinaria.Esoesfácildecomprobarsilaconstancialógicaseimplementamedianteelusodemutable,peroprobablementeelcompiladornopodrádetectarlosiseutilizalatécnicadelmoldeadodentrodeunmétodoconstante.Además:
Laclaseoestructuranopuedetenerconstructoresodestructordenidosporelusuario.
Nopuedenserclasesbase(capitulo14)uobjetosmiembroconconstructoresodestructordenidosporelusuario.ElefectodeunaoperacióndeescrituraenunapartedelobjetoconstantedeuntipoROMablenoestádenido.AunqueunobjetopuedasercolocadoenROMdeformaconveniente,notodoslorequieren.8.5.VolatileLasintaxisdevolatileesidénticaaladeconst,perovolatilesignica«estedatopuedecambiarsinqueelcompiladorseainformadodeello».Dealgún
245
i
i
“Volumen1”—2012/1/12—13:52—page246—#284i
i
i
i
i
i
Capítulo8.Constantes
modo,elentornomodicaeldato(posiblementemediantemultitarea,multihiloointerrupciones),yvolatileindicalacompiladorquenohagasuposicionessobreeldato,especialmentedurantelaoptimización.Sielcompiladordice,«yoguardéestedatoenunregistroanteriormente,ynohetocadoeseregistro»,normalmentenonecesitaráleereldatodenuevodesdememo-ria.Perosiesavariableesvolatile,elcompiladornodebehaceresasuposiciónporqueeldatopuedehabercambiadoacausadeotroproceso,ydebereleerelda-toenvezdeoptimizarelcódigo(dichaoptimizaciónconsisteeneliminarlalecturaredundantequesehacenormalmente).Puedencrearseobjetosvolatileusandolamismasintaxisqueseusaparacrearobjetosconstantes.Tambiénpuedecrearseobjetosvolatileconstantesquenopue-dencambiarseporelprogramadorclienteperosepuedenmodicarporunaentidadajenaalprograma.Aquísemuestraunejemploquerepresentaunaclaseasociadaconalgúnelementofísicodecomunicación.
//:C08:Volatile.cpp
//Thevolatilekeyword
classComm{
constvolatileunsignedcharbyte;
volatileunsignedcharflag;
enum{bufsize=100};
unsignedcharbuf[bufsize];
intindex;
public:
Comm();
voidisr()volatile;
charread(intindex)const;
};
Comm::Comm():index(0),byte(0),flag(0){}
//Onlyademo;won'tactuallywork
//asaninterruptserviceroutine:
voidComm::isr()volatile{
flag=0;
buf[index++]=byte;
//Wraptobeginningofbuffer:
if(index�=bufsize)index=0;
}
charComm::read(intindex)const{
if(index0||index&#x-600;=bufsize)
return0;
returnbuf[index];
}
intmain(){
volatileCommPort;
Port.isr();//OK
//!Port.read(0);//Error,read()notvolatile
}///:~
Comoocurreconconst,sepuedeusarvolatileparalosatributosdelaclase,
246
i
i
“Volumen1”—2012/1/12—13:52—page250—#288i
i
i
i
i
i
i
i
“Volumen1”—2012/1/12—13:52—page251—#289i
i
i
i
i
i
9:FuncionesinlineUnadelascaracterísticasmásimportantesqueC++heredadeCeslaeciencia.SilaecienciadeC++fuesedramáticamentemenorqueladeC,podríahaberuncontingentesignicativodeprogramadoresquenopodríanjusticarsuuso.EnC,unadelasmanerasdepreservarlaecienciaesmedianteelusodemacros,loquepermitehacerloquepareceunallamadaaunafunciónsinlasobrecargaha-bitualdelallamadaafunción.Lamacroestáimplementadaconelpreprocesadorenvezdelpropiocompilador,yelpreprocesadorreemplazatodaslasllamadasama-crosdirectamenteconelcódigodelamacro,demaneraquenohayquecomplicarsepasandoargumentos,escribiendocódigodeensambladorparaCALL,retornandoar-gumentosniimplementandocódigoensambladorparaelRETURN.Todoeltrabajolorealizarelpreprocesador,demaneraquesetienelacoherenciaylegibilidaddeunallamadaaunafunciónperosinningúncoste.HaydosproblemasrespectoalusodelpreprocesadorconmacrosenC++.Lapri-meratambiénexisteenC:unamacropareceunallamadaafunción,peronosiempreactúacomotal.Estopuedeacarreardicultadesparaencontrarerrores.ElsegundoproblemaesespecícodeC++:elpreprocesadornotienepermisosparaaccederalainformacióndelosmiembrosdeunaclase.Estosignicaquelasmacrosdeprepro-cesadornopuedenusarsecomométodosdeunaclase.Paramantenerlaecienciadelusodelpreprocesadorconmacrosperoañadiendolaseguridadylasemánticadeámbitodeverdaderasfuncionesenlasclases.C++tienelasfuncionesinline.EnestecapítuloveremoslosproblemasdelusodelasmarosdepreprocesadorenC++,cómoseresuelvenestosproblemasconfuncionesinline,ylasdirectriceseincursionesenlaformaenquetrabajanlasfuncionesinline.9.1.LospeligrosdelpreprocesadorLaclavedelosproblemasconlasmacrosdepreprocesadorradicaenquepuedescaerenelerrordepensarqueelcomportamientodelpreprocesadoresigualqueeldelcompilador.Porsupuesto,laintencióneraqueunamacroseparezcayactúecomounallamadaaunafunción,poresoesbastantefácilcaerenesteerror.Lasdicultadescomienzancuandolasdiferenciasaparecensubyacentes.Consideremosunejemplosencillo:
#defineF(x)(x+1)
251
i
i
“Volumen1”—2012/1/12—13:52—page257—#295i
i
i
i
i
i
9.2.Funcionesinline
intx=A.read();
}///:~
Aquí,elusuariodelaclasenuncatienecontactodirectoconlasvariablesdees-tadointernasalaclase,ypuedenmantenersecomoprivadas,bajoelcontroldeldiseñadordelaclase.Todoelaccesoalosatributossepuedecontrolaratravésdelosmétodosdelainterfaz.Además,elaccesoesnotablementeeciente.Conside-reread(),porejemplo.Sininline,elcódigogeneradoparalallamadaaread()podríaincluircolocarlaenlapilayejecutarlallamadaCALLdeensamblador.Enlamayoríadelasarquitecturas,eltamañodeesecódigoseríamayorqueelcódigocreadoparalavarianteinline,yeltiempodeejecuciónseríamayorcontodacerteza.Sinlasfuncionesinline,undiseñadordeclasespreocupadoporlaecienciaes-taríatentadodehacerqueifueseunatributopúblico,eliminadolasobrecargaypermitiendoalusuarioaccederdirectamenteai.Desdeelpuntodevistadeldiseña-dor,esoresultadesastroso,iseríapartedelainterfazpública,locualsignicaqueeldiseñadordelaclasenopodrácambiarloenelfuturo.Tendráquecargarconunenterollamadoi.Estoesunproblemaporquedespuéspuedequeconsideremejorusarunoatenlugardeunintpararepresentarelestado,perocomoiespartedelainterfazpública,nopodrácambiarlo.Opuedequenecesiterealizaralgúncálculoadicionalcomopartedelalecturaoescrituradei,quenopodráhacersiespúblico.Si,porelcontrario,siempreusamétodosparaleerycambiarlainformacióndees-tadodelobjeto,podrámodicarlarepresentaciónsubyacentedelobjetohastaestartotalmenteconvencido.Además,elusodemétodosparacontrolaratributoslepermiteañadircódigoalmétodoparadetectarcuandocambiaelvalor,algoquepuedesermuyútilduranteladepuración.Siunatributoespúblico,cualquierapuedecambiarloencualquiermomentosinqueelprogramadorlosepa.AccesoresymutadoresHaygentequedivideelconceptodefuncionesdeaccesoendos:accesores(paraleerlainformacióndeestadodeunobjeto)ymutadores(paracambiarelestadodeunobjeto).Además,sepuedeutilizarlasobrecargadefuncionesparatenermétodosaccesoresymutadoresconelmismonombre;elmodoenqueseinvoqueelmétododeterminasiseleeomodicalainformacióndeestado.Así,
//:C09:Rectangle.cpp
//Accessors&mutators
classRectangle{
intwide,high;
public:
Rectangle(intw=0,inth=0)
:wide(w),high(h){}
intwidth()const{returnwide;}//Read
voidwidth(intw){wide=w;}//Set
intheight()const{returnhigh;}//Read
voidheight(inth){high=h;}//Set
};
intmain(){
Rectangler(19,47);
//Changewidth&height:
257
i
i
“Volumen1”—2012/1/12—13:52—page258—#296i
i
i
i
i
i
Capítulo9.Funcionesinline
r.height(2*r.width());
r.width(2*r.height());
}///:~
Elconstructorusalalistadeinicialización(brevementeintroducidaenelcapítulo8yampliamentecubiertaenelcapitulo14)paraasignarvaloresawideyhigh(usandoelformatodepseudo-constructorparalostiposdedatosbásicos).Nopuededenirmétodosquetenganelmismonombrequelosatributos,demo-doquepuedequesesientatentadodedistinguirlosconunguiónbajoalnal.Sinembargo,losidenticadoresconguionesbajosnalesestánreservadosyelprogra-madornodeberíausarlos.Ensulugar,deberíausar«set»y«get»paraindicarquelosmétodossonaccesoresymutadores.
//:C09:Rectangle2.cpp
//Accessors&mutatorswith"get"and"set"
classRectangle{
intwidth,height;
public:
Rectangle(intw=0,inth=0)
:width(w),height(h){}
intgetWidth()const{returnwidth;}
voidsetWidth(intw){width=w;}
intgetHeight()const{returnheight;}
voidsetHeight(inth){height=h;}
};
intmain(){
Rectangler(19,47);
//Changewidth&height:
r.setHeight(2*r.getWidth());
r.setWidth(2*r.getHeight());
}///:~
Porsupuesto,losaccesoresymutadoresnotienenporquésersimplestuberíashacialasvariablesinternas.Aveces,puedenefectuarcálculosmássosticados.ElsiguienteejemplousalasfuncionesdetiempodelalibreríaCestándarparacrearunaclaseTime:
//:C09:Cpptime.h
//Asimpletimeclass
#ifndefCPPTIME_H
#defineCPPTIME_H
#include tim;
#include str;&#xing0;
classTime{
std::time_tt;
std::tmlocal;
charasciiRep[26];
unsignedcharlflag,aflag;
voidupdateLocal(){
258
i
i
“Volumen1”—2012/1/12—13:52—page259—#297i
i
i
i
i
i
9.2.Funcionesinline
if(!lflag){
local=*std::localtime(&t);
lflag++;
}
}
voidupdateAscii(){
if(!aflag){
updateLocal();
std::strcpy(asciiRep,std::asctime(&local));
aflag++;
}
}
public:
Time(){mark();}
voidmark(){
lflag=aflag=0;
std::time(&t);
}
constchar*ascii(){
updateAscii();
returnasciiRep;
}
//Differenceinseconds:
intdelta(Time*dt)const{
returnint(std::difftime(t,dt�-t));
}
intdaylightSavings(){
updateLocal();
returnlocal.tm_isdst;
}
intdayOfYear(){//SinceJanuary1
updateLocal();
returnlocal.tm_yday;
}
intdayOfWeek(){//SinceSunday
updateLocal();
returnlocal.tm_wday;
}
intsince1900(){//Yearssince1900
updateLocal();
returnlocal.tm_year;
}
intmonth(){//SinceJanuary
updateLocal();
returnlocal.tm_mon;
}
intdayOfMonth(){
updateLocal();
returnlocal.tm_mday;
}
inthour(){//Sincemidnight,24-hourclock
updateLocal();
returnlocal.tm_hour;
}
intminute(){
updateLocal();
returnlocal.tm_min;
}
259
i
i
“Volumen1”—2012/1/12—13:52—page260—#298i
i
i
i
i
i
Capítulo9.Funcionesinline
intsecond(){
updateLocal();
returnlocal.tm_sec;
}
};
#endif//CPPTIME_H///:~
LasfuncionesdelalibreríaCestándartienenmúltiplesrepresentacionesparaeltiempo,ytodasellassonpartedelaclaseTime.Sinembargo,noesnecesarioactualizartodosellos,asíquetime_tseusaparalarepresentaciónbase,ytmlo-calylarepresentaciónASCIIasciiReptienenbanderasparaindicarsihansidoactualizadasparaeltime_tactual.LasdosfuncionesprivadasupdateLocal()yupdateAscii()compruebanlasbanderasycondicionalmentehacenlaactualiza-ción.Elconstructorllamaalafunciónmark()(queelusuariopuedellamartambiénparaforzaralobjetoarepresentareltiempoactual),yesolimpialasdosbanderasparaindicarqueeltiempolocalylarepresentaciónASCIIyanosonválidas.Lafunciónascii()llamaaupdateAscii(),quecopiaelresultadodelafuncióndelalibreríaestándardeCasctime()enunbufferlocalporqueasctime()usaunaáreadedatosestáticaquesesobreescribesilafunciónsellamaenotraparte.Elvalorderetornodelafunciónascii()esladireccióndeesebufferlocal.TodaslasfuncionesqueempiezancondaylightSavings()usanlafunciónupdateLocal(),quecausaquelacomposiciónresultantedeinlinesseabastantelarga.Noparecequevalgalapena,especialmenteconsiderandoqueprobablementenoquierallamarmuchoaesasfunciones.Sinembargo,esonosignicaquetodaslasfuncionesdebanserno-inline.Sihaceotrasfuncionesno-inline,almenosmantengaupdateLocal()comoinlinedemodoquesucódigosedupliqueenlasfuncionesno-inline,eliminandolasobrecargaextradeinvocacióndefunciones.Esteesunpequeñoprogramadeprueba:
//:C09:Cpptime.cpp
//Testingasimpletimeclass
#include"Cpptime.h"
#include&#xiost;&#xream;
usingnamespacestd;
intmain(){
Timestart;
for(inti=1;i1000;i++){
couti'';
if(i%10==0)coutendl;
}
Timeend;
coutendl;
cout"start="start.ascii();
cout"end="end.ascii();
cout"delta="end.delta(&start);
}///:~
SecreaunobjetoTime,sehacealgunaactividadqueconsumatiempo,despuéssecreaunsegundoobjetoTimeparamarcareltiempodenalización.Seusanparamostrarlostiemposdeinicio,nylosintervalos.
260
i
i
“Volumen1”—2012/1/12—13:52—page261—#299i
i
i
i
i
i
9.3.StashyStackconinlines
9.3.StashyStackconinlinesDisponiendodeinlines,podemosmodicarlasclasesStashyStackparaha-cerlasmásecientes.
//:C09:Stash4.h
//Inlinefunctions
#ifndefSTASH4_H
#defineSTASH4_H
#include"../require.h"
classStash{
intsize;//Sizeofeachspace
intquantity;//Numberofstoragespaces
intnext;//Nextemptyspace
//Dynamicallyallocatedarrayofbytes:
unsignedchar*storage;
voidinflate(intincrease);
public:
Stash(intsz):size(sz),quantity(0),
next(0),storage(0){}
Stash(intsz,intinitQuantity):size(sz),
quantity(0),next(0),storage(0){
inflate(initQuantity);
}
Stash::~Stash(){
if(storage!=0)
delete[]storage;
}
intadd(void*element);
void*fetch(intindex)const{
require(0index,"Stash::fetch(-)index");
if(index�=next)
return0;//Toindicatetheend
//Producepointertodesiredelement:
return&(storage[index*size]);
}
intcount()const{returnnext;}
};
#endif//STASH4_H///:~
Obviamentelasfuncionespequeñasfuncionanbiencomoinlines,peronotequelasdosfuncionesmáslargassiguensiendono-inline,dadoqueconvertirlasainlinenorepresentaríaningunamejoraderendimiento.
//:C09:Stash4.cpp{O}
#include"Stash4.h"
#include&#xiost;&#xream;
#includeÊss;rt0;
usingnamespacestd;
constintincrement=100;
intStash::add(void*element){
if(next�=quantity)//Enoughspaceleft?
inflate(increment);
//Copyelementintostorage,
261
i
i
“Volumen1”—2012/1/12—13:52—page264—#302i
i
i
i
i
i
Capítulo9.Funcionesinline
//:C09:Stack4Test.cpp
//{T}Stack4Test.cpp
#include"Stack4.h"
#include"../require.h"
#includestr;êm0;
#include&#xiost;&#xream;
#include&#xstri;&#xng00;
usingnamespacestd;
intmain(intargc,char*argv[]){
requireArgs(argc,1);//Filenameisargument
ifstreamin(argv[1]);
assure(in,argv[1]);
Stacktextlines;
stringline;
//Readfileandstorelinesinthestack:
while(getline(in,line))
textlines.push(newstring(line));
//Popthelinesfromthestackandprintthem:
string*s;
while((s=(string*)textlines.pop())!=0){
cout*sendl;
deletes;
}
}///:~
Lagenteescribeavecesclasescontodassusfuncionesinline,asíquelaclasecompletaestáenelcherodecabecera(veráenestelibroqueyomismolohago).Duranteeldesarrollodeunprogramaprobablementeestoesinofensivo,aunqueavecespuedehacerquelascompilacionesseanmáslentas.Cuandoelprogramaseestabilizaunpoco,probablementequerrávolverahacerlasfuncionesno-inlinedondeseaconveniente.9.4.FuncionesinlineyelcompiladorParacomprendercuandoesconvenienteutilizarinlines,esútilsaberloquehaceelcompiladorcuandoencuentraunafuncióninline.Comoconcualquierfunción,elcompiladorapuntaeltipodelafunciónessutabladesímbolos(esdecir,elprototipodelafunciónincluyendoelnombreylostiposdelosargumentos,encombinaciónconvalorderetorno).Ademáscuandoelcompiladorvequelafunciónesinlineyelcuerponocontieneerrores,elcódigosecolocatambiénenlatabladesímbolos.Elcó-digosealmacenaensuformafuente,comoinstruccionesensambladorcompiladas,oalgunaotrarepresentaciónpropiadelcompilador.Cuandohaceunallamadaaunafuncióninline,elcompiladorseaseguraprime-rodequelallamadasepuedehacercorrectamente.Esdecir,lostiposdetodoslosargumentoscorrespondenexactamenteconlostiposdelalistadeargumentosdelafunción(oconvertibleatipocorrecto)yelvalorderetornotieneeltipocorrecto(oesconvertiblealtipocorrecto)enlaexpresióndestino.Esto,porsupuesto,esexacta-mentelomismoquehaceelcompiladorparacualquierfunciónyhayunadiferenciaconsiderablerespectodeloquehaceelpreprocesador,porqueelpreprocesadornocompruebatiposnihaceconversiones.
264
i
i
“Volumen1”—2012/1/12—13:52—page265—#303i
i
i
i
i
i
9.4.Funcionesinlineyelcompilador
Sitodalainformacióndeltipodelafunciónencajaenelcontextodelallama-da,entonceslallamadaalafunciónsesustituyedirectamenteporelcódigoinline,eliminandolasobrecargaypermitiendoqueelcompiladorpuedahacermásoptimi-zaciones.Además,sielinlineesunmétodo,ladireccióndelobjeto(this)seponeenellugarapropiado,quees,porsupuesto,otraacciónqueelpreprocesadoresincapazdehacer.9.4.1.LimitacionesHaydossituacionesenqueelcompiladornopuedeefectuarlasustitucióndeinli-ne.Enestoscasos,simplementeconviertelafunciónalaformaordinariatomandoladeniciónypidiendoespacioparalafuncióncomohaceconunafunciónno-inline.Sidebehacerloenvariasunidadesdetraducción(loquenormalmentecausaríaunerrordedeniciónmúltiple),informaalenlazadorqueignoreesasdenicionesmúl-tiples.Encompiladornopuedeefectuarlasustitucióndeinlinesilafunciónesdemasia-docomplicada.Estodependedecadacompiladorparticular,peroaunquemuchoscompiladoreslohagan,nohabráningunamejoradeeciencia.Engeneral,seconsi-deraquecualquiertipodebucleesdemasiadocomplicadoparaexpandircomounainline,ysilopiensa,elbucleimplicamuchomástiempoqueelqueconllevalasobre-cargadelainvocacióndelafunción.Silafunciónessimplementeunacolecciónsesentenciassimples,probablementeelcompiladornotendráningúnproblemaparautilizarinline,perosihaymuchassentencias,lasobrecargadellamadaserámuchomenorqueelcostedeejecutarelcuerpo.Yrecuerde,cadavezquellameaunafun-cióninlinegrande,elcuerpocompletoseinsertaenellugardelallamada,demodoqueeltamañodelcódigoseinaráfácilmentesinquesepercibaningunamejoraderendimiento.(Notequealgunosdelosejemplosdeestelibropuedenexcedereltamañorazonableparaunainlineacambiodemejorarlaestéticadeloslistados.Elcompiladortampocoefectúasustitucionesinlinesiladireccióndelafunciónsetomaimplícitaoexplícitamente.Sielcompiladordebeproducirunadirección,entoncestendráquealojarelcódigodelafunciónyusarladirecciónresultante.Sinembargo,cuandonoserequiereunadirección,probablementeelcompiladorharálasustitucióninline.Esimportantecomprenderqueunadeclaracióninlineessólounasugerenciaalcompilador;elcompiladornoestáforzadoahacernada.Unbuencompiladorharásustitucionesinlineparafuncionespequeñasysimplesmientrasqueignorarálasqueseandemasiadocomplicadas.Esoledaráloqueespera-laauténticasemánticadeunallamadaafunciónconlaecienciadeunamacro.9.4.2.ReferenciasadelantadasSiestáimaginandoqueelcompilador[FIXME:isdoingtoimplementinlines],puedeconfundirsepensandoquehaymáslimitacionesquelasqueexistenrealmen-te.Enconcreto,siunainlinehaceunareferenciaadelantaaunafunciónquenohasidodeclaradaaúnenlaclase(seainlineono),puedeparecerqueelcompiladornosabrátratarla.
//:C09:EvaluationOrder.cpp
//Inlineevaluationorder
classForward{
265
i
i
“Volumen1”—2012/1/12—13:52—page267—#305i
i
i
i
i
i
9.5.Reducireldesorden
}///:~
ElconstructorparaMemberessucientementesimpleparaserinline,dadoquenohaynadaespecialenél-ningunaherenciauobjetomiembroestáprovocandoactividadesocultasadicionales.PeroenlaclaseWithMembershaymásdeloqueseveasimplevista.Losconstructoresydestructoresparalosatributosq,ryssellamanautomáticamente,yesosconstructoresydestructorestambiénsoninline,asíqueladiferenciaessignicativarespectoamétodosnormales.Estonosignicanecesariamentequelosconstructoresydestructoresdebanserno-inline;haycasosenquetienesentido.También,cuandoseestáhaciendounprototipoinicialdeunprogramaescribiendocódigorápidamente,esconvenienteamenudousarinlines.Perosiestápreocupadoporlaeciencia,esunsitiodondemirar.9.5.ReducireldesordenEnunlibrocomoéste,lasimplicidadybrevedaddeponerdenicionesinlinedentrodelasclasesesmuyútilporquepermitemetermásenunapáginaopantalla(enunseminario).Sinembargo,DanSaks2haapuntadoqueenunproyectorealestotienecomoconsecuenciaeldesordendelainterfazdelaclaseyesohacequelaclaseseamásincomodadeusar.Élsereerealosmétodosdenidosdentrodelasclasesusandolaexpresióninsitu(enellugar)eindicaquetodaslasdenicionesdeberíancolocarsefueradelaclasemanteniendolainterfazlimpia.Laoptimización,argu-mentaél,esunaasuntodistinto.Siserequiereoptimizar,uselapalabrareservadainline.Siguienteeseenfoque,elejemploanteriorRectangle.cppquedaría:
//:C09:Noinsitu.cpp
//Removinginsitufunctions
classRectangle{
intwidth,height;
public:
Rectangle(intw=0,inth=0);
intgetWidth()const;
voidsetWidth(intw);
intgetHeight()const;
voidsetHeight(inth);
};
inlineRectangle::Rectangle(intw,inth)
:width(w),height(h){}
inlineintRectangle::getWidth()const{
returnwidth;
}
inlinevoidRectangle::setWidth(intw){
width=w;
}
inlineintRectangle::getHeight()const{
returnheight;
}
2Co-autorjuntoaTomPlumdeC++ProgrammingGuidelines,PlumHall,1991.
267
i
i
“Volumen1”—2012/1/12—13:52—page270—#308i
i
i
i
i
i
Capítulo9.Funcionesinline
conststd::string&msg="Requirementfailed"){
usingnamespacestd;
if(!requirement){
fputs(msg.c_str(),stderr);
fputs("\n",stderr);
exit(1);
}
}
inlinevoidrequireArgs(intargc,intargs,
conststd::string&msg=
"Mustuse%darguments"){
usingnamespacestd;
if(argc!=args+1){
fprintf(stderr,msg.c_str(),args);
fputs("\n",stderr);
exit(1);
}
}
inlinevoidrequireMinArgs(intargc,intminArgs,
conststd::string&msg=
"Mustuseatleast%darguments"){
usingnamespacestd;
if(argcminArgs+1){
fprintf(stderr,msg.c_str(),minArgs);
fputs("\n",stderr);
exit(1);
}
}
inlinevoidassure(std::ifstream&in,
conststd::string&filename=""){
usingnamespacestd;
if(!in){
fprintf(stderr,"Couldnotopenfile%s\n",
filename.c_str());
exit(1);
}
}
inlinevoidassure(std::ofstream&out,
conststd::string&filename=""){
usingnamespacestd;
if(!out){
fprintf(stderr,"Couldnotopenfile%s\n",
filename.c_str());
exit(1);
}
}
#endif//REQUIRE_H///:~
Losvalorespordefectoproporcionanmensajesrazonablesquesepuedencam-biarsiesnecesario.Fíjeseenqueenlugardeusarargumentoschar*seutilizaconststring&.Estopermitetantochar*,cadenasstringcomoargumentosparaestasfunciones,yasí
270
i
i
“Volumen1”—2012/1/12—13:52—page271—#309i
i
i
i
i
i
9.7.Comprobacióndeerroresmejorada
esmásgeneral(quizáquierautilizarestaformaensupropiocódigo).EnlasdenicionespararequireArgs()yrequireMinArgs(),seañadeunoalnúmerodeargumentosquenecesitaenlalíneadecomandosporqueargcsiempreincluyeelnombredelprogramaqueestáejecutadocomoargumentocero,yporesosiempretieneunvalorqueexcedeenunoalnúmerorealdeargumentosdelalíneadecomandos.Fíjeseenelusodedeclaracioneslocalesusingnamespacestdconcadafun-ción.Estoesporquealgunoscompiladoresenelmomentodeescribirestelibroinclu-yenincorrectamentelasfuncionesdelalibreríaCestándarenelespaciodenombresstd,asíquelacualicaciónexplícitapodríacausarunerrorentiempodecompila-ción.Lasdeclaracioneslocalespermitenquerequire.hfuncionetantoconlibreríascorrectascomoconincorrectassinabrirelespaciodenombresstdparacualquieraqueincluyaestecherodecabecera.Aquíhayunprogramasimpleparaprobarrequite.h:
//:C09:ErrTest.cpp
//{T}ErrTest.cpp
//Testingrequire.h
#include"../require.h"
#includestr;êm0;
usingnamespacestd;
intmain(intargc,char*argv[]){
inti=1;
require(i,"valuemustbenonzero");
requireArgs(argc,1);
requireMinArgs(argc,1);
ifstreamin(argv[1]);
assure(in,argv[1]);//Usethefilename
ifstreamnofile("nofile.xxx");
//Fails:
//!assure(nofile);//Thedefaultargument
ofstreamout("tmp.txt");
assure(out);
}///:~
Podríaestartentadoairunpasomásalláparamanejarlaaperturadecherosyañadirunamacroarequire.h.
#defineIFOPEN(VAR,NAME)\
ifstreamVAR(NAME);\
assure(VAR,NAME);
Quepodríausarseentoncesasí:
IFOPEN(in,argv[1])
Enprincipio,estopodríapareceratractivoporquesignicaquehayqueescribirmenos.Noesterriblementeinseguro,peroesuncaminoqueesmejorevitar.Fíjeseque,denuevo,unamacropareceunafunciónperosecomportadiferente;realmenteseestácreandounobjetoincuyoalcancepersistemásalládelamacro.Quizáloentienda,peroparaprogramadoresnuevosymantenedoresdecódigosóloesuna
271
i
i
“Volumen1”—2012/1/12—13:52—page272—#310i
i
i
i
i
i
Capítulo9.Funcionesinline
cosamásqueellosdebenresolver.C++essucientementecomplicadosinañadirconfusión,asíqueintentenoabusardelasmacrosdelpreprocesadorsiemprequepueda.9.8.ResumenEscríticoqueseacapazdeocultarlaimplementaciónsubyacentedeunaclaseporquepuedequerercambiarladespués.Haráestoscambiosporeciencia,opor-quehayaalcanzadounamejorcomprensióndelproblema,oporquehayalgunaclasenuevadisponibleparausarenlaimplementación.Cualquiercosaquehagapeligrarlaprivacidaddelaimplementaciónsubyacentereducelaexibilidaddellenguaje.Poreso,lafuncióninlineesmuyimportanteporqueprácticamenteeliminalanecesi-daddemacrosdepreprocesadorysusproblemasasociados.Coninline,losmétodospuedensertanecientescomolasmacros.Porsupuestosepuedeabusandelasfuncionesinlineenlasdenicionesdeclase.Elprogramadorestátentadodehacerloporqueesfácil,asíquelohace.Sinembargo,noesunproblemagraveporquedespués,cuandosebusquenreduccionesdetama-ño,siemprepuedecambiarlasinlineafuncionesconvencionalesdadoquenoafectaasufuncionalidad.Lapautadeberíaser«Primerohazeltrabajo,despuésoptimiza».9.9.EjerciciosLassolucionesalosejerciciossepuedenencontrareneldocumentoelectróni-cotitulado«TheThinkinginC++AnnotatedSolutionGuide»,disponibleporpocodineroenwww.BruceEckel.com.1.EscribaunprogramaqueuselamacroF()mostradaalprincipiodelcapítuloydemuestrequenoseexpandeapropiadamente,talcomodescribeeltexto.Arreglelamacroydemuestrequefuncionacorrectamente.2.EscribaunprogramaqueuselamacroFLOOR()mostradaalprincipiodelca-pítulo.Muestrelascondicionesenquenofuncionaapropiadamente.3.ModiqueMacroSideEffects.cppdemodoqueBAND()funcioneadecua-damente.4.Creedosfuncionesidénticas,f1()yf2().Hagainlineaf1()ydejef2-()comono-inline.Uselafunciónclock()delalibreríaCestándarqueseencuentraen&#x-500;ctimeparamarcarlospuntosdecomienzoynycomparelasdosfuncionesparavercuálesmásrápida.Puedequenecesitehacerunbucledellamadasrepetidasparaconseguirnúmerosrepresentativos.5.Experimenteconeltamañoycomplejidaddelcódigodelasfuncionesdelejer-cicio4paraversipuedeencontrarelpuntodondelafuncióninlineylacon-vencionaltardanlomismo.Sidisponedeellos,inténteloconcompiladoresdis-tintosyfíjeseenlasdiferencias.6.Pruebequelasfuncionesinlinehacenenlazadointernopordefecto.7.Creeunaclasequecontengaunarraydecaracteres.Añadaunconstructorin-linequeuselafunciónmemset()delalibreríaCestándarparainicializarelarrayalvalordadocomoargumentodelconstructor(pordefectoserá''),yunmétodoinlinellamadoprint()queimprimatodosloscaracteresdelarray.
272
i
i
“Volumen1”—2012/1/12—13:52—page273—#311i
i
i
i
i
i
9.9.Ejercicios
8.CojaelejemploNestFriend.cppdelCapítulo5yreemplacetodoslosméto-dosconinline.Nohagamétodosinlineinsitu.Tambiéncambielasfuncionesinitialize()porconstructores.9.ModiqueStringStack.cppdelCapítulo8parausarfuncionesinline.10.CreeunenumeradollamadoHuequecontengared,blueyyellow.AhoracreeunaclasellamadaColorquecontengaunatributodetipoHueyunconstruc-torquedévaloralHueconsuargumento.AñadamétodosdeaccesoalHueget()yset().Hagainlinetodoslosmétodos.11.Modiqueelejercicio10parausarelenfoque«accesor»y«mutador».12.ModiqueCpptime.cppdemodoquemidaeltiempodesdequecomienzaelprogramahastaqueelusuariopulsalatecla«Intro»o«Retorno».13.Creeunaclasecondosmétodosinline,elprimeroqueestádenidoenlaclasellamaalsegundo,sinnecesitarunadeclaraciónadelantada.Escribaunmain()quecreeunobjetodeesaclaseyllamealprimermétodo.14.CreeunaclaseAconunconstructorpordefectoinlinequeseanuncieasímis-mo.AhoracreeunanuevaclaseBypongaunobjetodeAcomomiembrodeB,ydeleaBunconstructorinline.CreeunarraydeobjetosByveaquésucede.15.Creeunagrancantidaddeobjetosdelejercicioanterior,yuselaclaseTimeparamedirlasdiferenciasentreloscontructoresinlineylosno-inline.(Sitieneunperlador,intenteusarlotambién).16.Escribaunprogramaquetomeunacadenaporlíneadecomandos.Escribaunbucleforqueelimineuncarácterdelacadenaencadapasada,yuselamacroDEGUB()deestecapítuloparaimprimirlacadenacadavez.17.CorrijalamacroTRACE()talcomoseexplicaenelcapítulo,ypruebequefuncionacorrectamente.18.ModiquelamacroFIELD()paraquetambiénincluyauníndicenumérico.CreeunaclasecuyosmiembrosestáncompuestosdellamadasalamacroFIE-LD().Añadaunmétodoquelepermitabuscarenuncampousandoelíndice.Escribaunmain()paraprobarlaclase.19.ModiquelamacroFIELD()paraqueautomáticamentegenerefuncionesdeaccesoparacadacampo(datadeberíanoobstanteserprivado).CreeunaclasecuyosmiembrosesténcompuestosdellamadasalamacroFIELD().Escribaunmain()paraprobarlaclase.20.Escribaunprogramaquetomedosargumentosdelíneadecomandos:elpri-meroesunenteroyelsegundoesunnombredechero.Userequiere.hparaasegurarquetieneelnúmerocorrectodeargumentos,queelenteroestáentre5y10,yqueelcherosepuedeabrirsatisfactoriamente.21.EscribaunprogramaqueuselamacroIFOPEN()paraabriruncherocomounujodeentrada.Fíjeseenlacreaciónunobjetoifstreamysualcance.22.(Desafío)Averigüecómoconseguirquesucompiladorgenerecódigoensam-blador.Creeuncheroquecontengaunafunciónmuypequeñayunmain().Genereelcódigoensambladorcuandolafunciónesinlineycuandonoloes,ydemuestrequelaversióninlinenotienelasobrecargaporlallamada.
273
i
i
“Volumen1”—2012/1/12—13:52—page277—#315i
i
i
i
i
i
10.1.LoselementosestáticosdeC
do,elcompiladorgarantizaquelavariableseinicializaráacero(convertidoaltipoadecuado)alcomenzarelprograma.Asípues,enoneChar(),laprimeravezquesellamaalafunción,svalecero.Enestecaso,secumplirálacondiciónif(!s).Lainicializaciónanteriorparasesmuysimple,perolainicializaciónparaobjetosestáticos(comoladecualquierotroobjeto)puedeserunaexpresiónarbitraria,queinvolucreconstantes,variablesofuncionespreviamentedeclaradas.Fíjesequelafuncióndearribaesmuyvulnerableaproblemasdeconcurren-cia.Siemprequediseñefuncionesquecontenganvariablesestáticas,deberátenerenmenteestetipodeproblemas.ObjetosestáticosdentrodefuncionesLasreglassonlasmismasparaobjetosestáticosdetiposdenidosporelusuario,añadiendoelhechoqueelobjetorequiereserinicializado.Sinembargo,laasignacióndelvalorcerosólotienesentidoparatipospredenidos.Lostiposdenidosporelusuariodebenserinicializadosllamandoasusrespectivosconstructores.Portanto,sinoespecicaargumentosenlosconstructorescuandodenaunobjetoestático,laclasedeberátenerunconstructorpordefecto.Porejemplo:
//:C10:StaticObjectsInFunctions.cpp
#include&#xiost;&#xream;
usingnamespacestd;
classX{
inti;
public:
X(intii=0):i(ii){}//Default
~X(){cout"X::~X()"endl;}
};
voidf(){
staticXx1(47);
staticXx2;//Defaultconstructorrequired
}
intmain(){
f();
}///:~
LosobjetosestáticosdetipoXdentrodef()puedenserinicializadostantoconlalistadeargumentosdelconstructorcomoconelconstructorpordefecto.Estacons-trucciónocurreúnicamentelaprimeravezqueelcontrolllegaaladenición.DestructoresdeobjetosestáticosLosdestructoresparaobjetosestáticos(esdecir,cualquierobjetoconalmacena-mientoestático,nosóloobjetosestáticoslocalescomoenelejemploanterior)soninvocadoscuandomain()nalizaocuandolafuncióndelibreríaestándardeCexit()sellamaexplícitamente.Enlamayoríadeimplementaciones,main()sim-plementellamaaexit()cuandotermina.Estosignicaquepuedeserpeligrosollamaraexit()dentrodeundestructorporquepodríaproducirseunainvocaciónrecursivainnita.LosdestructoresdeobjetosestáticosnoseinvocansisesaledelprogramautilizandolafuncióndelibreríaestándardeCabort().
277
i
i
“Volumen1”—2012/1/12—13:52—page281—#319i
i
i
i
i
i
10.2.Espaciosdenombres
ysiutiliza
staticvoidf();
signicaquef()esvisiblesóloparalaunidaddetraducción,(estosuelellamarseestáticoachero(lestatic).10.1.3.Otrosespecicadoresparaalmacenamientodecla-sesElusodestaticyexternestámuyextendido.Existenotrosdosespecica-doresdetipodealmacenamientobastantemenosconocidos.Elespecicadorautonoseutilizaprácticamentenuncaporqueledicealcompiladorqueesaesunavaria-blelocal.autoeslaabreviaturadeautomático«»ysereerealaformaenlaqueelcompiladorreservaespacioautomáticamenteparalavariable.Elcompiladorsiem-prepuededeterminaresehechoporelcontextoenquelavariablesedeneporloqueautoesredundante.Elespecicadorregisteraplicadoaunavariableindicaqueesunavariablelocal(auto),juntoconlapistaparaelcompiladordequeesavariableenconcretovaaserampliamenteutilizadaporloquedeberíaseralmacenadaenunregistrosiesposible.Portanto,esunaayudaparalaoptimización.Diferentescompiladoresres-pondendediferentemaneraantedichapista;inclusotienenlaopcióndeignorarla.Sitomaladireccióndelavariable,elespecicadorregistervaaserignoradocasicontotalseguridad.Serecomiendaevitarelusoderegisterporque,generalmen-te,elcompiladorsuelerealizarlaslaboresdeoptimizaciónmejorqueelusuario.10.2.EspaciosdenombresPeseaquelosnombrespuedenestaranidadosdentrodeclases,losnombresdefuncionesglobales,variablesglobalesyclasesseencuentranincluidosdentrodeunúnicoespaciodenombres.Lapalabrareservadastaticledacontrolsobreésteper-mitiéndoledarletantoavariablescomoafuncionesenlazadointerno(esdecircon-virtiéndolasenestáticasalchero).Peroenunproyectogrande,lafaltadecontrolsobreelespaciodenombresglobalpuedecausarproblemas.Conelndesolventaresosproblemasparaclases,losprogramadoressuelencrearnombreslargosycom-plicadosquetienenbajaprobabilidaddecrearconictosperoquesuponenhartarseateclearparaescribirlos.(Parasimplicaresteproblemasesueleutilizartypedef).Peseaqueellenguajelasoporta,noesunasoluciónelegante.Enlugardeesopuedesubdividirelespaciodenombresglobalenvariaspar-tesmásmanejablesutilizandolacaracterísticanamespacedeC++.Lapalabrare-servadanamespace,deformasimilaraclass,struct,enumyunion,sitúalosnombresdesusmiembrosenunespaciodiferente.Mientrasquelasdemáspalabrasreservadastienenpropósitosadicionales,laúnicafuncióndenamespaceesladecrearunnuevoespaciodenombres.10.2.1.CrearunespaciodenombresLacreacióndeunespaciodenombresesmuysimilaralacreacióndeunaclase:
281
i
i
“Volumen1”—2012/1/12—13:52—page282—#320i
i
i
i
i
i
Capítulo10.Controldenombres
//:C10:MyLib.cpp
namespaceMyLib{
//Declarations
}
intmain(){}///:~
Esecódigocreaunnuevoespaciodenombresquecontienelasdeclaracionesin-cluidasentrelasllaves.Detodasformas,existendiferenciassignicativasentrecl-ass,struct,enumyunion:
Unadeniciónconnamespacesolamentepuedeaparecerenunrangoglobaldevisibilidadoanidadodentrodeotronamespace.
Noesnecesariounpuntoycomatraslallavedecierreparanalizarladeni-cióndenamespace.
Unadeniciónnamespacepuedeser"continuada"enmúltiplesarchivosdecabecerautilizandounasintaxisque,paraunaclase,pareceríaserladeunaredenición:
//:C10:Header1.h
#ifndefHEADER1_H
#defineHEADER1_H
namespaceMyLib{
externintx;
voidf();
//...
}
#endif//HEADER1_H///:~
Elposiblecrearaliasdeunnamespacedeformaquenohacefaltaquetecleeunenrevesadonombrecreadoporalgúnfrabricantedelibrerías:
//:C10:BobsSuperDuperLibrary.cpp
namespaceBobsSuperDuperLibrary{
classWidget{/*...*/};
classPoppit{/*...*/};
//...
}
//Toomuchtotype!I'llaliasit:
namespaceBob=BobsSuperDuperLibrary;
intmain(){}///:~
Nopuedecrearunainstanciadeunnamespacetalycomopuedehacerconunaclase.EspaciosdenombressinnombreCadaunidaddetraduccióncontieneunespaciodenombressinnombrealquepuedereferirseescribiendo«namespace»sinningúnidenticador.
282
i
i
“Volumen1”—2012/1/12—13:52—page283—#321i
i
i
i
i
i
10.2.Espaciosdenombres
Losnombresenesteespacioestándisponiblesautomáticamenteenesaunidaddetraducciónsincualicación.Segarantizaqueunespaciosinnombreesúnicoparacadaunidaddetraducción.Siustedasignanombreslocalesenunespaciodenombresnonecesitarádarlesenlazadointernoconstatic.EnC++espreferibleutilizarespaciosdenombressinnombrequeestáticosache-ro.AmigasEsposibleañadirunadeclaracióntipofrienddentrodeunespaciodenombresincluyéndoladentrodeunaclase:
//:C10:FriendInjection.cpp
namespaceMe{
classUs{
//...
friendvoidyou();
};
}
intmain(){}///:~
Ahoralafunciónyou()esunmiembrodelespaciodenombresMe.Siintroduceunadeclaracióntipofriendenunaclasedentrodelespaciodenombresglobal,dichadeclaraciónseinyectaglobalmente.10.2.2.CómousarunespaciodenombresPuedereferirseaunnombredentrodeunespaciodenombresdetresmanerasdiferentes:especicandoelnombreutilizandoeloperadorderesolucióndeámbito,conunadirectivausingqueintroduzcatodoslosnombresenelespaciodenombresomedianteunadeclaraciónusingparaintroducirnombresdeunoenuno.ResolucióndelámbitoCualquiernombreenunespaciodenombrespuedeserexplícitamenteespeci-cadoutilizandoeloperadorderesolucióndeámbitodelamismaformaquepuedereferirsealosnombresdentrodeunaclase:
//:C10:ScopeResolution.cpp
namespaceX{
classY{
staticinti;
public:
voidf();
};
classZ;
voidfunc();
}
intX::Y::i=9;
classX::Z{
intu,v,w;
public:
Z(inti);
283
i
i
“Volumen1”—2012/1/12—13:52—page285—#323i
i
i
i
i
i
10.2.Espaciosdenombres
Integera,b;
Integerdivide(Integer,Integer);
//...
}
#endif//NAMESPACEMATH_H///:~
TambiénpuededeclarartodoslosnombresenIntdentrodelafunciónperodejandoesosnombresanidadosdentrodelafunción:
//:C10:Arithmetic.cpp
#include"NamespaceInt.h"
voidarithmetic(){
usingnamespaceInt;
Integerx;
x.setSign(positive);
}
intmain(){}///:~
Sinladirectivausing,todoslosnombresenelespaciodenombresrequeriríanestarcompletamenteexplicitados.Hayunaspectodeladirectivausingquepodríaparecerpocointuitivoalprinci-pio.Lavisibilidaddelosnombresintroducidosconunadirectivausingeselrangoenelquesecrealadirectiva.Pero¡puedehacercasoomisodelosnombresdenidosenladirectivausingcomosiestoshubiesensidodeclaradosglobalmenteparaeseámbito!
//:C10:NamespaceOverriding1.cpp
#include"NamespaceMath.h"
intmain(){
usingnamespaceMath;
Integera;//HidesMath::a;
a.setSign(negative);
//Nowscoperesolutionisnecessary
//toselectMath::a:
Math::a.setSign(positive);
}///:~
Supongaquetieneunsegundoespaciodenombresquecontienealgunosnom-bresennamespaceMath:
//:C10:NamespaceOverriding2.h
#ifndefNAMESPACEOVERRIDING2_H
#defineNAMESPACEOVERRIDING2_H
#include"NamespaceInt.h"
namespaceCalculation{
usingnamespaceInt;
Integerdivide(Integer,Integer);
//...
}
#endif//NAMESPACEOVERRIDING2_H///:~
285
i
i
“Volumen1”—2012/1/12—13:52—page286—#324i
i
i
i
i
i
Capítulo10.Controldenombres
Dadoqueesteespaciodenombrestambiénseintroduceconunadirectivau-sing,existelaposibilidaddetenerunacolisión.Detodosmodos,laambigüedadapareceenelmomentodeutilizarelnombre,noenladirectivausing:
//:C10:OverridingAmbiguity.cpp
#include"NamespaceMath.h"
#include"NamespaceOverriding2.h"
voids(){
usingnamespaceMath;
usingnamespaceCalculation;
//Everything'sokuntil:
//!divide(1,2);//Ambiguity
}
intmain(){}///:~
Portanto,esposibleescribirdirectivasusingparaintroducirunnúmerodees-paciosdenombreconnombresconictivossinproducirningunaambigüedad.LadeclaraciónusingPuedeinyectarnombresdeunoenunoenelámbitoactualutilizandounade-claraciónusing.Adiferenciadeladirectivausing,quetratalosnombrescomosihubiesensidodeclaradosglobalmenteparaeseámbito,unadeclaraciónusin-gesunadeclaracióndentrodelámbitoactual.Esosignicaquepuedesobrescribirnombresdeunadirectivausing:
//:C10:UsingDeclaration.h
#ifndefUSINGDECLARATION_H
#defineUSINGDECLARATION_H
namespaceU{
inlinevoidf(){}
inlinevoidg(){}
}
namespaceV{
inlinevoidf(){}
inlinevoidg(){}
}
#endif//USINGDECLARATION_H///:~
Ladeclaraciónusingsimplementedaelnombrecompletamenteespecicadodelidenticadorperonodainformacióndetipo.Esosignicaquesielespaciodenombrescontieneungrupodefuncionessobrecargadasconelmismonombre,ladeclaraciónusingdeclaratodaslasfuncionespertenecientesalgruposobrecargado.Esposibleponerunadeclaraciónusingencualquiersitiodondepodríaponerseunadeclaraciónnormal.Unadeclaraciónusingfuncionadelamismaformaquecualquierdeclaraciónnormalsalvoporunaspecto:puestoquenoseledaningunalistadeargumentos,unadeclaraciónusingpuedeprovocarlasobrecargadeunafunciónconlosmismostiposdeargumentos(cosaquenoestápermitidaporelpro-cedimientodesobrecarganormal).Detodasformas,estaambigüedadnosemuestrahastaelmomentodeuso,noapareciendoenelinstantededeclaración.Unadeclaraciónusingpuedetambiénaparecerdentrodeunespaciodenom-bresytieneelmismoefectoqueencualquierotrolugar(esenombresedeclaradentro
286
i
i
“Volumen1”—2012/1/12—13:52—page288—#326i
i
i
i
i
i
Capítulo10.Controldenombres
seguro.Losdatosglobalespuedensermodicadosportodoelmundoysunombrepuedechocarconotrosidénticossiesunproyectogrande.Seríaidealsilosdatospudiesenalmacenarsecomosifuesenglobalesperoocultosdentrodeunaclaseyclaramenteasociadosconesaclase.Estoesposibleusandoatributosstatic.Existeunaúnicaporcióndeespaciopa-ralosatributosstatic,independientementedelnúmerodeobjetosdedichaclasequesehayancreado.Todoslosobjetoscompartenelmismoespaciodealmacena-mientostaticparaeseatributo,constituyendounaformade«comunicarse»entreellos.Perolosdatosstaticpertenecenalaclase;sunombreestárestringidoalin-teriordelaclaseypuedeserpublic,privateoprotected.10.3.1.Denicióndelalmacenamientoparaatributosestá-ticosPuestoquelosdatosstatictienenunaúnicaporcióndememoriadondealma-cenarse,independientementedelnúmerodeobjetoscreados,esaporcióndebeserdenidaenunúnicositio.Elcompiladornoreservaráespaciodealmacenamientoporusted.Elenlazadorreportaráunerrorsiunatributomiembroesdeclaradoperonodenido.Ladenicióndeberealizarsefueradelaclase(nosepermiteelusodelasentenciainline),ysóloestápermitidaunadenición.Esporelloquehabitualmentesein-cluyeenelcherodeimplementacióndelaclase.Lasintaxissueletraerproblemas,peroenrealidadesbastantelógica.Porejemplo,sicreaunatributoestáticodentrodeunaclasedelasiguienteforma:
classA{
staticinti;
public:
//...
};
Deberádenirelalmacenamientoparaeseatributoestáticoenelcherodede-nicióndelasiguientemanera:
intA::i=1;
Siquisieradenirunavariableglobalordinaria,deberíautilizar
inti=1;
peroaquí,eloperadorderesolucióndeámbitoyelnombredelaclaseseutilizanparaespecicarA::i.AlgunaspersonastienenproblemasconlaideaqueA::iesprivate,ypeseaelloparecehaberalgoqueloestámanipulandoabiertamente.¿Norompeestoelmecanismodeprotección?Éstaesunaprácticacompletamentesegurapordosra-zones.Primera,elúnicositiodondeestainicializacióneslegalesenladenición.Efectivamente,sieldatostaticfueseunobjetoconunconstructor,habríallamadoalconstructorenlugardeutilizareloperador=.Segundo,unavezseharealizadoladenición,elusuarionalnopuedehacerunasegundadeniciónpuestoqueelenlazadorindicaríaunerror.Yelcreadordelaclaseestáforzadoacrearladenición
288
i
i
“Volumen1”—2012/1/12—13:52—page292—#330i
i
i
i
i
i
Capítulo10.Controldenombres
//!staticinti;//Error
//(Howwouldyoudefinei?)
}x;
}
intmain(){Outerx;f();}///:~
Yapuedeverelproblemaconatributosestáticosenclaseslocales:¿Cómodescri-biráeldatomiembroenelámbitodelcheroparapoderdenirlo?Enlapráctica,elusodeclaseslocalesesmuypococomún.10.3.3.MétodosestáticosTambiénpuedecrearmétodosestáticosque,comolosatributosestáticos,trabajanparalaclasecomountodoenlugardeparaunobjetoparticulardelaclase.Enlugardehacerunafunciónglobalquevivaeny«contamine»elespaciodenombresglobalolocal,puedeincluirelmétododentrodelaclase.Cuandocreaunmétodoestático,estáexpresandounaasociaciónconunaclaseparticular.Puedellamaraunmiembroestáticosdelaformahabitual,conelpuntoolae-cha,enasociaciónconunobjeto.Detodasformas,esmástípicollamaralosmétodosestáticosensimismos,sinespecicarningúnobjeto,utilizandoeloperadordereso-lucióndeámbito,comoenelsiguienteejemplo:
//:C10:SimpleStaticMemberFunction.cpp
classX{
public:
staticvoidf(){};
};
intmain(){
X::f();
}///:~
Cuandoveamétodosestáticosenunaclase,recuerdequeeldiseñadorpretendíaqueesafunciónestuvieseconceptualmenteasociadaalaclasecomountodo.Unmétodoestáticonopuedeaccederalosatributosordinarios,sóloalosestá-ticos.Sólopuedellamaraotrosmétodosestáticos.Normalmente,ladireccióndelobjetoactual(this)sepasadeformaencubiertacuandosellamaacualquierméto-do,perounmiembrostaticnotienethis,queeslarazónporlacualnopuedeaccederalosmiembrosordinarios.Portanto,seobtieneelligeroincrementodeve-locidadproporcionadoporunafunciónglobaldebidoaqueunmétodoestáticonoimplicalacargaextradetenerquepasarthis.Almismotiempo,obtienelosbene-ciosdetenerlafuncióndentrodelaclase.Paraatributos,staticindicaquesóloexisteunespaciodememoriaporatributoparatodoslosobjetosdelaclase.Estoestablecequeelusodestaticparadenirobjetosdentrodeunafunciónsignicaquesóloseutilizaunacopiadeunavariablelocalparatodaslasllamadasaesafunción.Aquíapareceunejemplomostrandoatributosymétodosestáticosutilizadoscon-juntamente:
292
i
i
“Volumen1”—2012/1/12—13:52—page294—#332i
i
i
i
i
i
Capítulo10.Controldenombres
staticEgge;
inti;
Egg(intii):i(ii){}
Egg(constEgg&);//Preventcopy-construction
public:
staticEgg*instance(){return&e;}
intval()const{returni;}
};
EggEgg::e(47);
intmain(){
//!Eggx(1);//Error--can'tcreateanEgg
//Youcanaccessthesingleinstance:
coutEgg::instance&#x-600;()-val()endl;
}///:~
Lainicializacióndeeocurreunavezsecompletaladeclaracióndelaclase,porloqueelcompiladortienetodalainformaciónquenecesitaparareservarespacioyllamaralconstructor.Paraimpedircompletamentelacreacióndecualquierotroobjeto,sehaañadidoalgomás:unsegundoconstructorprivadollamadoconstructordecopia.Llegadosaestepuntodellibro,ustednopuedesaberporquéestoesnecesariopuestoqueelconstructordecopianoseestudiaráhastaelsiguientecapítulo.Detodasformas,comounbreveadelanto,sisepropusieseretirarelconstructordecopiadenidoenelejemploanterior,seríaposiblecrearobjetosEggdelasiguienteforma:
Egge=*Egg::instance();
Egge2(*Egg::instance());
Ambosutilizanelconstructordecopia,porloqueparaevitarestaposibilidad,sedeclaraelconstructordecopiacomoprivado(noserequieredeniciónporquenuncavaaserllamado).Buenapartedelsiguientecapítuloesunadiscusiónsobreelconstructordecopiaporloqueestoquedarámásclaroentonces.10.4.DependenciaenlainicializacióndevariablesestáticasDentrodeunaunidaddetraducciónespecíca,estágarantizadoqueelordendeinicializacióndelosobjetosestáticosseráelmismoqueeldeaparicióndesusdenicionesenlaunidaddetraducción.Noobstante,nohaygarantíassobreelordenenqueseinicializanlosobjetosestá-ticosentrediferentesunidadesdetraducción,yellenguajenoproporcionaningunaformadeaveriguarlo.Estopuedeproducirproblemassignicativos.Comoejemplodedesastreposible(queprovocaráelcuelguedesistemasoperativosprimitivosolanecesidaddematarelprocesoenotrosmássosticados),siunarchivocontiene:
//:C10:Out.cpp{O}
//Firstfile
#includestr;êm0;
std::ofstreamout("out.txt");///:~
294
i
i
“Volumen1”—2012/1/12—13:52—page296—#334i
i
i
i
i
i
Capítulo10.Controldenombres
plataformadeterminaday,degolpeymisteriosamente,compilarloenotroentornoyquedejedefuncionar.10.4.1.QuéhacerExistentresaproximacionesparatratarconesteproblema:1.Nohacerlo.Evitarlasdependenciasdeinicializaciónestáticaeslamejorsolu-ción.2.Sidebehacerlo,coloquelasdenicionesdeobjetosestáticoscríticosenunúni-cochero,deformaquepuedacontrolar,deformaportable,suinicializacióncolocándolosenelordencorrecto.3.Siestáconvencidoqueesinevitabledispersarobjetosestáticosentreunida-desdetraduccióndiferentes(comoenelcasodeunalibrería,dondenopuedecontrolarelprogramaquelausa),haydostécnicasdeprogramaciónparasol-ventarelproblema.TécnicaunoElpionerodeestatécnicafueJerrySchwarzmientrascreabalalibreríaiostream(puestoquelasdenicionesparacin,coutycerrsonstaticyresidenenarchi-vosdiferentes).Realmenteesinferioralasegundatécnicaperohapululadodurantemuchotiempoporloquepuedeencontrarseconcódigoquelautilice;asípues,esimportantequeentiendacomofunciona.Estatécnicarequiereunaclaseadicionalensuarchivodecabecera.Estaclaseeslaresponsabledelainicializacióndinámicadesusobjetosestáticosdelibrería.Heaquíunejemplosimple:
//:C10:Initializer.h
//Staticinitializationtechnique
#ifndefINITIALIZER_H
#defineINITIALIZER_H
#include&#xiost;&#xream;
externintx;//Declarations,notdefinitions
externinty;
classInitializer{
staticintinitCount;
public:
Initializer(){
std::cout"Initializer()"std::endl;
//Initializefirsttimeonly
if(initCount++==0){
std::cout"performinginitialization"
std::endl;
x=100;
y=200;
}
}
~Initializer(){
std::cout"~Initializer()"std::endl;
//Cleanuplasttimeonly
if(--initCount==0){
296
i
i
“Volumen1”—2012/1/12—13:52—page298—#336i
i
i
i
i
i
Capítulo10.Controldenombres
cout"leavingmain()"endl;
}///:~
Ahoranoimportaenquéunidaddetraducciónseinicializaprimero.LaprimeravezqueunaunidaddetraducciónquecontengaInitializer.hseinicialice,in-itCountseráceroporloquelainicializaciónserállevadaacabo.(Estodependeengranmedidaenelhechoquelazonadealmacenamientoestáticoestáaceroantesdequecualquierinicializacióndinámicaselleveacabo).Paraelrestodeunidadesdetraducción,initCountnoseráceroyseeludirálainicialización.Lalimpiezaocurreenelordeninverso,y~Initializer()aseguraquesóloocurriráunavez.Esteejemploutilizatiposdellenguajecomoobjetosestáticosglobales.Estatéc-nicatambiénfuncionaconclases,peroesosobjetosdebenserinicializadosdinámi-camenteporlaclaseInitializer.Unaformadehacerestoescreandoclasessinconstructoresnidestructores,perosíconmétodosdeinicializaciónylimpiezaconnombresdiferentes.Unaaproximaciónmáscomún,detodasformas,estenerpunte-rosaobjetosycrearlosutilizandonewdentrodeInitializer().TécnicadosBastantedespuésdelaaparicióndelatécnicauno,alguien(noséquien)llegóconlatécnicaexplicadaenestasección,queesmuchomássimpleylimpiaquelaanterior.ElhechodequetardasetantoendescubrirseesuntributoalacomplejidaddeC++.Estatécnicasesustentaenelhechodequelosobjetosestáticosdentrodefun-ciones(sólo)seinicializanlaprimeravezquesellamaalafunción.Tengapresentequeelproblemaqueestamosintentandoresolveraquínoescuandoseinicializanlosobjetosestáticos(quesepuedecontrolarseparadamente)sinomásbienasegurarnosdequelainicializaciónocurreenelordenadecuado.Estatécnicaesmuylimpiayastuta.Paracualquierdependenciadeinicialización,secolocaunobjetoestáticodentrodeunafunciónquedevuelveunareferenciaaeseobjeto.Deestaforma,laúnicamaneradeaccederalobjetoestáticoesllamandoalafunción,ysieseobjetonecesitaaccederaotrosobjetosestáticosdelosquedepende,debellamarasusfunciones.Ylaprimeravezquesellamaaunafunción,sefuerzaallevaracabolainicialización.Estágarantizadoqueelordendelainicializaciónserácorrectodebidoaldiseñodelcódigo,noalordenquearbitrariamentedecideelenlazador.Paramostrarunejemplo,aquítenemosdosclasesquedependenlaunadelaotra.Laprimeracontieneunboolquesóloseinicializaporelconstructor,porloquesepuededecirsisehallamadoelconstructorporunainstanciaestáticadelaclase(eláreadealmacenamientoestáticoseinicializaaceroaliniciodelprograma,loqueproduceunvalorfalseparaelboolsielconstructornohasidollamado).
//:C10:Dependency1.h
#ifndefDEPENDENCY1_H
#defineDEPENDENCY1_H
#include&#xiost;&#xream;
classDependency1{
boolinit;
public:
Dependency1():init(true){
std::cout"Dependency1construction"
298
i
i
“Volumen1”—2012/1/12—13:52—page304—#342i
i
i
i
i
i
Capítulo10.Controldenombres
8.EnStaticDestructors.cpp,experimenteconelordendellamadadelosconstructoresydestructoresllamandoaf()yg()dentrodemain()endife-rentesórdenes.¿Sucompiladorinicializalosobjetosdelaformacorrecta?9.EnStaticDestructors.cpp,pruebeelmanejodeerrorespordefectodesuimplementaciónconvirtiendoladeniciónoriginaldeoutdentrodeunadeclaraciónextern,yponiendoladeniciónrealdespuésdeladenicióndea(dondeelconstructordeObjmandainformaciónaout).Asegúresequenohayningúnotroprogramaimportantefuncionandoensumáquinacuandoejecuteelcódigooquesumáquinamanejelasfaltasrobustamente.10.Pruebequelasvariablesestáticasdecheroenlosarchivosdecabeceranochocanentresícuandosonincluidasenmásdeunarchivocpp.11.Creeunaúnicaclasequecontengaunint,unconstructorqueinicialiceelintconsuargumento,unmétodoquecambieelvalordelintconsuargumentoyunafunciónprint()quemuestreporpantallaelint.Coloquesuclaseenunarchivodecabeceraeincluyadichoarchivoendosarchivoscpp.Enunodeelloscreeunainstanciadelaclaseyenlaotradeclareeseidenticadorcomoexternypruebedentrodemain().Recuerde,debeenlazarlosdosarchivosobjetoodelocontrarioelenlazadornoencontraráelobjeto.12.CreelainstanciadelobjetodelEjercicio11comostaticyveriqueque,de-bidoaeso,elenlazadoresincapazdeencontrarla.13.Declareunafunciónenunarchivodecabecera.Denalafunciónenunarchivocppyllámeladesdemain()enunsegundoarchivocpp.Compileyveriquequefunciona.Ahoracambieladenicióndelafuncióndeformaqueseasta-ticyveriquequeelenlazadornopuedeencontrarla.14.ModiqueVolatile.cppdelCapítulo8parahacerquecomm::isr()fun-cionerealmentecomounarutinadeserviciodeinterrupción.Pista:unarutinadeserviciodeinterrupciónnotomaningúnargumento.15.Escribaycompileunúnicoprogramaqueutilicelaspalabrasclaveautoyregister.16.Creeunarchivodecabeceraquecontengaunespaciodenombres.Dentrodelespaciodenombrescreevariasdeclaracionesdefunciones.Creeahoraunse-gundoarchivodecabeceraqueincluyaelprimeroycontinúeelespaciodenombres,añadiendovariasdeclaracionesdefuncionesmás.Creeahoraunar-chivocppqueincluyaelsegundoarchivodecabecera.Cambiesuespaciodenombresaotronombre(máscorto).Dentrodeunadenicióndefunción,lla-meaunadesusfuncionesutilizandolaresolucióndeámbito.Dentrodeunadenicióndefunciónseparada,escribaunadirectivausingparaintroducirsuespaciodenombresenelámbitodeesafunción,ydemuestrequenonecesitautilizarlaresolucióndeámbitoparallamaralasfuncionesdesdesuespaciodenombres.17.Creeunarchivodecabeceraconunespaciodenombressinnombre.Incluyalacabeceraendosarchivoscppdiferentesydemuestrequeunespaciosinnombreesúnicoparacada:unidaddetraducción.18.UtilizandoelarchivodecabeceradelEjercicio17,demuestrequelosnombresdeunespaciodenombressinnombreestándisponiblesautomáticamenteenuna:unidaddetraducciónsincalicación.
304
i
i
“Volumen1”—2012/1/12—13:52—page307—#345i
i
i
i
i
i
11:LasreferenciasyelconstructordecopiaLasreferenciassoncomopunterosconstantesqueelcompiladorde-referenciaautomáticamente.AunquelasreferenciastambiénexistenenPascal,laversióndeC++setomódellenguajeAlgol.LasreferenciassonesencialesenC++paraayudarenlasintaxisdelosoperadoressobrecargados(veaelcapítulo12)y,además,sonunabuenaformaparacontrolarlamaneraenquelosargumentossepasanalasfuncionestantohaciadentrocomohaciafuera.EnestecapítuloprimeroveráladiferenciaentrelospunterosenCyenC++,yluegosepresentaránlasreferencias.PerolamayorpartedelcapítuloahondaráenelasuntountantoconfusoparalosprogramadoresdeC++novatos:elconstructordecopia,unconstructorespecial(queusareferencias)yconstruyeunnuevoobjetodeotroyaexistentedelmismotipo.Elcompiladorutilizaelconstructordecopiaparapasaryretornarobjetosporvaloralasfunciones.Finalmente,sehablarásobrelacaracterística(untantooscura)delospunteros-a-miembrodeC++.11.1.PunterosenC++LadiferenciamásimportanteentrelospunterosenCyenC++esquelosdeC++estánfuertementetipados.Sobretodoenloquealtipovoid*sereere.Cnopermiteasignarunpunterodeuntipoaotrodeformacasual,perosípermitehacerlomedianteunvoid*.Porejemplo,
Bird*b;
Rock*r;
void*v;
v=r;
b=v;
Acausadeesta«característica»deC,puedeutilizarcualquiertipocomosideotrosetratarasinningúnavisoporpartedelcompilador.C++nopermitehaceresto;elcompiladordaunmensajedeerror,ysirealmentequiereutilizaruntipocomootrodiferente,debehacerloexplícitamente,tantoparaelcompiladorcomoparaellector,usandomolde(casteninglés).(Enelcapítulo3sehablósobrelasintaxismejoradadelmolde«explícito»).
307
i
i
“Volumen1”—2012/1/12—13:52—page308—#346i
i
i
i
i
i
Capítulo11.Lasreferenciasyelconstructordecopia
11.2.ReferenciasenC++Unareferencia(&)escomounpunteroconstantequesede-referenciaautomática-mente.Normalmenteseutilizaenlalistadeargumentosyenelvalorderetornodedelasfunciones.Perotambiénsepuedehacerunareferenciaqueapunteaalgoquenohasidoasignado.Porejemplo:
//:C11:FreeStandingReferences.cpp
#include&#xiost;&#xream;
usingnamespacestd;
//Ordinaryfree-standingreference:
inty;
int&r=y;
//Whenareferenceiscreated,itmust
//beinitializedtoaliveobject.
//However,youcanalsosay:
constint&q=12;//(1)
//Referencesaretiedtosomeoneelse'sstorage:
intx=0;//(2)
int&a=x;//(3)
intmain(){
cout"x="x",a="aendl;
a++;
cout"x="x",a="aendl;
}///:~
Enlalinea(1)elcompiladorasignalacantidadnecesariadememoria,lainicializaconelvalor12,yligalareferenciaaesaporcióndememoria.Loimportanteesqueunareferenciadebeestarligadaalamemoriadealguien.Cuandoseaccedeaunareferencia,seestáaccediendoaesamemoria.Asípues,siescribelaslineas(2)y(3)incrementaráxcuandoseincrementea,talcomosemuestraenelmain().Lomásfácilespensarqueunareferenciaescomounpunterodelujo.Laventajadeeste«puntero»esquenuncahayquepreguntarsesihasidoinicializado(elcompiladorloimpone)osihayquedestruirlo(elcompiladorlohace).Hayqueseguirunasdeterminadasreglascuandoseutilizanreferencias:1.Lareferenciadeserinicializadacuandosecrea.(Lospunterospuedeniniciali-zarseencualquiermomento).2.Unavezqueseinicializaunareferencia,ligándolaaunobjeto,nosepuedeligaraotroobjeto.(Lospunterospuedenapuntaraotroobjetoencualquiermomento).3.Nosepuedentenerreferenciasconvalornulo.Siemprehadesuponerqueunareferenciaestáconectadaaunatrozodememoriayaasignada.11.2.1.ReferenciasenlasfuncionesEllugarmáscomúnenelqueveráreferenciasesenlosargumentosyvalordere-tornodelasfunciones.Cuandoseutilizaunareferenciacomounargumentodeunafunción,cualquiercambiorealizadoenlareferenciadentrodelafunciónserealizarárealmentesobreelargumentofueradelafunción.Porsupuestoquepodríahacerlo
308
i
i
“Volumen1”—2012/1/12—13:52—page312—#350i
i
i
i
i
i
Capítulo11.Lasreferenciasyelconstructordecopia
pusha
callf()
addsp,4
movg,registera
Estecódigosehasimplicadoparahacerlogenérico;lasexpresionesbyaserándiferentesdependiendodesilasvariablessonglobales(encuyocasoserían_by_a)olocales(elcompiladorlaspondríaenlapila).Estotambiénesciertoparag.Lasintaxisdelallamadaaf()dependeríadesuguíadeestilo,yregisteradependeríadecómosuensambladorllamaalosregistrosdelaCPU.Apesardelasimplicación,lalógicadelcódigoseríalamisma.TantoenCcomoenC++,primeroseponenlosargumentosenlapiladederechaaizquierda,yluegosellamaalafunción.Elcódigodellamadaesresponsablederecogerlosargumentosdelapila(locualexplicalasentenciaaddsp,4).Peroten-gaencuentaquecuandosepasanargumentosporvalor,elcompiladorsimplementeponecopiasenlapila(conocelostamañosdecadauno,porloquelospuedecopiar).Elvalorderetornodef()secolocaenunregistro.Comoelcompiladorsabeloqueseestáretornando,porquelainformacióndeltipoyaestáenellenguaje,puederetornarlocolocándoloenunregistro.EnC,contiposprimitivos,elsimplehechodecopiarlosbitsdelvaloresequivalenteacopiarelobjeto.PasoyretornodeobjetosgrandesConsidereahoralostiposdenidosporelusuario.Sicreaunaclaseydeseapasarunobjetodeesaclaseporvalor,¿cómosabeelcompiladorloquetienequehacer?Lainformacióndelaclasenoestáenelcompilador,pueslohadenidoelusuario.Parainvestigaresto,puedeempezarconunaestructurasimpleque,claramente,esdemasiadograndeparaserdevueltaatravésdelosregistros:
//:C11:PassingBigStructures.cpp
structBig{
charbuf[100];
inti;
longd;
}B,B2;
Bigbigfun(Bigb){
b.i=100;//Dosomethingtotheargument
returnb;
}
intmain(){
B2=bigfun(B);
}///:~
Laconversiónacódigoensambladoresunpocomáscomplicadaporquelama-yoríadeloscompiladoresutilizanfunciones«auxiliares»(helper)envezdeinline.Enlafunciónmain(),lallamadaabigfun()empiezacomodebe:secolocaelconteni-dodeBenlapila.(AquípodríaocurrirquealgunoscompiladorescarguenregistrosconladirecciónytamañodeBigyluegounafunciónauxiliarseencarguedecolocarelBigenlapila).Enelfragmentodecódigofuenteanterior,loúniconecesarioantesdellamarala
312
i
i
“Volumen1”—2012/1/12—13:52—page313—#351i
i
i
i
i
i
11.3.Elconstructordecopia
funciónescolocarlosargumentosenlapila.Sinembargo,enelcódigoensambladordePassingBigStructures.cppseveunaacciónadicional:ladireccióndeB2secolocaenlapilaantesdehacerlallamadaalafunciónaunque,obviamente,noseaunargumento.Paraentenderquépasa,necesitaentenderlasrestriccionesdelcompiladorcuandollamaaunafunción.MarcodepilaparallamadasafunciónCuandoelcompiladorgeneracódigoparallamaraunafunción,primerocolocaenlapilatodoslosargumentosyluegohacelallamada.Dentrodelafunciónsegeneracódigoparamoverelpunterodepilahaciaabajo,yasíproporcionamemoriaparalasvariableslocalesdentrodelafunción.(«haciaabajo»esrelativo,lamáquinapuedeincrementarodecrementarelpunterodepilaalcolocarunargumento).PerocuandosehaceelCALLdeensambladorparallamaralafunción,laCPUcolocaladireccióndesdelaqueserealizalallamada,yenelRETURNdeensambladorseutilizaesadirecciónparavolveralpuntodesdedondeserealizólallamada.Estadirecciónessagrada,porquesinellaelprogramaseperderíaporcompleto.HeaquíesaspectodelmarcodepiladespuésdeejecutarCALLyponerlasvariableslocalesdelafunción:
Figura11.1:LlamadaaunafunciónElcódigogeneradoporelrestodelafunciónesperaquelamemoriatengaestadisposiciónparaquepuedautilizarlosargumentosylasvariableslocalessintocarladirecciónderetorno.Llámeseaestebloquedememoria,queestodoloqueunafunciónnecesitacuandoselallama,elmarcodelafunción(functionframe).Podríaparecerrazonableretornarvaloresmediantelautilizacióndelapila.Elcompiladorsimplementeloscolocaríaallíylafuncióndevolveríaundesplazamientoqueindicaradóndeempiezaelvalorderetorno.Re-entradaEsteproblemaocurreporquelasfuncionesenCyC++puedensufririnterrupcio-nes;estoes,loslenguajeshandeser(ydehechoson)re-entrantes.Tambiénpermitenllamadasafuncionesrecursivas.Estoquieredecirqueencualquierpuntodeeje-cucióndeunprogramapuedesufrirunainterrupciónsinqueelprogramaseveaafectadoporello.Obviamentelapersonaqueescribelarutinadeserviciodeinte-rrupciones(ISR)esresponsabledeguardaryrestaurartodoslosregistrosqueseutilicenenlaISR.PerosilaISRnecesitautilizarlapila,hadehacerloconseguridad.(PiensequeunaISRescomounafunciónnormalsinargumentosyconvalordere-tornovoidqueguardayrestauraelestadodelaCPU.LaejecucióndeunaISRsuele
313
i
i
“Volumen1”—2012/1/12—13:52—page314—#352i
i
i
i
i
i
Capítulo11.Lasreferenciasyelconstructordecopia
producirseporuneventohardware,ynoconunainvocacióndentrodelprogramadeformaexplícita).Ahoraimaginequepasaríasiunafunciónnormalintentararetornarvaloresme-diantelapila.Nopuedetocarlapilaporencimadelladirecciónderetorno,asíquelafuncióntendríaquecolocarlosvaloresderetornodebajodeladireccióndere-torno.PerocuandoseejecutaelRETURN,elpunterodepiladeberíaestarapuntandoaladirecciónderetorno(ojustodebajo,dependedelamáquina),asíquelafuncióndebesubirelpunterodelapila,desechandotodaslasvariableslocales.Siintentare-tornarvaloresusandolapilapordebajodeladirecciónderetorno,enesemomentoesvulnerableaunainterrupción.LaISRescribiríaencimadelosvaloresderetornoparacolocarsudirecciónderetornoysusvariableslocales.Pararesolveresteproblema,elquellamaalafunciónpodríahacerseresponsabledeasignarlamemoriaextraenlapilaparalosvaloresderetornoantesdellamaralafunción.Sinembargo,CnosediseñódeestamanerayC++hadesercompatible.Comoverápronto,elcompiladordeC++utilizaunesquemamásecaz.Otraideaseríaretornarelvalorutilizandounáreadedatosglobal,perotampocofuncionaría.Lare-entradasignicaquecualquierfunciónpuedeserunarutinadeinterrupciónparaotrafunción,incluidalafunciónqueseestáejecutando.Porlotanto,sicolocaunvalorderetornoenunáreaglobal,podríaretornaralamismafunción,locualsobreescribiríaelvalorderetorno.Lamismalógicaseaplicaalarecursividad.Losregistrossonelúnicolugarseguroparadevolvervalores,asíquesevuelvealproblemadequéhacercuandolosregistrosnosonlosucientementegrandesparacontenerelvalorderetorno.Larespuestaescolocarladireccióndelaubicacióndelvalorderetornoenlapilacomounodelosargumentosdelafunción,ydejarquelafuncióncopielainformaciónquesedevuelvedirectamenteenesaubicación.Estonosolosolucionatodolosproblemas,sinoqueademásesmásecaz.ÉstaeslarazónporlaqueelcompiladorcolocaladireccióndeB2antesdellamarabigfunenlafunciónmain()dePassingBigStructures.cpp.Sivieraelcódigoensambladordebigfun()observaríaquelafunciónesperaesteargumentoescondidoylocopiaaldestinodentrodelafunción.Copiabitabitvs.inicializaciónHastaaquí,todobien.Tenemosunprocedimientoparapasaryretornarestruc-turassimplesgrandes.Peronotequeloúnicoquetieneesunamaneradecopiarbitsdeunlugaraotro,loqueciertamentefuncionabienparalaforma(muyprimitiva)enqueCtratalasvariables.Sinembargo,enC++losobjetospuedensermuchomásavanzadosqueunpuñadodebits,puestienensignicadoy,porlotanto,puedequenorespondabienalsercopiado.Considereunejemplosimple:unaclasequeconocecuantosobjetosdeuntipoexistenencualquiermomento.EnelCapítulo10seviolamaneradehacerloinclu-yendounatributoestático(static):
//:C11:HowMany.cpp
//Aclassthatcountsitsobjects
#includestr;êm0;
#include&#xstri;&#xng00;
usingnamespacestd;
ofstreamout("HowMany.out");
classHowMany{
staticintobjectCount;
314
i
i
“Volumen1”—2012/1/12—13:52—page315—#353i
i
i
i
i
i
11.3.Elconstructordecopia
public:
HowMany(){objectCount++;}
staticvoidprint(conststring&msg=""){
if(msg.size()!=0)outmsg":";
out"objectCount="
objectCountendl;
}
~HowMany(){
objectCount--;
print("~HowMany()");
}
};
intHowMany::objectCount=0;
//PassandreturnBYVALUE:
HowManyf(HowManyx){
x.print("xargumentinsidef()");
returnx;
}
intmain(){
HowManyh;
HowMany::print("afterconstructionofh");
HowManyh2=f(h);
HowMany::print("aftercalltof()");
}///:~
LaclaseHowManycontieneunenteroestáticollamadoobjectCountyunméto-doestáticollamadoprint()pararepresentarelvalordeobjectCount,juntoconargumentodemensajeopcional.ElconstructorincrementaobjectCountcadavezquesecreaunobjeto,yeldestructorlodisminuye.Sinembargolasalidanoeslaquecabríaesperar:
afterconstructionofh:objectCount=1
xargumentinsidef():objectCount=1
~HowMany():objectCount=0
aftercalltof():objectCount=0
~HowMany():objectCount=-1
~HowMany():objectCount=-2
Despuésdecrearh,elcontadoresuno,locualestábien.Perodespuésdelallamadaaf()seesperaríaqueelcontadorestuvieraados,porqueh2estáahoratambiéndentrodeámbito.Sinembargo,elcontadorescero,locualindicaquealgohaidomuymal.Estoseconrmaporelhechodequelosdosdestructores,llamadosalnaldemain(),hacenqueelcontadorpaseasernegativo,algoquenodeberíaocurrirnunca.Mireloqueocurredentrodef()despuésdequeelargumentosepaseporvalor.Estoquieredecirqueelobjetooriginalhexistefueradelámbitodelafuncióny,porotrolado,hayunobjetodemásdentrodelámbitodelafunción,queescopiadelob-jetoquesepasóporvalor.ElargumentoquesepasóutilizaelprimitivoconceptodecopiabitabitdeC,perolaclaseC++HowManynecesitainicializarsecorrectamen-teparamantenersuintegridad.Porlotanto,sedemuestraquelacopiabitabitnolograelefectodeseado.Cuandoelobjetolocalsaledeámbitoalacabarlafunciónf(),sellamaasu
315
i
i
“Volumen1”—2012/1/12—13:52—page317—#355i
i
i
i
i
i
11.3.Elconstructordecopia
HowMany2(conststring&id=""):name(id){
++objectCount;
print("HowMany2()");
}
~HowMany2(){
--objectCount;
print("~HowMany2()");
}
//Thecopy-constructor:
HowMany2(constHowMany2&h):name(h.name){
name+="copy";
++objectCount;
print("HowMany2(constHowMany2&)");
}
voidprint(conststring&msg="")const{
if(msg.size()!=0)
outmsgendl;
out'\t'name":"
"objectCount="
objectCountendl;
}
};
intHowMany2::objectCount=0;
//PassandreturnBYVALUE:
HowMany2f(HowMany2x){
x.print("xargumentinsidef()");
out"Returningfromf()"endl;
returnx;
}
intmain(){
HowMany2h("h");
out"Enteringf()"endl;
HowMany2h2=f(h);
h2.print("h2aftercalltof()");
out"Callf(),noreturnvalue"endl;
f(h);
out"Aftercalltof()"endl;
}///:~
Hayunascuantascosasnuevasparaquepuedahacerseunaideamejordeloquepasa.Primeramente,elstringnamehacedeidenticadordeobjetocuandoseim-primaenlasalida.Puedeponerunidenticador(normalmenteelnombredelobjeto)enelconstructorparaquesecopieennameutilizandoelconstructorconunstringcomoargumento.Pordefectosecreaunstringvacío.Elconstructorincrementaob-jectCountyeldestructorlodisminuye,igualqueenelejemploanterior.Losiguienteeselconstructordecopia,HowMany2(constHowMany2&).Elcons-tructordecopiasimplementecreaunobjetoapartirdeotroexistente,asíquecopiaennameelidenticadordelobjetoorigen,seguidodelapalabra«copy»,yasípuedeverdedóndeprocede.Simiraatentamente,veráquelallamadaname(h.name)enlalistadeinicializadoresdelconstructorestállamandoalconstructordecopiadelaclasestring.
317
i
i
“Volumen1”—2012/1/12—13:52—page318—#356i
i
i
i
i
i
Capítulo11.Lasreferenciasyelconstructordecopia
Dentrodelconstructordecopia,seincrementaelcontadorigualqueenelcons-tructornormal.Estoquieredecirqueobtendráuncontadordeobjetosprecisocuan-dopaseyretorneporvalor.Lafunciónprint()sehamodicadoparaimprimirenlasalidaunmensaje,elidenticadordelobjetoyelcontadordeobjetos.Comoahoraaccedealatributonamedeunobjetoconcreto,yanopuedeserunmétodoestático.Dentrodemain()puedeverquehayunasegundallamadaaf().SinembargoestallamadautilizalacaracterísticadeCparaignorarelvalorderetorno.Peroahoraquesabecómoseretornaelvalor(esdecir,códigodentrodelafunciónquemanejaelprocesoderetornoponiendoelresultadoenunlugarcuyadirecciónsepasacomounargumentoescondido),podríapreguntarsequéocurrecuandoseignoraelvalorderetorno.Lasalidadelprogramamostraráalgunaluzsobreelasunto.Peroantesdemostrarlasalida,heaquíunpequeñoprogramaqueutilizaiost-reamsparaañadirnúmerosdelíneaacualquierarchivo:
//:C11:Linenum.cpp
//{T}Linenum.cpp
//Addlinenumbers
#include"../require.h"
#include&#xvect;&#xor00;
#include&#xstri;&#xng00;
#includestr;êm0;
#include&#xiost;&#xream;
#include mat;&#xh000;
usingnamespacestd;
intmain(intargc,char*argv[]){
requireArgs(argc,1,"Usage:linenumfile\n"
"Addslinenumberstofile");
ifstreamin(argv[1]);
assure(in,argv[1]);
stringline;
vectorstring�lines;
while(getline(in,line))//Readinentirefile
lines.push_back(line);
if(lines.size()==0)return0;
intnum=0;
//Numberoflinesinfiledetermineswidth:
constintwidth=
int(log10((double)lines.size()))+1;
for(inti=0;ilines.size();i++){
cout.setf(ios::right,ios::adjustfield);
cout.width(width);
cout++num")"lines[i]endl;
}
}///:~
Elarchivosepasaaun&#xstri;&#xng00;vectorutilizandoelmismocódigofuentequehavistoanteriormenteenestelibro.Cuandoseponenlosnúmerosdelínea,nosgustaríaquetodaslaslíneasestuvieranalineadas,yestonecesitaconocerelnúmerodelíneasenelarchivoparaqueseacoherente.Sepuedeconocerelnúmerodelíneasconvector::size(),peroloquerealmentenecesitamosesconocersihaymáslíneasde10,100,1000,etc.Siseutilizaellogaritmoenbase10sobreelnúmerode
318
i
i
“Volumen1”—2012/1/12—13:52—page320—#358i
i
i
i
i
i
Capítulo11.Lasreferenciasyelconstructordecopia
ObjetostemporalesEnlalínea15seempiezalallamadaaf(h),yestavezignoraelvalorderetorno.Puedeverqueseinvocaelconstructordecopiaenlalínea16,igualqueantes,parapasarelargumento.Ytambién,igualqueantes,enlalínea21sellamaalconstructordecopiaparaelvalorderetorno.Peroelconstructordecopianecesitaunadirecciónparautilizarcomodestino(esdecir,paratrabajarconelpunterothis).¿Dedóndeprocedeestadirección?Estopruebaqueelcompiladorpuedecrearunobjetotemporalcuandolonecesitaparaevaluaradecuadamenteunaexpresión.Enestecaso,creaunoquenisiquieraseleveactuarcomodestinoparaelvalorignoradoretornadoporf().Eltiempodevidadeesteobjetotemporalestancortocomoseaposibleparaqueelprogramanosellenedeobjetostemporalesesperandoaserdestruidos,locualprovocaríalautilizacióninecazderecursosvaliosos.Enalgunoscasos,elobjetotemporalpodríapasarseinmediatamenteaotrafunción,peroenestecasonosenecesitadespuésdelallamadaalafunción,asíqueencuantolafuncióntermina,llamandoaldestructordelobjetolocal(líneas23y24),elobjetotemporaltambiénsedestruye(líneas25y26).Finalmente,delalínea28alalínea31,sedestruyeelobjetoh2,seguidodehyelcontadordeobjetosvuelveacero.11.3.3.ElconstructordecopiapordefectoComoelconstructordecopiaimplementaelpasoyretornoporvalor,esimpor-tantequeelcompiladorcreeunoenelcasodeestructurassimples(dehecho,eslomismoquehaceC).Sinembargotodoloquesehavistoeselcomportamientopordefecto:unacopiabitabit.Cuandoseutilizantiposmáscomplejos,elcompiladordeC++crearáuncons-tructordecopiaautomáticamentesinoseimplementaexplícitamente.Denuevo,unacopiabitabitnotienesentido,porquenotieneporquéserelcomportamientoquesenecesita.Heaquíunejemploparamostrarelcomportamientomásinteligentedelcom-pilador.Supongaquecreaunanuevaclasecompuestaporobjetosdevariasclasesdiferentes.Aestoseledenominacomposición,yesunadelasformasenlasquesepuedenhacernuevasclasesapartirdelasyaexistentes.Ahoradesempeñeelpa-peldeunnovatoquetrataderesolverunproblemarápidamentecreandounanuevaclasedeestamanera.Nosabenadasobrelosconstructoresdecopia,asíquenoloim-plementa.Elejemplomuestraloqueelcompiladorhacecuandocreaunconstructordecopiapordefectoparasunuevaclase:
//:C11:DefaultCopyConstructor.cpp
//Automaticcreationofthecopy-constructor
#include&#xiost;&#xream;
#include&#xstri;&#xng00;
usingnamespacestd;
classWithCC{//Withcopy-constructor
public:
//Explicitdefaultconstructorrequired:
WithCC(){}
WithCC(constWithCC&){
cout"WithCC(WithCC&)"endl;
}
320
i
i
“Volumen1”—2012/1/12—13:52—page324—#362i
i
i
i
i
i
Capítulo11.Lasreferenciasyelconstructordecopia
siguienteejemplo:
//:C11:SimpleStructure.cpp
structSimple{inta;};
intmain(){
Simpleso,*sp=&so;
sp�-a;
so.a;
}///:~
Ahorasupongaquetieneunpunteronormalquesellamaipyqueapuntaaunentero.Paraaccederaloqueapuntaip,hadeestarprecedidoporun'*':
*ip=4;
Finalmente,sepreguntaráquépasasitieneunpunteroqueestáapuntandoaalgoqueestádentrodeunobjeto,inclusosiloquerealmenterepresentaesundes-plazamientodentrodelobjeto.Paraaccederaloqueestáapuntando,debeprecederelpunterocon'*'.Perocomoesundesplazamientodentrodeunobjeto,tambiénhadereferirsealobjetoconelqueestamostratando.Así,el*secombinaconelobjeto.Portanto,lanuevasintaxisseescribe�-*paraunpunteroqueapuntaaunobjeto,y.*paraunobjetooreferencia,talcomoesto:
objectPointer�-*pointerToMember=47;
object.*pointerToMember=47;
Pero,¿cuáleslasintaxisparadenirelpointerToMember?Puescomocual-quierpuntero,tienequedecireltipoalqueapuntará,porloqueseutilizaríael'*'enladenición.Laúnicadiferenciaesquedebedeciraquéclasedeobjetosapuntaráeseatributopuntero.Obviamente,estoseconsigueconelnombredelaclaseyeloperadorderesolucióndeámbito.Así,
intObjectClass::*pointerToMember;
deneunatributopunterollamadopointerToMemberqueapuntaacualquierenterodentrodeObjectClass.Tambiénpuedeinicializarelpunterocuandolodena(oencualquierotromomento):
intObjectClass::*pointerToMember=&ObjectClass::a;
Realmentenoexisteuna«dirección»deObjectClass::aporqueseestáre-riendoalaclaseynoaunobjetodeesaclase.Así,&ObjectClass::asepuedeutilizarsóloconlasintaxisdeunpunteroamiembro.Heaquíunejemploquemuestracómocrearyutilizarpunterosaatributos:
//:C11:PointerToMemberData.cpp
#include&#xiost;&#xream;
usingnamespacestd;
classData{
324
i
i
“Volumen1”—2012/1/12—13:52—page325—#363i
i
i
i
i
i
11.4.Punterosamiembros
public:
inta,b,c;
voidprint()const{
cout"a="a",b="b
",c="cendl;
}
};
intmain(){
Datad,*dp=&d;
intData::*pmInt=&Data::a;
dp�-*pmInt=47;
pmInt=&Data::b;
d.*pmInt=48;
pmInt=&Data::c;
dp�-*pmInt=49;
dp�-print();
}///:~
Obviamente,sonmuydesagradablesdeutilizarencualquierlugarexceptoparacasoespeciales(queesexactamenteparaloquesecrearon).Además,lospunterosamiembrosonbastantelimitados:puedenasignarseso-lamenteaunaubicaciónespecícadentrodeunaclase.Nopodría,porejemplo,incrementarlosocompararlostalcomopuedehacerconpunterosnormales.11.4.1.FuncionesUnejerciciosimilarseproduceconlasintaxisdepunteroamiembroparaméto-dos.Unpunteroaunafunción(presentadoalnaldelCapítulo3)sedenecomo:
int(*fp)(float);
Losparéntesisqueenglobana(*fb)sonnecesariosparaquefuercenlaevalua-cióndeladeniciónapropiadamente.Sinellosseríaunafunciónquedevuelveunint*.Losparéntesistambiéndesempeñanunpapelimportantecuandosedenenyutilizanpunterosamétodos.Sitieneunafuncióndentrodeunaclase,puededenirunpunteroaesemétodoinsertandoelnombredelaclaseyeloperadorderesolucióndeámbitoenunadeniciónhabitualdepunteroafunción:
//:C11:PmemFunDefinition.cpp
classSimple2{
public:
intf(float)const{return1;}
};
int(Simple2::*fp)(float)const;
int(Simple2::*fp2)(float)const=&Simple2::f;
intmain(){
fp=&Simple2::f;
}///:~
325
i
i
“Volumen1”—2012/1/12—13:52—page328—#366i
i
i
i
i
i
Capítulo11.Lasreferenciasyelconstructordecopia
Unareferenciacontieneunadirección,perosetratacomounobjeto.Lasreferenciassonesencialesparaunasintaxisclaraconlasobrecargadeoperadores(eltemadelsiguientecapítulo),perotambiénproporcionanmejorassintácticasparaelpasoyretornodeobjetosenfuncionesnormales.Elconstructordecopiacogeunareferenciaaunobjetoexistentedelmismotipoqueelargumento,yloutilizaparalacreacióndeunnuevoobjetoapartirdelqueexistente.Elcompiladorllamaautomáticamentealconstructordecopiacuandopasaoretornaunobjetoporvalor.Aunqueelcompiladorcreaunconstructordecopiaautomáticamente,sicreequesuclasenecesitauno,deberíadenirloparaaseguraruncomportamientoapropiado.Sinodeseaqueelobjetosepaseoretorneporvalor,deberíacrearunconstructordecopiaprivado.Lospunterosamiembrotienenlamismacapacidadquelospunterosnormales:puedeelegirunaregióndememoriaparticular(atributoométodo)entiempodeejecución.Lospunterosamiembrofuncionanconlosmiembrosdeunaclaseenvezdevariablesofuncionesglobales.Ofrecenlasucienteexibilidadparacambiarelcomportamientoentiempodeejecución.11.6.EjerciciosLassolucionesalosejerciciossepuedenencontrareneldocumentoelectróni-cotitulado«TheThinkinginC++AnnotatedSolutionGuide»,disponibleporpocodineroenwww.BruceEckel.com.1.Conviertaelfragmentodecódigo«bird&rock»delprincipiodeestecapítuloaunprogramaC(utilizandoestructurasparalostiposdedatos),yquecompile.AhoraintentecompilarloconuncompiladordeC++yveaquéocurre.2.Cojalosfragmentosdecódigoalprincipiodelaseccióntitulada«ReferenciasenC++»ypóngalosenunmain().Añadasentenciasparaimprimirenlasali-daparaquepuedademostrarustedmismoquelasreferenciassoncomopun-terosquesedereferencianautomáticamente.3.Escribaunprogramaenelcualintente(1)Crearunareferenciaquenoestéinicializadacuandosecrea.(2)Cambiarunareferenciaparaquesereeraaotroobjetodespuésdequesehayainicializado.(3)Crearunareferencianula.4.Escribaunafunciónquetomeunpunteroporargumento,modiqueelconte-nidodeloqueelapuntapuntero,yretorneesemismocontenidocomosideunareferenciasetratara.5.Creeunanuevaclaseconalgunosmétodos,yhagaqueelobjetoseaapuntadoporelargumentodelEjercicio4.Hagaqueelpunteropasadocomoargumentoyalgunosmétodosseanconstantesypruebequesólopuedellamaralosmé-todosconstantesdentrodesufunción.Hagaqueelargumentodesufunciónseaunareferenciaenvezdeunpuntero.6.Cojalosfragmentosdecódigoalprincipiodelasección«referenciasapuntero»yconviértalosenunprograma.7.Creeunafunciónquetomecomoargumentounareferenciaaunpunteroqueapuntaaotropunteroymodiqueeseargumento.Enmain(),llamealafun-ción.
328
i
i
“Volumen1”—2012/1/12—13:52—page330—#368i
i
i
i
i
i
Capítulo11.Lasreferenciasyelconstructordecopia
19.Creeunaclaseconunconstructordecopiaqueseanuncieasímismo(esdecirqueimprimaporlasalidaquehasidollamado).Hagaunasegundaclasequecontengaunobjetomiembrodelaprimeraclase,peronocreeunconstructordecopia.Demuestrequeelconstructordecopia,quegeneraautomáticamenteelcompiladorenlasegundaclase,llamaalconstructordecopiadelaprimera.20.Creeunaclasemuysimple,yunafunciónquedevuelvaunobjetodeesaclaseporvalor.Creeunasegundafunciónquetomeunareferenciadeunobjetodesuclase.Llamealasegundafunciónpasándolecomoargumentounallamadaalaprimerafunción,ydemuestrequelasegundafuncióndebeutilizarunareferenciaconstantecomoargumento.21.Creeunaclasesimplesinconstructordecopia,yunafunciónsimplequetomeunobjetodeesaclaseporvalor.Ahoracambiesuclaseañadiéndoleunadecla-ración(sólodeclare,nodena)privadadeunconstructordecopia.Expliqueloqueocurrecuandocompilalafunción.22.Esteejerciciocreaunaalternativaalautilizacióndelconstructordecopia.CreeunaclaseXydeclare(peronodena)unconstructordecopiaprivado.Hagaunafunciónclone()públicacomounmétodoconstantequedevuelveunacopiadelobjetocreadoutilizandonew.AhoraescribaunafunciónquetomecomoargumentounconstX&ycloneunacopialocalquepuedemodicarse.Elinconvenientedeestoesqueeselprogramadorelresponsablededestruirexplícitamenteelobjetoclonado(utilizandodelete)cuandohayaterminadoconél.23.ExpliquequéestámalenMem.cppyMemTest.cppdelCapítulo7.Solucioneelproblema.24.Creeunaclasequecontengaundoubleyunafunciónprint()queimprimaeldouble.Creepunterosamiembrotantoparaelatributocomoalmétododesuclase.Creeunobjetodesuclaseyunpunteroaeseobjeto,ymanipuleamboselementosdelaclaseatravésdelospunterosamiembro,utilizandotantoelobjetocomoelpunteroalobjeto.25.Creeunaclasequecontengaunarraydeenteros.¿Puederecorrerelarrayme-dianteunpunteroamiembro?26.ModiquePmemFunDefinition.cppañadiendounmétodof()sobrecar-gado(puededeterminarlalistadeargumentosquecauselasobrecarga).Aho-racreeunsegundopunteroamiembro,asígneloalaversiónsobrecargadadef(),yllamealmétodoatravésdelpuntero.¿Cómosucedelaresolucióndelafunciónsobrecargadaenestecaso?27.EmpiececonlafunciónFunctionTable.cppdelCapítulo3.Creeunaclasequecontengaunvectordepunterosafunciones,conmétodosadd()yremo-ve()paraañadiryquitarpunterosafunción.Añadaunafuncióndenominadarun()querecorraelvectoryllameatodaslafunciones.28.ModiqueelEjercicio27paraquefuncioneconpunterosamétodos.
330
i
i
“Volumen1”—2012/1/12—13:52—page331—#369i
i
i
i
i
i
12:SobrecargadeoperadoresLasobrecargadeoperadoresessolamente«azúcarsintáctico»,loquesignicaqueessimplementeotramaneradeinvocarfunciones.Ladiferenciaesquelosargumentosparaestasfuncionesnoaparecenentrepa-réntesis,sinoquerodeanosiguenaloscaracteresquesiemprepensócomoopera-doresinalterables.Haydosdiferenciasentreelusodeunoperadoryeldeunallamadaafunciónnormal.Lasintaxisesdiferente:unoperadoresamenudo«llamado»situándoloentre(odespuésde)losargumentos.Lasegundadiferenciaesqueelcompiladordeterminaqué«función»llamar.Porejemplo,siestáusandoeloperador+conar-gumentosdepuntootante,elcompilador«llama»alafunciónpararealizarunasumadepuntootante(esta«llamada»normalmenteconsisteeninsertarcódigoenlinea,ounainstruccióndepuntootantedelprocesador).Siusaeloperador+conunnúmerodepuntootanteyunentero,elcompilador«llama»aunafunciónespe-cialparaconvertirelintaunoat,yentonces«llama»alafuncióndesumaenpuntootante.Sinembargo,enC++esposibledenirnuevosoperadoresquetrabajenconcla-ses.Estadeniciónesexactamentecomoladenicióndeunafunciónordinaria,ex-ceptoqueelnombredelafunciónconsisteenlapalabrareservadaoperatorse-guidadeloperador.Siendoestalaúnicadiferencia,eloperadorseconvierteenunafuncióncomootracualquieraqueelcompiladorllamacuandoveelprototipoade-cuado.12.1.PrecauciónytranquilidadEstentadorconvertirseenunsuper-entusiastadelasobrecargadeoperadores.Sonunjuguetedivertido,alprincipio.Perorecuerdequeessólounendulzamientosintáctico,otramaneradellamaraunafunción.Mirándolodesdeesaperspectiva,nohayrazónparasobrecargarunoperadorexceptosiesohacealcódigoimplicadoconlaclasemássencilloeintuitivodeescribiryespecialmentedeleer.(Recuerde,elcódigoseleemuchomásqueseescribe).Siéstenoeselcasonosemoleste.Otrareaccióncmúnfrentealusodelasobrecargadeoperadoreseselpánico:derepente,losoperadoresdeCpierdensusignicadofamiliar.«¡TodohacambiadoymicódigoCporcompletoharácosasdiferentes!».Estonoesverdad.Todoslosoperadoresusadosenexpresionesquecontienensolotiposdedatosincorporadosnopuedensercambiados.Nuncapodrásobrecargaroperadoresasí:
14;
331
i
i
“Volumen1”—2012/1/12—13:52—page333—#371i
i
i
i
i
i
12.3.Operadoressobrecargables
Losdosoperadoressobrecargadosestándenidoscomométodosenlíneaqueimprimenunmensajealserllamados.Elúnicoargumentodeestasfuncionesmiem-broseráelqueaparezcadelladoderechodeloperadorbinario.Losoperadoresuna-riosnotienenargumentoscuandosedenencomométodos.Elmétodoesinvocadoporelobjetodelaparteizquierdadeloperador.Paralosoperadoresincondicionales(loscondicionalesgeneralmentedevuelvenunvalorbooleano),generalmentesedesearádevolverunobjetoounareferenciadelmismotipoqueestáoperando,silosdosargumentossondelmismotipo.(Sinosondelmismotipo,lainterpretacióndeloquedeberíapasaresresponsabilidadsuya).Deestamanera,sepuedenconstruirexpresionestancomplicadascomolasiguiente:
kk+=ii+jj;
Laexpresiónoperator+creaunnuevoobjetoInteger(untemporario)queseusacomoargumentorvparaeloperadoroperator+=.Esteobjetotemporalsedestruyetanprontocomodejadenecesitarse.12.3.OperadoressobrecargablesAunquepuedesobrecargarcasitodoslosoperadoresdisponiblesenC,elusodeoperadoressobrecargadosesbastanterestrictivo.Enparticular,nopuedecombinaroperadoresqueactualmentenotienensignicadoenC(como**pararepresentarlapotencia),nopuedecambiarlaprecedenciadeevaluacióndeoperadores,ytampocoelnúmerodeargumentosrequeridosporunoperador.Estasrestriccionesexistenparaprevenirquelacreacióndenuevosoperadoresofusquenelsignicadoenlugardeclaricarlo.Lassiguientesdossubseccionesmuestranejemplosdetodoslosoperadoresnor-males,sobrecargadosenlaformahabitual.12.3.1.OperadoresunariosElsiguienteejemplomuestralasintaxisparasobrecargartodoslosoperadoresunarios,enambasformas:comofuncionesglobales(funcionesfriend,nométodos)ycomométodos.EstasexpandiránlaclaseIntegervistapreviamenteyañadiráunanuevaclasebyte.Elsignicadodesusoperadoresparticularesdependerádelaformaenquelosuse,peroconsiderealosprogramadoresdelgrupoantesdehaceralgoinesperado.Heaquíuncatálogodetodaslasfuncionesunarias:
//:C12:OverloadingUnaryOperators.cpp
#include&#xiost;&#xream;
usingnamespacestd;
//Non-memberfunctions:
classInteger{
longi;
Integer*This(){returnthis;}
public:
Integer(longll=0):i(ll){}
//Nosideeffectstakesconst&argument:
333
i
i
“Volumen1”—2012/1/12—13:52—page337—#375i
i
i
i
i
i
12.3.Operadoressobrecargables
defuncionesmiembro,sielcompiladorve++b,generaunallamadaaB::operat-or++()ysiveb++generaunallamadaaB::operator++(int).Todoloqueelusuarioveesquesellamaaunafuncióndiferenteparalasver-sionespostjaypreja.Internamente,sinembargo,lasdosllamadasdefuncionestienendiferentesrmas,asíqueconectancondoscuerposdiferentes.Elcompiladorpasaunvalorconstantecticioparaelargumentoint(elcualnuncaseproporcio-nadaporunidenticadorporqueelvalornuncaseusa)paragenerarlasdiferentesrmasparalaversiónpostja.12.3.2.OperadoresbinariosLossiguienteslistadosrepitenelejemplodeOverloadingUnaryOperators.cppparalosoperadoresbinariospresentándoleunejemplodetodoslosoperadoresquepuedaquerersobrecargar.Denuevosemuestranambasversiones,laglobalylademétodo.
//:C12:Integer.h
//Non-memberoverloadedoperators
#ifndefINTEGER_H
#defineINTEGER_H
#include&#xiost;&#xream;
//Non-memberfunctions:
classInteger{
longi;
public:
Integer(longll=0):i(ll){}
//Operatorsthatcreatenew,modifiedvalue:
friendconstInteger
operator+(constInteger&left,
constInteger&right);
friendconstInteger
operator-(constInteger&left,
constInteger&right);
friendconstInteger
operator*(constInteger&left,
constInteger&right);
friendconstInteger
operator/(constInteger&left,
constInteger&right);
friendconstInteger
operator%(constInteger&left,
constInteger&right);
friendconstInteger
operator^(constInteger&left,
constInteger&right);
friendconstInteger
operator&(constInteger&left,
constInteger&right);
friendconstInteger
operator|(constInteger&left,
constInteger&right);
friendconstInteger
operator(constInteger&left,
constInteger&right);
friendconstInteger
337
i
i
“Volumen1”—2012/1/12—13:52—page338—#376i
i
i
i
i
i
Capítulo12.Sobrecargadeoperadores
operator��(constInteger&left,
constInteger&right);
//Assignmentsmodify&returnlvalue:
friendInteger&
operator+=(Integer&left,
constInteger&right);
friendInteger&
operator-=(Integer&left,
constInteger&right);
friendInteger&
operator*=(Integer&left,
constInteger&right);
friendInteger&
operator/=(Integer&left,
constInteger&right);
friendInteger&
operator%=(Integer&left,
constInteger&right);
friendInteger&
operator^=(Integer&left,
constInteger&right);
friendInteger&
operator&=(Integer&left,
constInteger&right);
friendInteger&
operator|=(Integer&left,
constInteger&right);
friendInteger&
operator��=(Integer&left,
constInteger&right);
friendInteger&
operator(Integer&left,
constInteger&right);
//Conditionaloperatorsreturntrue/false:
friendint
operator==(constInteger&left,
constInteger&right);
friendint
operator!=(constInteger&left,
constInteger&right);
friendint
operator(constInteger&left,
constInteger&right);
friendint
operator�(constInteger&left,
constInteger&right);
friendint
operator(constInteger&left,
constInteger&right);
friendint
operator�=(constInteger&left,
constInteger&right);
friendint
operator&&(constInteger&left,
constInteger&right);
friendint
operator||(constInteger&left,
constInteger&right);
338
i
i
“Volumen1”—2012/1/12—13:52—page339—#377i
i
i
i
i
i
12.3.Operadoressobrecargables
//Writethecontentstoanostream:
voidprint(std::ostream&os)const{osi;}
};
#endif//INTEGER_H///:~
//:C12:Integer.cpp{O}
//Implementationofoverloadedoperators
#include"Integer.h"
#include"../require.h"
constInteger
operator+(constInteger&left,
constInteger&right){
returnInteger(left.i+right.i);
}
constInteger
operator-(constInteger&left,
constInteger&right){
returnInteger(left.i-right.i);
}
constInteger
operator*(constInteger&left,
constInteger&right){
returnInteger(left.i*right.i);
}
constInteger
operator/(constInteger&left,
constInteger&right){
require(right.i!=0,"dividebyzero");
returnInteger(left.i/right.i);
}
constInteger
operator%(constInteger&left,
constInteger&right){
require(right.i!=0,"modulobyzero");
returnInteger(left.i%right.i);
}
constInteger
operator^(constInteger&left,
constInteger&right){
returnInteger(left.i^right.i);
}
constInteger
operator&(constInteger&left,
constInteger&right){
returnInteger(left.i&right.i);
}
constInteger
operator|(constInteger&left,
constInteger&right){
returnInteger(left.i|right.i);
}
constInteger
operator(constInteger&left,
constInteger&right){
returnInteger(left.iright.i);
339
i
i
“Volumen1”—2012/1/12—13:52—page342—#380i
i
i
i
i
i
Capítulo12.Sobrecargadeoperadores
out"c1=";c1.print(out);\
out",c2=";c2.print(out);\
out";c1"#OP"c2produces";\
(c1OPc2).print(out);\
outendl;
TRY(+)TRY(-)TRY(*)TRY(/)
TRY(%)TRY(^)TRY(&)TRY(|)
TRY()TRY&#x-600;&#x-600;()TRY(+=)TRY(-=)
TRY(*=)TRY(/=)TRY(%=)TRY(^=)
TRY(&=)TRY(|=)TRY��(=)TRY()
//Conditionals:
#defineTRYC(OP)\
out"c1=";c1.print(out);\
out",c2=";c2.print(out);\
out";c1"#OP"c2produces";\
out(c1OPc2);\
outendl;
TRYC()TRYC&#x-600;()TRYC(==)TRYC(!=)TRYC()
TRYC�(=)TRYC(&&)TRYC(||)
}
intmain(){
cout"friendfunctions"endl;
Integerc1(47),c2(9);
h(c1,c2);
}///:~
//:C12:Byte.h
//Memberoverloadedoperators
#ifndefBYTE_H
#defineBYTE_H
#include"../require.h"
#include&#xiost;&#xream;
//Memberfunctions(implicit"this"):
classByte{
unsignedcharb;
public:
Byte(unsignedcharbb=0):b(bb){}
//Nosideeffects:constmemberfunction:
constByte
operator+(constByte&right)const{
returnByte(b+right.b);
}
constByte
operator-(constByte&right)const{
returnByte(b-right.b);
}
constByte
operator*(constByte&right)const{
returnByte(b*right.b);
}
constByte
operator/(constByte&right)const{
require(right.b!=0,"dividebyzero");
returnByte(b/right.b);
}
342
i
i
“Volumen1”—2012/1/12—13:52—page346—#384i
i
i
i
i
i
Capítulo12.Sobrecargadeoperadores
sos.(Enalgunoscasosescorrecto,perosiempredeberíatenerloenmentecuandoescribaoperator=).Todoslosoperadoresmostradosenlosdosejemplospreviossonsobrecargadosparamanejaruntiposimple.Tambiénesposiblesobrecargaroperadoresparamane-jartiposcompuestos,demaneraquepuedasumarmanzanasanaranjas,porejem-plo.Antesdequeempieceunasobrecargaexhaustivadeoperadores,noobstante,deberíamirarlaseccióndeconversiónautomáticadetiposmásadelanteenesteca-pitulo.Amenudo,unaconversióndetiposenellugaradecuadopuedeahorrarleunmontóndeoperadoressobrecargados.12.3.3.ArgumentosyvaloresderetornoPuedeparecerunpococonfusoinicialmentecuandolealosarchivosOverloadingUnaryOperators.cpp,Integer.hyByte.hyveatodaslasmanerasdiferentesenquesepasanyde-vuelvenlosargumentos.Aunqueustedpuedapasarydevolverargumentosdelaformaquepreera,lasdecisionesenestosejemplosnosehanrealizadoalazar.Si-guenunpatrónlógico,elmismoquequerráusarenlamayoríadesusdecisiones.1.Comoconcualquierargumentodefunción,sisólonecesitaleerelargumentoynocambiarlo,lousualespasarlocomounareferenciaconst.Normalmen-teoperacionesaritméticas(como+y-,etc.)ybooleanasnocambiaránsusargumentos,asíquepasarlascomounareferenciaconstesloqueverémayo-ritariamente.Cuandolafunciónesunmétodo,estosetraduceenunamétodoconst.Sóloconlosoperadoresdeasignación(como+=)yoperator=,quecambianelargumentodelapartederecha,noeselargumentoderechounaconstante,perotodavíasepasaendirecciónporqueserácambiado.2.Eltipodevalorderetornoquedebeseleccionardependedelsignicadoes-peradodeloperador.(Otravez,puedehacercualquiercosaquedeseeconlosargumentosyconlosvaloresderetorno).Sielefectodeloperadoresproducirunnuevovalor,necesitarágenerarunnuevoobjetocomoelvalorderetorno.Porejemplo,Integer::operator+debeproducirunobjetoIntegerqueeslasumadelosoperandos.Esteobjetosedevuelveporvalorcomounacons-tanteasíqueelresultadonosepuedemodicarcomoun«valorizquierdo».3.Todaslosoperadoresdeasignaciónmodicanelvalorizquierdo.Parapermitirqueelresultadodelaasignaciónpuedaserusadoenexpresionesencadenadas,comoa=b=c,seesperaquedevuelvaunareferenciaalmismovaloriz-quierdoqueacabadesermodicado.Pero¿deberíaserestareferenciaconstonoconst?.Aunqueleaa=b=cdeizquierdaaderecha,elcompiladorlaanalizadederechaaizquierda,asíquenoestáobligadoadevolverunareferen-cianoconstparasoportarasignacionesencadenadas.Sinembargo,lagenteavecesesperasercapazderealizarunaoperaciónsobreelelementodeacabadeserasignado,como(a=b).func();parallamarafuncdeadespuésdeasignarleb.Deesemodo,elvalorderetornoparatodoslosoperadoresdeasignacióndeberíaserunareferencianoconstparaelvalorizquierdo.4.Paralosoperadoreslógicos,todoelmundoesperaobtenerenelpeordeloscasosuntipoint,yenelmejoruntipobool.(LaslibreríasdesarrolladasantesdequeloscompiladoresdeC++soportaraneltipoincorporadoboolusabanuntipointountypedefequivalente).Losoperadoresdeincrementoydecrementopresentanundilemaacausadelasversionespostjaypreja.Ambasversionescambianelobjetoyportantonopue-
346
i
i
“Volumen1”—2012/1/12—13:52—page348—#386i
i
i
i
i
i
Capítulo12.Sobrecargadeoperadores
valorderetornoexterno.Latercera,sellamaaldestructorparatmpcuandosaledelámbito.Encontraste,laaproximaciónde«devolverunobjetotemporal»funcionadema-nerabastantediferente.Cuandoelcompiladorveeso,sabequenotieneotrarazónparacrearlomasqueparadevolverlo.Elcompiladoraprovechalaventajaqueofreceparaconstruirelobjetodirectamenteenlalocalizacióndelvalorderetornoexternoalafunción.Estonecesitadeunasolayordinariallamadaalconstructor(lallamadaalconstructordecopianoesnecesaria)ynohayllamadasaldestructorporquenuncasecreaunobjetolocal.Deestamanera,norequierenadamásqueelconocimien-todelprogramador,yessignicativamentemaseciente.Estoamenudosellamaoptimizacióndelvalorderetorno.12.3.4.OperadorespocousualesVariosoperadoresadicionalestienenunaformaligeramentediferentedeserso-brecargados.Elsubíndice,operator[]debeserunmétodoyprecisadeunúnicoargumen-to.Dadoqueoperator[]implicaqueelobjetoqueestásiendoutilizadocomounarray,amenudodevolveráunareferenciadeesteoperador,asíquepuedesercon-venientementeusadoenlapartederechadeunsignodeigualdad.Esteoperadoresmuycomúnmentesobrecargado;veráejemplosenelrestodellibro.Losoperadoresnewydeletecontrolanlareservadinámicadealmacenamientoysepuedensobrecargardemuchasmanerasdiferentes.Estetemasecubreenelcapitulo13.EloperadorcomaEloperadorcomasellamacuandoaparecedespuésdeunobjetodeltipoparaelqueestádenido.Sinembargo,«operator,»nosellamaparalistasdeargumentosdefunciones,sóloparaobjetosfueradeeselugarseparadosporcomas.Noparecehaberunmontóndeusosprácticosparaesteoperador,soloesporconsistenciadellenguaje.Heaquíunejemploquemuestracomolafuncióncomasepuedellamarcuandoapareceantesdeunobjeto,asícomodespués:
//:C12:OverloadingOperatorComma.cpp
#include&#xiost;&#xream;
usingnamespacestd;
classAfter{
public:
constAfter&operator,(constAfter&)const{
cout"After::operator,()"endl;
return*this;
}
};
classBefore{};
Before&operator,(int,Before&b){
cout"Before::operator,()"endl;
returnb;
}
348
i
i
“Volumen1”—2012/1/12—13:52—page349—#387i
i
i
i
i
i
12.3.Operadoressobrecargables
intmain(){
Aftera,b;
a,b;//Operatorcommacalled
Beforec;
1,c;//Operatorcommacalled
}///:~
Lasfuncionesglobalespermitensituarlacomaantesdelobjetoencuestión.Elusomostradoesbastanteoscuroycuestionable.Probablementepodríaunalistase-paradaporcomascomopartedeunaexpresiónmáscomplicada,esdemasiadore-nadoenlamayoríadelasocasiones.Eloperador�-Eloperador�-seusageneralmentecuandoquierehacerqueunobjetoparezcaunpuntero.Estetipodeobjetosesuelellamarpunterointeligenteomásamenudoporsuequivalenteeninglés:smartpointer.Resultanespecialmenteutilessiquiere«envolver»unaclaseconunpunteroparahacerqueesepunteroseaseguro,oenlaformacomúndeuniterador,queesunobjetoquesemueveatravésdeunacolecciónocontenedordeotrosobjetosylosseleccionadeunoenunocadavez,sinproporcionaraccesodirectoalaimplementacióndelcontenedor.(Amenudoencontraráiteradoresycontenedoresenlaslibreríasdeclases,comoenlaBibliotecaEstándardeC++,descritaenelvolumen2deestelibro).Eloperadordeindireccióndepunteros(*)debeserunmétodo.Tieneotrasrestric-cionesatípicas:debedevolverunobjeto(ounareferenciaaunobjeto)quetambiéntengaunoperadordeindireccióndepunteros,odebedevolverunpunteroquepue-daserusadoparaencontraraloqueapuntalaechadeloperadordeindirecióndepunteros.Heaquíunejemplosimple:
//:C12:SmartPointer.cpp
#include&#xiost;&#xream;
#include&#xvect;&#xor00;
#include"../require.h"
usingnamespacestd;
classObj{
staticinti,j;
public:
voidf()const{couti++endl;}
voidg()const{coutj++endl;}
};
//Staticmemberdefinitions:
intObj::i=47;
intObj::j=11;
//Container:
classObjContainer{
vectorObj*�a;
public:
voidadd(Obj*obj){a.push_back(obj);}
friendclassSmartPointer;
};
349
i
i
“Volumen1”—2012/1/12—13:52—page352—#390i
i
i
i
i
i
Capítulo12.Sobrecargadeoperadores
Obj*operator�-()const{
require(oc.a[index]!=0,"Zerovalue"
"returnedby�SmartPointer::operator-()");
returnoc.a[index];
}
};
//Functiontoproduceasmartpointerthat
//pointstothebeginningoftheObjContainer:
SmartPointerbegin(){
returnSmartPointer(*this);
}
};
intmain(){
constintsz=10;
Objo[sz];
ObjContaineroc;
for(inti=0;isz;i++)
oc.add(&o[i]);//Fillitup
ObjContainer::SmartPointersp=oc.begin();
do{
sp�-f();//Pointerdereferenceoperatorcall
sp�-g();
}while(++sp);
}///:~
Ademásdelanidamientodelaclase,haysolodosdiferenciasaquí.Laprimeraesladeclaracióndelaclaseparaquepuedaserfriend:
classSmartPointer;
friendSmartPointer;
Elcompiladordebesaberprimeroquelaclaseexiste,antesdequeseledigaquees«amiga».LasegundadiferenciaesenObjContainerdondeelmétodobegin()produ-ceelSmartPointerqueapuntaalprincipiodelasecuenciadelObjContainer.Aunquerealmenteessóloporconveniencia,esadecuadoporquesiguelamanerahabitualdelalibreríaestándardeC++.Operador�-*Eloperador�-*esunoperadorbinarioquesecomportacomotodoslosotrosoperadoresbinarios.Seproporcionaparaaquellassituacionesenlasquequieraimi-tarelcomportamientoproducidoporlasintaxisincorporadapunteroamiembro,des-critaenelcapituloanterior.Igualque�operator-,eloperadordeindireccióndepunteroamiembroseusanormalmenteconalgunaclasedeobjetosquerepresentanun«punterointeligente»,aunqueelejemplomostradoaquíserámássimpleparaqueseacomprensible.Eltrucocuandosedene�operator-*esquedebedevolverunobjetoparaelqueoperator()puedaserllamadoconlosargumentosparalafunciónmiembroqueustedllama.Lallamadaafunciónoperator()debeserunmétodo,yesúnicoenqueper-mitecualquiernúmerodeargumentos.Hacequeelobjetoparezcarealmenteuna
352
i
i
“Volumen1”—2012/1/12—13:52—page353—#391i
i
i
i
i
i
12.3.Operadoressobrecargables
función.Aunquepodríadenirvariasfuncionessobrecargadasoperator()condiferentesargumentos,amenudoseusaparatiposquesolotienenunaoperaciónsimple,oalmenosunaespecialmentedestacada.EnelVolumen2veráquelaLibreríaEstándardeC++usaeloperadordellamadaafunciónparacrear«objetos-función».Paracrearun�operator-*debeprimerocrearunaclaseconunoperator(-)queseaeltipodeobjetoque�operator-*devolverá.Estaclasedebe,dealgúnmodo,capturarlainformaciónnecesariaparaquecuandooperator()seallamada(loquesucedeautomáticamente),elpunteroamiembroseaindireccionadoparaelobjeto.Enelsiguienteejemplo,elconstructordeFunctionObjectcapturayalma-cenaelpunteroalobjetoyelpunteroalafunciónmiembro,yentoncesoperator()losusaparahacerlaverdaderallamadapunteroamiembro:
//:C12:PointerToMemberOperator.cpp
#include&#xiost;&#xream;
usingnamespacestd;
classDog{
public:
intrun(inti)const{
cout"run\n";
returni;
}
inteat(inti)const{
cout"eat\n";
returni;
}
intsleep(inti)const{
cout"ZZZ\n";
returni;
}
typedefint(Dog::*PMF)(int)const;
//operator�-*mustreturnanobject
//thathasanoperator():
classFunctionObject{
Dog*ptr;
PMFpmem;
public:
//Savetheobjectpointerandmemberpointer
FunctionObject(Dog*wp,PMFpmf)
:ptr(wp),pmem(pmf){
cout"FunctionObjectconstructor\n";
}
//Makethecallusingtheobjectpointer
//andmemberpointer
intoperator()(inti)const{
cout"FunctionObject::operator()\n";
return(ptr�-*pmem)(i);//Makethecall
}
};
FunctionObjectoperator�-*(PMFpmf){
cout&#x-600;"operator-*"endl;
returnFunctionObject(this,pmf);
}
};
intmain(){
Dogw;
353
i
i
“Volumen1”—2012/1/12—13:52—page354—#392i
i
i
i
i
i
Capítulo12.Sobrecargadeoperadores
Dog::PMFpmf=&Dog::run;
cout(w&#x-600;-*pmf)(1)endl;
pmf=&Dog::sleep;
cout(w&#x-600;-*pmf)(2)endl;
pmf=&Dog::eat;
cout(w&#x-600;-*pmf)(3)endl;
}///:~
Dogtienetresmétodos,todostomanunargumentoenteroydevuelvenunentero.PMCesuntypedefparasimplicarladenicióndeunpunteroamiembroparalosmétodosdeDog.UnaFunctionObjectescreadaydevueltapor�operator-*.Desecuentaque�operator-*conoceelobjetoparaelquepunteroamiembroestásiendolla-mado(this)yelpunteroamiembro,ylospasaalconstructorFunctionObjectquealmacenasusvalores.Cuandosellamaa�operator-*,elcompiladorinme-diatamentelorevuelveyllamaaoperator()paraelvalorderetornodeoper-�ator-*,pasándolelosargumentosquelefueronpasadosa�operator-*.Fu-nctionObject::operator()tomalosargumentosedesreferenciaelpunteroamiembro«real»usandolospunterosaobjetoyamiembroalmacenados.Percátesedequeloqueestáocurriendoaquí,justocomocon�operator-,seinsertaenlamitaddelallamadaa�operator-*.Estopermiterealizaralgunasoperacionesadicionalessisenecesita.Elmecanismo�operator-*implementadoaquísolotrabajaparafuncionesmiembroquetomanunargumentoenteroydevuelvenotroentero.Estoesunali-mitación,perosiintentacrearmecanismossobrecargadosparacadaposibilidaddi-ferente,veráqueesunatareaprohibitiva.Afortunadamente,elmecanismodeplan-tillasdeC++(descritoelelultimocapitulodeestelibro,yenelvolumen2)estádiseñadoparamanejarsemejanteproblema.12.3.5.OperadoresquenopuedesobrecargarHayciertaclasedeoperadoresenelconjuntodisponiblequenopuedensersobre-cargados.Larazóngeneralparaestarestriccióneslaseguridad.Siestosoperadoresfuesensobrecargables,podríadealgúnmodoarriesgaroromperlosmecanismosdeseguridad,hacerlascosasmasdifícilesoconfundirlascostumbresexistentes.1.Eloperadordeseleccióndemiembrosoperator..Actualmente,elpuntotie-nesignicadoparacualquiermiembrodeunaclase,perosisepudierasobre-cargar,nosepodríaaccederamiembrosdelaformanormal;enlugardeesodeberíausarunpunteroylaecha�operator-.2.Laindireccióndepunterosamiembrosoperator.*porlamismarazónqueoperator..3.Nohayunoperadordepotencia.Laelecciónmáspopularparaésteeraoper-ator**deFortram,peroprovocacasosdeanálisisgramaticaldifíciles.Ctam-pocotieneunoperadordepotencia,asíqueC++noparecetenernecesidaddeunoporquesiemprepuederealizarunallamadaaunafunción.Unoperadordepotenciaañadiráunanotaciónadecuada,peroningunanuevafuncionalidadacuentadeunamayorcomplejidaddelcompilador.4.Nohayoperadoresdenidosporelusuario.Estoes,nopuedecrearnuevosoperadoresquenoexistanya.Unapartedelproblemaescomodeterminarla
354
i
i
“Volumen1”—2012/1/12—13:52—page356—#394i
i
i
i
i
i
Capítulo12.Sobrecargadeoperadores
for(intj=0;jia.sz;j++)
is��ia.i[j];
returnis;
}
intmain(){
stringstreaminput("47345692103");
IntArrayI;
input��I;
I[4]=-1;//Useoverloadedoperator[]
coutI;
}///:~
Estaclasecontienetambiénunoperadorsobrecargadooperator[]elcualde-vuelveunareferenciaaunvalorlegítimoenelarray.Dadoquedevuelveunarefe-rencia,laexpresión:
I[4]=-1;
Nosóloparecemuchomásadecuadaquesiseusaranpunteros,tambiéncausaelefectodeseado.Esimportantequelosoperadoresdedesplazamientosobrecargadossepasenydevuelvanporreferencia,paraqueloscambiosafectenalosobjetosexternos.Enlasdenicionesdelasfunciones,expresionescomo:
osia.i[j];
provocanqueseanllamadaslasfuncionesdelosoperadoressobrecargados(estoes,aquellasdenidaseniostream).Enestecaso,lafunciónllamadaesostream&operator(int)dadoqueia[i].jseresuelveaint.Unavezquelasoperacionessehanrealizadoenistreamoenostreamsede-vuelveparaquesepuedausarenexpresionesmáscomplicadas.Enmain()seusaunnuevotipodeiostream:elstringstream(declaradoen&#xsstr;êm0;).Esunaclasequetomaunacadena(quesepuedecreardeunarraydechar,comoseveaquí)yloconvierteenuniostream.Enelejemplodearriba,estosignicaquelosoperadoresdedesplazamientopuedensercomprobadossinabrirunarchivoosinescribirdatosenlalíneadecomandos.Lamaneramostradaenesteejemploparaelextractoryelinsertadoresestándar.Siquierecrearestosoperadoresparasupropiaclase,copieelprototipodelafunciónylostiposderetornodearribaysigaelestilodelcuerpo.12.4.1.DirectricesbásicasMurray1sugiereestasreglasdeestiloparaelegirentremiembrosynomiembros:
1RobMurray,C++Strategies&Tactics,AddisonWesley,1993,pagina47.
356
i
i
“Volumen1”—2012/1/12—13:52—page358—#396i
i
i
i
i
i
Capítulo12.Sobrecargadeoperadores
Feefum=fi;//Fee(Fi)
}///:~
Cuandosetrataconelsigno=,esimportantemantenerladiferenciaenmente:Sielobjetonohasidocreadotodavía,serequiereunainicialización;enotrocasoseusaeloperadordeasignación=.Esinclusomejorevitarescribircódigoqueusa=paralainicialización;encambio,usesiemprelamaneradelconstructorexplícito.Lasdosconstruccionesconelsignoigualseconviertenen:
Feefee(1);
Feefum(fi);
Deestamanera,evitaráconfundirasuslectores.12.5.1.Comportamientodeloperador=EnInteger.hyenByte.hvimosqueeloperador=sólopuedeserunafunciónmiembro.Estáíntimamenteligadoalobjetoquehayenlaparteizquierdadel=.Sifueseposibledeniroperator=deformaglobal,entoncespodríaintentarredenirelsigno=dellenguaje:
intoperator=(int,MyType);//Global=!Nopermitido!
Elcompiladorevitaestasituaciónobligandoleahacerunmétodooperator=.Cuandocreeunoperator=,debecopiartodalainformaciónnecesariadesdeelobjetodelapartederechaalobjetoactual(esdecir,elobjetoparaelqueoperato-r=estásiendollamado)pararealizarloqueseaqueconsidere«asignación»parasuclase.Paraobjetossimples,estoestrivial:
//:C12:SimpleAssignment.cpp
//Simpleoperator=()
#include&#xiost;&#xream;
usingnamespacestd;
classValue{
inta,b;
floatc;
public:
Value(intaa=0,intbb=0,floatcc=0.0)
:a(aa),b(bb),c(cc){}
Value&operator=(constValue&rv){
a=rv.a;
b=rv.b;
c=rv.c;
return*this;
}
friendostream&
operator(ostream&os,constValue&rv){
returnos"a="rv.a",b="
rv.b",c="rv.c;
}
358
i
i
“Volumen1”—2012/1/12—13:52—page359—#397i
i
i
i
i
i
12.5.Sobrecargarlaasignación
};
intmain(){
Valuea,b(1,2,3.3);
cout"a:"aendl;
cout"b:"bendl;
a=b;
cout"aafterassignment:"aendl;
}///:~
Aquí,elobjetodelaparteizquierdadeligualcopiatodosloselementosdelobjetodelapartederecha,yentoncesdevuelveunareferenciaasímismo,loquepermitecrearexpresionesmáscomplejas.Esteejemploincluyeunerrorcomón.Cuandoasignanedosobjetosdelmismotipo,siempredeberíacomprobarprimerolaauto-asignación:¿Estáasignadoelob-jetoasímismo?.Enalgunoscasoscomoéste,esinofensivosirealizalaoperacióndeasignaciónentodocaso,perosiserealizancambiosalaimplementacióndelaclase,puedeserimportanteysinolotomaconunacuestióndecostumbre,puedeolvidarloyprovocarerroresdifícilesdeencontrar.Punterosenclases¿Quéocurresielobjetonoestansimple?.Porejemplo,¿quépasasielobjetocontienepunterosaotrosobjetos?.Sólocopiarelpunterosignicaqueobtendrádosobjetosqueapuntanalamismalocalizacióndememoria.Ensituacionescomoésta,necesitahaceralgodecontabilidad.Haydosaproximacionesaesteproblema.Latécnicamássimpleescopiarloquequieraqueapuntaelpunterocuandorealizaunaasignaciónounaconstruccióndecopia.Estoessencillo:
//:C12:CopyingWithPointers.cpp
//Solvingthepointeraliasingproblemby
//duplicatingwhatispointedtoduring
//assignmentandcopy-construction.
#include"../require.h"
#include&#xstri;&#xng00;
#include&#xiost;&#xream;
usingnamespacestd;
classDog{
stringnm;
public:
Dog(conststring&name):nm(name){
cout"CreatingDog:"*thisendl;
}
//Synthesizedcopy-constructor&operator=
//arecorrect.
//CreateaDogfromaDogpointer:
Dog(constDog*dp,conststring&msg)
:nm(dp�-nm+msg){
cout"Copieddog"*this"from"
*dpendl;
}
~Dog(){
359
i
i
“Volumen1”—2012/1/12—13:52—page360—#398i
i
i
i
i
i
Capítulo12.Sobrecargadeoperadores
cout"DeletingDog:"*thisendl;
}
voidrename(conststring&newName){
nm=newName;
cout"Dogrenamedto:"*thisendl;
}
friendostream&
operator(ostream&os,constDog&d){
returnos"["d.nm"]";
}
};
classDogHouse{
Dog*p;
stringhouseName;
public:
DogHouse(Dog*dog,conststring&house)
:p(dog),houseName(house){}
DogHouse(constDogHouse&dh)
:p(newDog(dh.p,"copy-constructed")),
houseName(dh.houseName
+"copy-constructed"){}
DogHouse&operator=(constDogHouse&dh){
//Checkforself-assignment:
if(&dh!=this){
p=newDog(dh.p,"assigned");
houseName=dh.houseName+"assigned";
}
return*this;
}
voidrenameHouse(conststring&newName){
houseName=newName;
}
Dog*getDog()const{returnp;}
~DogHouse(){deletep;}
friendostream&
operator(ostream&os,constDogHouse&dh){
returnos"["dh.houseName
"]contains"*dh.p;
}
};
intmain(){
DogHousefidos(newDog("Fido"),"FidoHouse");
coutfidosendl;
DogHousefidos2=fidos;//Copyconstruction
coutfidos2endl;
fidos2.getDog�()-rename("Spot");
fidos2.renameHouse("SpotHouse");
coutfidos2endl;
fidos=fidos2;//Assignment
coutfidosendl;
fidos.getDog�()-rename("Max");
fidos2.renameHouse("MaxHouse");
}///:~
Dogesunaclasesimplequecontienesolounacadenaconelnombredelperro.
360
i
i
“Volumen1”—2012/1/12—13:52—page361—#399i
i
i
i
i
i
12.5.Sobrecargarlaasignación
Sinembargo,generalmentesabrácuandolesucedealgoalperroporquelosconstruc-toresydestructoresimprimeninformacióncuandoseinvocan.FíjesequeelsegundoconstructoresunpococomounconstructordecopiaexceptoquetomaunpunteroaDogenvezdeunareferencia,ytieneunsegundoargumentoqueesunmensajeaserconcatenadoconelnombredelperro.Estosehaceasíparaayudararastrearelcomportamientodelprograma.Puedeverquecuandounmétodoimprimeinformación,noaccedeaesainforma-cióndirectamentesinoquemanda*thisacout.Ésteasuvezllamaaostreamoperator.Esaconsejablehacerestoasídadoquesiquierereformatearlamane-raenlaqueinformacióndelperroesmostrada(comohiceañadiendoel«[»yel«]»)solonecesitahacerloenunlugar.UnaDogHousecontieneunDog*ydemuestralascuatrofuncionesquesiemprenecesitarádenircuandosusclasescontenganpunteros:todoslosconstructoresne-cesariosusuales,elconstructordecopia,operator=(sedeneosedeshabilita)yundestructor.Operator=compruebalaauto-asignacióncomounacuestióndees-tilo,inclusoaunquenoesestrictamentenecesarioaquí.Estovirtualmenteeliminalaposibilidaddequeolvidecomprobarlaauto-asignaciónsicambiaelcódigo.ContabilidaddereferenciasEnelejemplodearriba,elconstructordecopiayeloperador=realizanunaco-piadeloqueapuntaelpuntero,yeldestructorloborra.Sinembargo,sisuobjetorequiereunagrancantidaddememoriaounagraninicializaciónja,alomejorpuedequererevitarestacopia.Unaaproximacióncomúnaesteproblemasellamaconteodereferencias.Seledainteligenciaalobjetoqueestásiendoapuntadodetalfor-maquesabecuántosobjetosleestánapuntado.Entonceslaconstrucciónporcopiaolaasignaciónconsisteenañadirotropunteroaunobjetoexistenteeincrementarlacuentadereferencias.Ladestrucciónconsisteenreducirestacuentadereferenciasydestruirelobjetosilacuentallegaacero.¿Peroquepasasiquiereescribirelobjeto(Dogenelejemploanterior)?.MásdeunobjetopuedeestarusandoesteDogluegopodríaestarmodicandoelperrodealguienmásalavezqueelsuyo,locualnoparecesermuyamigable.Pararesol-veresteproblemade«solapamiento»seusaunatécnicaadicionalllamadacopia-en-escritura.Antesdeescribirunbloquedememoria,debeasegurarsequenadiemásloestáusando.Silacuentadereferenciaessuperiorauno,deberealizarunacopiapersonaldelbloqueantesdeescribirlo,detalmaneraquenomolesteelespaciodeotro.Heaquíunejemplosimpledeconteodereferenciasycopia-en-escritura:
//:C12:ReferenceCounting.cpp
//Referencecount,copy-on-write
#include"../require.h"
#include&#xstri;&#xng00;
#include&#xiost;&#xream;
usingnamespacestd;
classDog{
stringnm;
intrefcount;
Dog(conststring&name)
:nm(name),refcount(1){
cout"CreatingDog:"*thisendl;
}
//Preventassignment:
Dog&operator=(constDog&rv);
361
i
i
“Volumen1”—2012/1/12—13:52—page362—#400i
i
i
i
i
i
Capítulo12.Sobrecargadeoperadores
public:
//Dogscanonlybecreatedontheheap:
staticDog*make(conststring&name){
returnnewDog(name);
}
Dog(constDog&d)
:nm(d.nm+"copy"),refcount(1){
cout"Dogcopy-constructor:"
*thisendl;
}
~Dog(){
cout"DeletingDog:"*thisendl;
}
voidattach(){
++refcount;
cout"AttachedDog:"*thisendl;
}
voiddetach(){
require(refcount!=0);
cout"DetachingDog:"*thisendl;
//Destroyobjectifnooneisusingit:
if(--refcount==0)deletethis;
}
//ConditionallycopythisDog.
//CallbeforemodifyingtheDog,assign
//resultingpointertoyourDog*.
Dog*unalias(){
cout"UnaliasingDog:"*thisendl;
//Don'tduplicateifnotaliased:
if(refcount==1)returnthis;
--refcount;
//Usecopy-constructortoduplicate:
returnnewDog(*this);
}
voidrename(conststring&newName){
nm=newName;
cout"Dogrenamedto:"*thisendl;
}
friendostream&
operator(ostream&os,constDog&d){
returnos"["d.nm"],rc="
d.refcount;
}
};
classDogHouse{
Dog*p;
stringhouseName;
public:
DogHouse(Dog*dog,conststring&house)
:p(dog),houseName(house){
cout"CreatedDogHouse:"*thisendl;
}
DogHouse(constDogHouse&dh)
:p(dh.p),
houseName("copy-constructed"+
dh.houseName){
p�-attach();
362
i
i
“Volumen1”—2012/1/12—13:52—page363—#401i
i
i
i
i
i
12.5.Sobrecargarlaasignación
cout"DogHousecopy-constructor:"
*thisendl;
}
DogHouse&operator=(constDogHouse&dh){
//Checkforself-assignment:
if(&dh!=this){
houseName=dh.houseName+"assigned";
//Cleanupwhatyou'reusingfirst:
p�-detach();
p=dh.p;//Likecopy-constructor
p�-attach();
}
cout"DogHouseoperator=:"
*thisendl;
return*this;
}
//Decrementrefcount,conditionallydestroy
~DogHouse(){
cout"DogHousedestructor:"
*thisendl;
p�-detach();
}
voidrenameHouse(conststring&newName){
houseName=newName;
}
voidunalias(){p=p�-unalias();}
//Copy-on-write.Anytimeyoumodifythe
//contentsofthepointeryoumust
//firstunaliasit:
voidrenameDog(conststring&newName){
unalias();
p�-rename(newName);
}
//...orwhenyouallowsomeoneelseaccess:
Dog*getDog(){
unalias();
returnp;
}
friendostream&
operator(ostream&os,constDogHouse&dh){
returnos"["dh.houseName
"]contains"*dh.p;
}
};
intmain(){
DogHouse
fidos(Dog::make("Fido"),"FidoHouse"),
spots(Dog::make("Spot"),"SpotHouse");
cout"Enteringcopy-construction"endl;
DogHousebobs(fidos);
cout"Aftercopy-constructingbobs"endl;
cout"fidos:"fidosendl;
cout"spots:"spotsendl;
cout"bobs:"bobsendl;
cout"Enteringspots=fidos"endl;
spots=fidos;
cout"Afterspots=fidos"endl;
363
i
i
“Volumen1”—2012/1/12—13:52—page364—#402i
i
i
i
i
i
Capítulo12.Sobrecargadeoperadores
cout"spots:"spotsendl;
cout"Enteringself-assignment"endl;
bobs=bobs;
cout"Afterself-assignment"endl;
cout"bobs:"bobsendl;
//Commentoutthefollowinglines:
cout"Enteringrename(\"Bob\")"endl;
bobs.getDog�()-rename("Bob");
cout"Afterrename(\"Bob\")"endl;
}///:~
LaclaseDogeselobjetoapuntadoporDogHouse.Contieneunacuentaderefe-renciasymétodosparacontrolaryleerlacuentadereferencias.HayunconstructordecopiademodoquepuedecrearunnuevoDogapartirdeunoexistente.Lafunciónattach()incrementalacuentadereferenciasdeunDogparaindicarquehayotroobjetousándolo.Lafuncióndetach()decrementalacuentaderefe-rencias.Sillegaacero,entoncesnadiemasloestáusando,asíqueelmétododestruyesupropioobjetollamandoadeletethis.Antesdequehagacualquiermodicación(comorenombrarunDog),deberíaasegurarsedequenoestácambiandounDogquealgúnotroobjetoestáusando.HágalollamandoaDogHouse::unalias(),quellamaaDog::unalias().EstaúltimafuncióndevolveráelpunteroaDogexistentesilacuentadereferenciasesuno(loquesignicaquenadiemasestáusandoeseDog),peroduplicaráDogsiesacuentaesmayorqueuno.Elconstructordecopia,ademásdepedirsupropiamemoria,asignaDogalDogdelobjetofuente.Entonces,dadoqueahorahayunobjetomásusandoesebloquedememoria,incrementalacuentadereferenciasllamandoaDog::attach().Eloperador=trataconunobjetoquehasidocreadoenlaparteizquierdadel=,asíqueprimerodebelimpiarlollamandoadetach()paraeseDog,loquedestruiráelDogviejosinadiemásloestáusando.Entoncesoperator=repiteelcomporta-mientodelconstructordecopia.Adviertaqueprimerorealizacomprobacionesparadetectarcuandoestáasignandoelobjetoasímismo.Eldestructorllamaadetach()paradestruircondicionalmenteelDog.Paraimplementarlacopia-en-escritura,debecontrolartodaslasoperacionesqueescribenensubloquedememoria.Porejemplo,elmétodorenameDog()lepermitecambiarvaloresenelbloquedememoria.Peroprimero,llamaaunalias()paraevitarlamodicacióndeunDogsolapado(unDogconmásdeunobjetoDogHou-seapuntándole).YsinecesitacrearunpunteroaunDogdesdeunDogHousedebellamarprimeroaunalias()paraesepuntero.Lafunciónmain()compruebavariasfuncionesquedebenfuncionarcorrecta-menteparaimplementarlacuentadereferencias:elconstructor,elconstructordecopia,operator=yeldestructor.Tambiéncompruebalacopia-en-escriturallaman-doarenameDog().Heaquílasalida(despuésdeunpocodereformateo):
CreandoDog:[Fido],rc=1
CreadoDogHouse:[FidoHouse]
contiene[Fido],rc=1
CreandoDog:[Spot],rc=1
CreadoDogHouse:[SpotHouse]
364
i
i
“Volumen1”—2012/1/12—13:52—page366—#404i
i
i
i
i
i
Capítulo12.Sobrecargadeoperadores
Cargo&operator=(constCargo&){
cout"insideCargo::operator=()"endl;
return*this;
}
};
classTruck{
Cargob;
};
intmain(){
Trucka,b;
a=b;//Prints:"insideCargo::operator=()"
}///:~
Eloperador=generadoautomáticamenteparaTruckllamaaCargo::oper-ator=.Engeneral,noquerráqueelcompiladorhagaestoporusted.Conclasesdecual-quiersosticación(¡Especialmentesicontienenpunteros!)querrácreardeformaex-plicitaunoperator=.Sirealmentenoquierequelagenterealiceasignaciones,de-clareoperator=comounamétodoprivado.(Nonecesitadenirlaamenosquelaestéusandodentrodelaclase).12.6.ConversiónautomáticadetiposEnCyC++,sielcompiladorencuentraunaexpresiónounallamadaafunciónqueusauntipoquenoeselqueserequiere,amenudopodrárealizarunaconversiónautomáticadetiposdesdeeltipoquetienealtipoquenecesita.EnC++,puedecon-seguirestemismoefectoparalostiposdenidosporelusuariocreandofuncionesdeconversiónautomáticadetipos.Estasfuncionessepuedenverendosversiones:untipoparticulardeconstructoresyunoperadorsobrecargado.12.6.1.ConversiónporconstructorSideneunconstructorquetomacomosuúnicoargumentounobjeto(orefe-rencia)deotrotipo,eseconstructorpermitealcompiladorrealizarunaconversiónautomáticadetipos.Porejemplo:
//:C12:AutomaticTypeConversion.cpp
//Typeconversionconstructor
classOne{
public:
One(){}
};
classTwo{
public:
Two(constOne&){}
};
voidf(Two){}
366
i
i
“Volumen1”—2012/1/12—13:52—page367—#405i
i
i
i
i
i
12.6.Conversiónautomáticadetipos
intmain(){
Oneone;
f(one);//WantsaTwo,hasaOne
}///:~
Cuandoelcompiladorvequef()esinvocadapasandounobjetoOne,miraenladeclaracióndef()yvequerequiereunTwo.EntoncesbuscasihayalgunamaneradeconseguirunTwoapartirdeunOne,encuentraelconstructorTwo::Two(One)ylollama.PasaelobjetoTworesultanteaf().Enestecaso,laconversiónautomáticadetiposlehasalvadodelproblemadedenirdosversionessobrecargadasdef().SinembargoelcosteeslallamadaocultaalconstructordeTwo,quepuedeserimportantesiestápreocupadoporlaecienciadelasllamadasaf(),EvitarlaconversiónporconstructorHayvecesenquelaconversiónautomáticadetiposvíaconstructorpuedeoca-sionarproblemas.Paradesactivarlo,modiqueelconstructoranteponiéndolelapa-labrareservadaexplicit(quesólofuncionaconconstructores).AsísehahechoparamodicarelconstructordelaclaseTwoenelejemploanterior:
//:C12:ExplicitKeyword.cpp
//Usingthe"explicit"keyword
classOne{
public:
One(){}
};
classTwo{
public:
explicitTwo(constOne&){}
};
voidf(Two){}
intmain(){
Oneone;
//!f(one);//Noautoconversionallowed
f(Two(one));//OK--userperformsconversion
}///:~
HaciendoelconstructordeTwoexplícito,seledicealcompiladorquenorealiceningunaconversiónautomáticadetiposusandoeseconstructorenparticular(sísepodríanusarotrosconstructoresnoexplícitosdeesaclasepararealizarconversionesautomáticas).Sielusuarioquierequeocurraesaconversión,debeescribirelcodigonecesario.Enelcódigodearriba,f(Two(one))creaunobjetotemporaldetipoT-woapartirdeone,justocomoelcompiladorhizoautomáticamenteenlaversiónanterior.
367
i
i
“Volumen1”—2012/1/12—13:52—page368—#406i
i
i
i
i
i
Capítulo12.Sobrecargadeoperadores
12.6.2.ConversiónporoperadorLasegundaformadeproducirconversionesautomáticasdetipoesatravésdelasobrecargadeoperadores.Puedecrearunmétodoquetomeeltipoactualyloconviertaaltipodeseadousandolapalabrareservadaoperatorseguidadeltipoalquequiereconvertir.Estaformadesobrecargadeoperadoresesúnicaporqueparecequenoseespecicauntipoderetorno--eltipoderetornoeselnombredeloperadorqueestásobrecargando.Heaquíunejemplo:
//:C12:OperatorOverloadingConversion.cpp
classThree{
inti;
public:
Three(intii=0,int=0):i(ii){}
};
classFour{
intx;
public:
Four(intxx):x(xx){}
operatorThree()const{returnThree(x);}
};
voidg(Three){}
intmain(){
Fourfour(1);
g(four);
g(1);//CallsThree(1,0)
}///:~
Conlatécnicadelconstructor,laclasedestinorealizalaconversión,peroconlosoperadores,larealizalaclaseorigen.Elvalordelatécnicadelconstructoresquepuedeañadirunanuevarutadeconversiónaunsistemaexistentealcrearunanuevaclase.Sinembargo,creandounconstructorconunúnicoargumentosiempredeneunaconversiónautomáticadetipos(inclusosirequieremásdeunargumentosielrestodelosargumentostieneunvalorpordefecto),quepuedenoserloquedesea(encuyocasopuededesactivarlousandoexplicit).Además,nohayningunafor-madeusarunaconversiónporconstructordesdeuntipodenidoporelusuarioauntipoincorporado;esosóloesposibleconlasobrecargadeoperadores.ReexividadUnadelasrazonesmásconvenientesparausaroperadoressobrecargadosglo-balesenlugardeoperadoresmiembrosesqueenlaversiónglobal,laconversiónautomáticadetipospuedeaplicarseacualquieradelosoperandos,mientrasqueconobjetosmiembro,eloperandodelaparteizquierdadebeserdeltipoapropiado.Siquierequeambosoperandosseanconvertidos,laversiónglobalpuedeahorrarunmontóndecódigo.Heaquíunpequeñoejemplo:
//:C12:ReflexivityInOverloading.cpp
classNumber{
inti;
public:
368
i
i
“Volumen1”—2012/1/12—13:52—page371—#409i
i
i
i
i
i
12.6.Conversiónautomáticadetipos
Stringcs1("hello"),s2("there");
strcmp(s1,s2);//StandardCfunction
strspn(s1,s2);//Anystringfunction!
}///:~
Ahoracualquierfunciónqueacepteunargumentochar*puedeaceptartambiénunargumentoStringcporqueelcompiladorsabecómocrearunchar*apartirdeStringc.12.6.4.LastrampasdelaconversiónautomáticadetiposDadoqueelcompiladordebedecidircómorealizarunaconversióndetipos,pue-demeterseenproblemassielprogramadornodiseñalasconversionescorrectamen-te.UnasituaciónobviaysimplesucedecuandounaclaseXquepuedeconvertirseasímismaenunaclaseYconunoperatorY().SilaclaseYtieneunconstructorquetomaunargumentosimpledetipoX,estorepresentalaconversióndetiposidéntica.ElcompiladorahoratienedosformasdeirdeXaY,asíquesegeneraráunaerrordeambigüedad:
//:C12:TypeConversionAmbiguity.cpp
classOrange;//Classdeclaration
classApple{
public:
operatorOrange()const;//ConvertAppletoOrange
};
classOrange{
public:
Orange(Apple);//ConvertAppletoOrange
};
voidf(Orange){}
intmain(){
Applea;
//!f(a);//Error:ambiguousconversion
}///:~
Lasoluciónobviaaesteproblemaesnohacerla.Simplementeproporcioneunarutaúnicaparalaconversiónautomáticadeuntipoaotro.Unproblemamásdifícildeeliminarsucedecuandoproporcionaconversionesautomáticasamásdeuntipo.Estosellamaavecesacomodamiento(FIXME):
//:C12:TypeConversionFanout.cpp
classOrange{};
classPear{};
classApple{
public:
operatorOrange()const;
operatorPear()const;
371
i
i
“Volumen1”—2012/1/12—13:52—page372—#410i
i
i
i
i
i
Capítulo12.Sobrecargadeoperadores
};
//Overloadedeat():
voideat(Orange);
voideat(Pear);
intmain(){
Applec;
//!eat(c);
//Error:Apple�-OrangeorApple�-Pear???
}///:~
LaclaseAppletieneconversionesautomáticasaOrangeyaPear.Elelementocapciosoaquíesquenohayproblemahastaquealguieninocentementecreadosversionessobrecargadasdeeat().(Conunaúnicaversiónelcodigoenmain()funcionacorrectamente).Denuevolasolución--yellemageneraldelaconversiónautomáticadetipos--esproporcionarsolounaconversiónautomáticadeuntipoaotro.Puedetenercon-versionesaotrostipos,sóloquenodeberíanserautomáticas.PuedecrearllamadasafuncionesexplícitasconnombrescomomakeA()ymakeB().ActividadesocultasLaconversiónautomáticadetipospuedeproducirmasactividadsubyacentedelaquesepodríaesperar.MireestamodicacióndeCopyingVsInitialization.cppcomounpequeñorompecabezas:
//:C12:CopyingVsInitialization2.cpp
classFi{};
classFee{
public:
Fee(int){}
Fee(constFi&){}
};
classFo{
inti;
public:
Fo(intx=0):i(x){}
operatorFee()const{returnFee(i);}
};
intmain(){
Fofo;
Feefee=fo;
}///:~
NohayunconstructorparacrearFeefeedeunobjetoFo.Sinembargo,F-otieneunaconversiónautomáticadetiposaFee.NohayunconstructordecopiaparacrearunFeeapartirdeunFee,peroésaesunadelasfuncionesespecialesqueelcompiladorpuedecrear.(Elconstructorpordefecto,elconstructordecopiayo-perator=)yeldestructorpuedensintetizarseautomáticamenteporelcompilador.
372
i
i
“Volumen1”—2012/1/12—13:52—page374—#412i
i
i
i
i
i
Capítulo12.Sobrecargadeoperadores
9.Creeunaclasequecontengaunúnicoprivatechar.Sobrecarguelosoperadoresdeujosdeentrada/saliday&#x]TJ/;ཇ ; .96;& T; 13;&#x.848;&#x 0 T; [0;&#x]TJ/;ཇ ; .96;& T; 13;&#x.848;&#x 0 T; [0;(comoenIostreamOperatorOverloading.cpp)ypruébelos.Puedeprobarlosconfstreams,stringstreamsycinycout.10.Determineelvalorconstantecticioquesucompiladorpasaalosoperadorespostjos++y--.11.EscribaunaclaseNumberquecontengaundoubleyañadaoperadoressobre-cargadospara+,-,*,/ylaasignación.Elijalosvaloresderetornoparaestasfuncionesparaquelasexpresionessepuedanencadenaryqueseaeciente.Escribaunaconversiónautomáticadetiposoperatorint().12.Modiqueelejercicio11paraqueuselaoptimizacióndelvalorderetorno,sito-davíanolohahecho.13.Creeunaclasequecontengaunpuntero,ydemuestrequesipermitealcompi-ladorsintetizareloperador=elresultadodeusareseoperadorseránpunterosqueestaránsolapadosenlamismaubicacióndememoria.Ahoraarregleelproblemadeniendosupropiooperador=ydemuestrequecorrigeelsola-pamiento.Asegúresequecompruebalaauto-asignaciónyquemanejaelcasoapropiadamente.14.EscribaunaclasellamadaBirdquecontengaunmiembrostringyunstaticint.Enelconstructorpordefecto,useelintparagenerarautomáticamenteuniden-ticadorqueustedconstruyaenelstringjuntoconelnombredelaclase(Bird#1,Bird#2,etc).Añadaunoperadorparaujosdesalidaparaimpri-mirlosobjetosBird-Escribaunoperadordeasignación=yunconstructordecopia.Enmain()veriquequetodofuncionacorrectamente.15.EscribaunaclasellamadaBirdHousequecontengaunobjeto,unpunteroyunareferenciaparalaclaseBirddelejercicio14.Elconstructordeberíatomar3Birdscomoargumentos.AñadaunoperadordeujodesalidaparaB-irdHouse.Deshabiliteeloperadordeasignación=yelconstructordecopia.Enmain()veriquequetodofuncionacorrectamente.16.AñadaunmiembrodedatosintaBirdyaBirdHouseenelejercicio15.Añadaoperadoresmiembros+,-,*y/queusenelmiembrointpararealizarlasoperacionesenlosrespectivosmiembros.Veriquequefuncionan.17.Repitaelejercicio16usandooperadoresnomiembro.18.Añadaunoperador-aSmartPointer.cppyaNestedSmartPointer.cpp.19.ModiqueCopyingVsInitialization.cppparaquetodoslosconstruc-toresimprimanunmensajequeexpliquequéestápasando.Ahoraveriquequelasdosmanerasdellamaralconstructordecopia(ladeasignaciónyladeparéntesis)sonequivalentes.20.Intentecrearunoperadornomiembro=paraunaclaseyveaquéclasedemensajedelcompiladorrecibe.21.Creeunaclaseconunoperadordeasignaciónquetengaunsegundoargumen-to,unstringquetengaunvalorpordefectoquedigaop=call.Creeunafunciónqueasigneunobjetodesuclaseaotroymuestrequesuoperadordeasignaciónesllamadocorrectamente.
374
i
i
“Volumen1”—2012/1/12—13:52—page376—#414i
i
i
i
i
i
i
i
“Volumen1”—2012/1/12—13:52—page377—#415i
i
i
i
i
i
13:CreacióndinámicadeobjetosAvecesseconocelacantidadexactaexacta,eltipoyduracióndelavidadelosobjetosenunprograma,peronosiempreesasí.¿Cuántosavionestendráquesupervisarunsistemadecontroldetrácoaéreo?¿CuántasformasogurasseusaránenunsistemaCAD?¿Cuántosnodoshabráenunared?Pararesolverunproblemageneraldeprogramación,esesencialpodercrearydestruirobjetosentiempodeejecución.Porsupuesto,Cproporcionalasfuncionesdeasignacióndinámicadememoriamalloc()ysusvariantes,yfree(),queper-mitenobteneryliberarbloquesenelespaciodememoriadelmontículo(tambiénllamadoespaciolibre1mientrasseejecutaelprograma.Estemétodosinembargo,nofuncionaráenC++.Elconstructornolepermitemanipularladireccióndememoriaainicializar,yconmotivo.Depermitirse,seríaposible:1.Olvidarlallamadaalconstructor.ConlocualnoseríaposiblegarantizarlainicializacióndelosobjetosenC++.2.Usaraccidentalmenteunobjetoqueaúnnohasidoinicializado,esperandoquetodovayabien.3.Manipularunobjetodetamañoincorrecto.Yporsupuesto,inclusosisehizotodocorrectamente,cualquieraquemodiqueelprogramaestaríaexpuestoacometeresosmismoserrores.Unagranpartedelosproblemasdeprogramacióntienensuorigenenlainicializaciónincorrectadeobje-tos,loquehaceespecialmenteimportantegarantizarlallamadaalosconstructoresparalosobjetosquehandesercreadosenelmontículo.¿CómosegarantizaenC++lacorrectainicializaciónylimpieza,permitiendolacreacióndinámicadeobjetos?Larespuestaestáenintegrarenellenguajemismolacreacióndinámicadeobje-tos.malloc()yfree()sonfuncionesdebibliotecayportanto,estánfueradelcontroldelcompilador.Sisedisponedeunoperadorquelleveacaboelactocombina-dodelaasignacióndinámicadememoriaylainicialización,ydeotrooperadorquerealiceelactocombinadodelalimpiezaydeliberacióndememoria,elcompiladorpodrágarantizarlallamadaalosconstructoresydestructoresdelosobjetos.EnestecapítuloverácómoseresuelvedemodoeleganteesteproblemaconlosoperadoresnewydeletedeC++.
1N.T.espaciodealmacenamientolibre(freestore)
377
i
i
“Volumen1”—2012/1/12—13:52—page378—#416i
i
i
i
i
i
Capítulo13.Creacióndinámicadeobjetos
13.1.CreacióndeobjetosLacreacióndeunobjetoenC++tienelugarendospasos:1.Asignacióndememoriaparaelobjeto.2.Llamadaalconstructor.Aceptemosporahoraqueestesegundopasoocurresiempre.C++lofuerza,de-bidoaqueelusodeobjetosnoinicializadosesunadelascausasmásfrecuentesdeerroresdeprogramación.Siempreseinvocaalconstructor,sinimportarcómonidóndesecreaelobjeto.Elprimerodeestospasospuedeocurrirdevariosmodosyendiferentemomento:1.Asignacióndememoriaenlazonadealmacenamientoestático,quetienelugardurantelacargadelprograma.Elespaciodememoriaasignadoalobjetoexistehastaqueelprogramatermina.2.Asignacióndememoriaenlapila,cuandosealcanzaalgúnpuntodetermina-dodurantelaejecucióndelprograma(lallavedeaperturadeunbloque).Lamemoriaasignadasevuelvealiberardeformaautomáticaencuantosealcan-zaelpuntodeejecucióncomplementario(lallavedecierredeunbloque).Lasoperacionesdemanipulacióndelapilaformanpartedelconjuntodeinstruc-cionesdelprocesadorysonmuyecientes.Porotraparte,esnecesariosabercuantasvariablessenecesitanmientrasseescribeelprogramademodoqueelcopiladorpuedagenerarelcódigocorrespondiente.3.Asignacióndinámica,enunazonadememorialibrellamadamontículo(heapofreestore).Sereservaespacioparaunobjetoenestazonamediantelallamadaaunafuncióndurantelaejecucióndelprograma;estosignicaquesepuededecidirencualquiermomentoquesenecesitaciertacantidaddememoria.Estoconllevalaresponsabilidaddedeterminarelmomentoenquehadeliberarselamemoria,loqueimplicadeterminareltiempodevidadelamismaque,portanto,yanoestábajocontroldelasreglasdeámbito.Amenudo,lastresregionesdememoriareferidassedisponenenunazonacon-tiguadelamemoriafísica:áreaestática,lapila,yelmontículo,enunordendetermi-nadoporelescritordelcompilador.Nohayreglasjas.Lapilapuedeestarenunazonaespecial,ypuedequelasasignacionesenelmontículoseobtenganmedian-tepeticióndebloquesdelamemoriadelsistemaoperativo.Estosdetallesquedannormalmenteocultosalprogramadorpuestoquetodoloquesenecesitaconoceralrespectoesqueesamemoriaestarádisponiblecuandosenecesite.13.1.1.AsignacióndinámicaenCCproporcionalasfuncionesdesubibliotecaestándarmalloc()ysusvariantescalloc()yrealloc()paraasignar,yfree()paraliberarbloquesdememoriadinámicamenteentiempodeejecución.Estasfuncionessonpragmáticasperorudi-mentariasporloquerequierencomprensiónyuncuidadosomanejoporpartedelprogramador.EllistadoquesigueesunejemploqueilustraelmododecrearunainstanciadeunaclaseconestasfuncionesdeC:
//:C13:MallocClass.cpp
378
i
i
“Volumen1”—2012/1/12—13:52—page379—#417i
i
i
i
i
i
13.1.Creacióndeobjetos
//Mallocwithclassobjects
//Whatyou'dhavetodoifnotfor"new"
#include"../require.h"
#include std;&#xlib0;//malloc()&free()
#include str;&#xing0;//memset()
#include&#xiost;&#xream;
usingnamespacestd;
classObj{
inti,j,k;
enum{sz=100};
charbuf[sz];
public:
voidinitialize(){//Can'tuseconstructor
cout"initializingObj"endl;
i=j=k=0;
memset(buf,0,sz);
}
voiddestroy()const{//Can'tusedestructor
cout"destroyingObj"endl;
}
};
intmain(){
Obj*obj=(Obj*)malloc(sizeof(Obj));
require(obj!=0);
obj�-initialize();
//...sometimelater:
obj�-destroy();
free(obj);
}///:~
Observeelusodemalloc()paralaobtencióndeespacioparaelobjeto:
Obj*obj=(Obj*)malloc(sizeof(Obj));
Sedebepasarcomoparámetroamalloc()eltamañodelobjeto.Eltipodere-tornodemalloc()esvoid*,puesessólounpunteroaunbloquedememoria,nounobjeto.EnC++nosepermitelaasignacióndirectadeunvoid*aningúnotrotipodepuntero,deahílanecesidaddelaconversiónexplícitadetipo(molde).Puedeocurrirquemalloc()noencuentreunbloqueadecuado,encuyocasodevolveráunpunteronulo,deahílanecesidaddecomprobarlavalidezdelpunterodevuelto.Elprincipalescolloestáenlalínea:
obj�-initialize();
Elusuariodeberáasegurarsedeinicializarelobjetoantesdesuuso.Obsérvesequenosehausadoelconstructordebidoaqueéstenopuedeserllamadodemodoexplícito2;esllamadoporelcompiladorcuandosecreaunobjeto.Elproblemaes
2Existeunasintaxisespecialllamadaplacement-newquepermitellamaralconstructorparaunbloquedememoriapreasignando.Severámásadelante,enestemismocapítulo.
379
i
i
“Volumen1”—2012/1/12—13:52—page380—#418i
i
i
i
i
i
Capítulo13.Creacióndinámicadeobjetos
queelusuariopuedeolvidarinicializarelobjetoantesdeusarlo,introduciendoasíunaimportantefuentedeproblemas.Comoconsecuencia,muchosprogramadoresencuentranmuyconfusasycom-plicadaslasfuncionesdeasignacióndinámicadelamemoriaenC.Noesmuydifícilencontrarprogramadoresque,usandomáquinasconmemoriavirtual,usanvecto-resenormeseneláreadealmacenamientoestáticoparaevitartenerquetratarconlaasignacióndinámica.DadoqueC++intentafacilitarelusodelabibliotecaalospro-gramadoresocasionales,noesaceptablelaformadeabordarlaasignacióndinámicaenC.13.1.2.EloperadornewLasoluciónqueofreceC++consisteencombinarlaseriedeaccionesnecesariasparalacreacióndeunobjetoenunúnicooperadorllamado�new.Cuandosecreaunobjetomedianteeloperador�new,ésteseencargadeobtenerelespacionecesarioparaelobjetoydellamarasuconstructor.Cuandoseejecutaelcódigo:
MyType*fp=newMyType(1,2);
seasignaespaciomediantealgunallamadaequivalentea�malloc(sizeof(M-yType))--confrecuenciaesasí,literalmente--,yusandoladirecciónobtenidacomopuntero�this,y(1,2)comoargumentos,sellamaalconstructordelaclaseM-yType.Paracuandoestádisponible,elvalorderetornodenewesyaunpunteroválidoaunobjetoinicializado.Ademásesdeltipocorrecto,loquehaceinnecesarialaconversión.Eloperadornewpordefecto,compruebaeléxitoofracasodelaasignacióndememoriacomopasoprevioalallamadaalconstructor,haciendoinnecesariayre-dundantelaposteriorcomprobación.Másadelanteenestecapítuloseveráquésu-cedesiseproduceestefallo.Enlasexpresionesconnewsepuedeusarcualquieradelosconstructoresdispo-niblesparaunaclase.Siéstenotieneargumentos,seescribelaexpresiónsinlistadeargumentos
MyType*fp=newMyType;
Esnotablelasimplezaalcanzadaenlacreacióndinámicadeobjetos:unaúnicaexpresiónrealizatodoeltrabajodecálculodetamaño,asignación,comprobacionesdeseguridadyconversióndetipo.Estohacequelacreacióndinámicadeobjetosseatansencillacomolacreaciónenlapila.13.1.3.EloperadordeleteElcomplementoalaexpresiónneweslaexpresióndelete,queprimerollamaaldestructorydespuésliberalamemoria(amenudomedianteunallamadaafree(-)).Elargumentoparaunaexpresióncondeletedebeserunadirección:unpunteroaobjetocreadomediantenew.
deletefp;
380
i
i
“Volumen1”—2012/1/12—13:52—page381—#419i
i
i
i
i
i
13.1.Creacióndeobjetos
Estaexpresióndestruyeelobjetoydespuésliberaelespaciodinámicamenteasig-nadoalobjetoMyTypeElusodeloperadordeletedebelimitarsealosobjetosquehayansidocrea-dosmediantenew.Lasconsecuenciasdeaplicareloperadordeletealosobjetoscreadosconmalloc(),calloc()orealloc()noestándenidas.Dadoquelamayoríadelasimplementacionespordefectodenewydeleteusanmalloc()yfree(),elresultadoseráprobablementelaliberacióndelamemoriasinlallamadaaldestructor.Noocurrenadasielpunteroqueselepasaadeleteesnulo.Poresarazón,amenudoserecomiendaasignarceroalpunteroinmediatamentedespuésdeusardelete;seevitaasíquepuedaserusadodenuevocomoargumentoparadelete.Tratardedestruirunobjetomásdeunavezesunerrordeconsecuenciasimprevisi-bles.13.1.4.UnejemplosencilloElsiguienteejemplodemuestraquelainicializacióntienelugar:
//:C13:Tree.h
#ifndefTREE_H
#defineTREE_H
#include&#xiost;&#xream;
classTree{
intheight;
public:
Tree(inttreeHeight):height(treeHeight){}
~Tree(){std::cout"*";}
friendstd::ostream&
operator(std::ostream&os,constTree*t){
returnos"Treeheightis:"
t&#x-600;-heightstd::endl;
}
};
#endif//TREE_H///:~
SepuedeprobarqueelconstructoresinvocadoimprimiendoelvalordeTre-e.AquísehacesobrecargandoeloperatorparausarloconunostreamyunTree*.Note,sinembargo,queaunquelafunciónestádeclaradacomofriend,estádenidacomounainline!.Estoesasíporconveniencia--denirunafunciónamigacomoinlineaunaclasenocambiasucondicióndeamigaoelhechodequeesunafunciónglobalynounmétodo.Tambiénresaltarqueelvalorderetornoeselresultadodeunaexpresióncompleta(elostream&),yasídebeser,parasatisfacereltipodelvalorderetornodelafunción.13.1.5.TrabajoextraparaelgestordememoriaCuandosecreanobjetosautomáticosenlapila,eltamañodelosobjetosysutiempodevidaquedajadoenelcódigogenerado,porqueelcompiladorconocesutipo,cantidadyalcance.Crearobjetosenelmontículoimplicaunasobrecargaadicio-nal,tantoentiempocomoenespacio.Veamoselescenariotípico(Puedereemplazar
381
i
i
“Volumen1”—2012/1/12—13:52—page384—#422i
i
i
i
i
i
Capítulo13.Creacióndinámicadeobjetos
teroaobjetoalmacenadoenelcontenedor.Elcontenedornopuederealizarlalim-piezaparalospunterosquealmacenapuestoquesonpunterosvoid*.Estopuedederivarenunserioproblemasiauncontenedorselepasanpunterosaobjetosauto-máticosjuntoconpunterosaobjetosdinámicos;elresultadodeusardeletesobreunpunteroquenohayasidoobtenidodelmontículoesimprevisible.Másaún,alobtenerdelcontenedorunpunterocualquiera,existirándudassobreelorigen,au-tomático,dinámicooestático,delobjetoalqueapunta.EstoimplicaquehayqueasegurarsedelorigendinámicodelospunterosquesealmacenenenlasiguienteversióndeStashyStack,bienseamedianteunaprogramacióncuidadosa,obienporlacreacióndeclasesquesólopuedanserconstruidasenelmontículo.Esmuyimportanteasegurarsetambiéndequeelprogramadorclienteseres-ponsabilicedelalimpiezadelospunterosdelcontenedor.SehavistoenejemplosanterioresquelaclaseStackcomprobabaensudestructorquetodoslosobjetosLinkhabíansidodesapilados.UnobjetoStashparapunterosrequiereunmododiferentedeabordarelproblema.13.2.3.StashparapunterosEstanuevaversióndelaclaseStash,quellamamosPStash,almacenapunterosaobjetosexistentesenelmontículo,adiferenciadelaviejaversión,queguardabaunacopiaporvalordelosobjetos.Usandonewydelete,esfácilyseguroalmace-narpunterosaobjetoscreadosenelmontículo.Heaquíelarchivodecabecerapara«Stashparapunteros»:
//:C13:PStash.h
//Holdspointersinsteadofobjects
#ifndefPSTASH_H
#definePSTASH_H
classPStash{
intquantity;//Numberofstoragespaces
intnext;//Nextemptyspace
//Pointerstorage:
void**storage;
voidinflate(intincrease);
public:
PStash():quantity(0),storage(0),next(0){}
~PStash();
intadd(void*element);
void*operator[](intindex)const;//Fetch
//RemovethereferencefromthisPStash:
void*remove(intindex);
//NumberofelementsinStash:
intcount()const{returnnext;}
};
#endif//PSTASH_H///:~
Loselementosdedatossubyacentesnohancambiadomucho,peroahoraelal-macenamientosehacesobreunvectordepunterosvoid,queseobtienemediantenewenlugardemalloc().Enlaexpresión
void**st=newvoid*[quantity+increase];
384
i
i
“Volumen1”—2012/1/12—13:52—page386—#424i
i
i
i
i
i
Capítulo13.Creacióndinámicadeobjetos
memset(st,0,(quantity+increase)*psz);
memcpy(st,storage,quantity*psz);
quantity+=increase;
delete[]storage;//Oldstorage
storage=st;//Pointtonewmemory
}///:~
Lafunciónadd()es,enefecto,lamismaqueantessiexceptuamoselhechodequeloquesealmacenaahoraesunpunteroaunobjetoenlugardeunacopiadelobjeto.Elcódigodeinflate()hasidomodicadoparagestionarlaasignacióndeme-moriaparaunvectordevoid*,adiferenciadeldiseñoprevio,quesólotratabaconbytes.Aquí,enlugardeusarelmétododecopiaporelíndicedelvector,seponeprimeroaceroelvectorusandolafunciónmemset()delabibliotecaestándardeC,aunqueestonoseaestrictamentenecesarioyaque,presumiblemente,PStashmanipularálamemoriadeformaadecuada,peroavecesnoesmuycostosoañadirunpocomásdeseguridad.Acontinuación,secopianalnuevovectorusandom-emcpy()losdatosexistentesenelantiguo.Confrecuenciaveráquelasfuncionesmemcpy()ymemset()hansidooptimizadasencuantoaltiempodeproceso,demodoquepuedensermásrápidasquelosbuclesanteriormentevistos.Noobstan-te,unafuncióncomoinflate()noesprobablequeseallamadaconlafrecuencianecesariaparaqueladiferenciaseapalpable.Encualquiercaso,elhechodequelasllamadasafunciónseanmásconcisasquelosbucles,puedeayudaraprevenirerroresdeprogramación.Paradejardenitivamentelaresponsabilidaddelalimpiezadelosobjetosso-breloshombrosdelprogramadorcliente,seproporcionandosformasdeaccederalospunterosenPStash:eloperador[],quedevuelveelpunterosineliminarlodelcontenedor,yunsegundométodoremove()queademásdedevolverelpunteroloeliminadelcontenedor,poniendoacerolaposiciónqueocupaba.Cuandoseprodu-celallamadaaldestructordePStash,sepruebasihansidopreviamenteretiradostodoslospunteros,sinoesasí,senotica,demodoqueesposibleprevenirlafugadememoria.Severánotrassolucionesmaselegantesencapítulosposteriores.UnapruebaAquíapareceelprogramadepruebadeStash,reescritoparaPStash:
//:C13:PStashTest.cpp
//{L}PStash
//TestofpointerStash
#include"PStash.h"
#include"../require.h"
#include&#xiost;&#xream;
#includestr;êm0;
#include&#xstri;&#xng00;
usingnamespacestd;
intmain(){
PStashintStash;
//'new'workswithbuilt-intypes,too.Note
//the"pseudo-constructor"syntax:
for(inti=0;i25;i++)
intStash.add(newint(i));
386
i
i
“Volumen1”—2012/1/12—13:52—page389—#427i
i
i
i
i
i
13.3.newydeleteparavectores
creó,yllamaraldestructorparacadaunodedichoselementos.Estaesunamejorasobrelasintaxisprimitiva,quepuedeverseocasionalmenteenelcódigodeviejosprogramas:
delete[100]fp;
queforzabaalprogramadoraincluirelnúmerodeobjetoscontenidosenelvec-tor,introduciendoconellounaposiblefuentedeerrores.Elesfuerzoadicionalquesuponeparaelcompiladortenerenestoencuentaespequeño,yporesoseconsiderópreferibleespecicarelnúmerodeobjetosenunlugarynoendos.13.3.1.CómohacerqueunpunteroseamásparecidoaunvectorComodefectocolateral,existelaposibilidaddemodicarelpunterofpanterior-mentedenido,paraqueapunteacualquierotracosa,loquenoesconsistenteconelhechodeserladireccióndeiniciodeunvector.Tienemássentidodenirlocomounaconstante,demodoquecualquierintentodemodicaciónseaseñaladocomounerror.Paraconseguiresteefectosepodríaprobarcon:
intconst*q=newint[10];
obien:
constint*q=newint[10];
peroenamboscasoselespecicadorconstquedaríaasociadoalint,esdecir,alvaloralqueapunta,enlugardealpunteroensí.Sisequiereconseguirelefectodeseado,enlugardelasanteriores,sedebeponer:
int*constq=newint[10];
Ahoraesposiblemodicarelvalordeloselementosdelvector,siendoilegalcual-quierintentoposteriordemodicarq,comoq++porejemplo,aligualqueocurreconelidenticadordeunvectorordinario.13.3.2.Cuandosesuperaelespaciodealmacenamiento¿Quéocurrecuandonew()nopuedeencontrarunbloquecontiguosuciente-mentegrandeparaalojarelobjeto?Enestecasoseproducelallamadaaunafunciónespecial:elmanejadordeerroresdenewonew-handler.Paraellocompruebasiundeterminadopunteroafunciónesnulo,sinoloes,seefectúalallamadaalafunciónalaqueapunta.Elcomportamientopordefectodelmanejadordeerroresdenewesdispararunaexcepción,asuntodelquesetrataráenelVolumen2.Sisepiensausarlaasignacióndinámica,convienealmenosreemplazarelmanejadordeerroresdenewporunafunciónqueadviertadelafaltadememoriayfuercelaterminacióndelprograma.Deestemodo,duranteladepuracióndelprograma,sepodráseguirlapistadelosu-cedido.Paralaversiónnaldelprograma,serámejorimplementarunarecuperacióndeerroresmáselaborada.
389
i
i
“Volumen1”—2012/1/12—13:52—page390—#428i
i
i
i
i
i
Capítulo13.Creacióndinámicadeobjetos
Laformadereemplazarelmanejadordenew-handlerpordefectoconsisteenin-cluirelarchivonew.hyhacerunallamadaalafunciónset_new_handler()conladireccióndelafunciónquesedeseainstalar:
//:C13:NewHandler.cpp
//Changingthenew-handler
#include&#xiost;&#xream;
#include std;&#xlib0;
#includenew&#x]TJ/;ྖ ;.96;d T; 5.;͹ ;� Td;&#x [00;
usingnamespacestd;
intcount=0;
voidout_of_memory(){
cerr"memoryexhaustedafter"count
"allocations!"endl;
exit(1);
}
intmain(){
set_new_handler(out_of_memory);
while(1){
count++;
newint[1000];//Exhaustsmemory
}
}///:~
Lafunciónainstalardeberetornarvoidynotomarargumentos.Elbuclewh-ileseguirápidiendobloquesdeinthastaconsumirlamemorialibredisponible,sinhacernadaconellos.Justoalasiguientellamadaanew,nohabráespacioparaasignaryseproducirálallamadaalmanejadordenew.Estecomportamientodelnew-handlerestáasociadoaloperatornew(),demo-doquesisesobrecargaoperatornew()(asuntoquesetrataenlasiguientesec-ción),noseproducirálallamadaalmanejadordenew.Sisedeseaqueseproduzcadichallamadaseránecesarioquelohagaeneloperatornew()quesubstituyaaloriginal.Porsupuesto,esposibleescribirmanejadoresnewmássosticados,inclusoal-gunoqueintentereclamarlosbloquesasignadosquenoseusan(conocidoshabi-tualmentecomorecolectoresdebasura).Peroestenoesuntrabajoadecuadoparapro-gramadoresnoveles.13.3.3.SobrecargadelosoperadoresnewydeleteCuandoseejecutaunaexpresiónconnew,ocurrendoscosas.Primeroseasignalamemoriaalejecutarelcódigodeloperatornew()ydespuésserealizalalla-madaalconstructor.Enelcasodeunaexpresióncondelete,sellamaprimeroaldestructorydespuésseliberalamemoriaconeloperadoroperatordelete().Lasllamadasalconstructorydestructornoestánbajoelcontroldelprogramador,perosepuedencambiarlasfuncionesopertatornew()yoperatatordelet-e().Elsistemadeasignacióndememoriausadopornewydeleteesunsistemadepropósitogeneral.Ensituacionesespeciales,puedequenofuncionecomoserequie-
390
i
i
“Volumen1”—2012/1/12—13:52—page392—#430i
i
i
i
i
i
Capítulo13.Creacióndinámicadeobjetos
quequeseallamadoelconstructor,unactoqueelcompiladorgarantizayqueestáfueradelcontroldeesteoperador.Eloperadoroperatordelete()tomacomoargumentounpunterovoid*aunbloqueobtenidoconeloperatornew().Esunvoid*yaqueeldeleteobtieneelpunterosólodespuésdequehayasidollamadoeldestructor,loqueefectivamenteeliminasucaracterdeobjetoconvirtiéndoloenunsimplebloquedememoria.Eltipoderetornoparadeleteesvoid.Acontinuaciónseexponeunejemplodelmododesobrecargarglobalmentenewydelete:
//:C13:GlobalOperatorNew.cpp
//Overloadglobalnew/delete
#include std;&#xio00;
#include std;&#xlib0;
usingnamespacestd;
void*operatornew(size_tsz){
printf("operatornew:%dBytes\n",sz);
void*m=malloc(sz);
if(!m)puts("outofmemory");
returnm;
}
voidoperatordelete(void*m){
puts("operatordelete");
free(m);
}
classS{
inti[100];
public:
S(){puts("S::S()");}
~S(){puts("S::~S()");}
};
intmain(){
puts("creating&destroyinganint");
int*p=newint(47);
deletep;
puts("creating&destroyingans");
S*s=newS;
deletes;
puts("creating&destroyingS[3]");
S*sa=newS[3];
delete[]sa;
}///:~
Aquípuedeverselaformageneraldesobrecargadeoperadoresnewydelete.Estosoperadoressustitutivosusanlasfuncionesmalloc()yfree()delabiblio-tecaestándardeC,queesprobablementeloqueocurreenlosoperadoresoriginales.Imprimentambiénmensajessobreloqueestánhaciendo.Nótesequenosehanusa-doiostreamssinoprintf()yputs().Estosehacedebidoaquelosobjetosiostreamcomolosglobalescin,coutycerrllamananewparaobtenermemoria
392
i
i
“Volumen1”—2012/1/12—13:52—page393—#431i
i
i
i
i
i
13.3.newydeleteparavectores
3.Usarprintf()evitaelfatalbloqueo,yaquenohacellamadasanew.Enmain(),secreanalgunosobjetosdetiposbásicosparademostrarquetam-biénenestoscasossellamaalosoperadoresnewydeletesobrecargados.Poste-riormente,secreanunobjetosimpleyunvector,ambosdetipoS.Enelcasodelvectorsepuedever,porelnúmerodebytespedidos,quesesolicitaalgodememoriaextraparaincluirinformaciónsobreelnúmerodeobjetosquetendrá.Entodosloscasosseefectúalallamadaalasversionesglobalessobrecargadasdenewydelete.SobrecargadenewydeleteespecícaparaunaclaseAunquenoesnecesarioponerelmodicadorstatic,cuandosesobrecargan-ewydeleteparaunaclaseseestáncreandométodosestáticos(métodosdeclase).Lasintaxiseslamismaqueparacualquierotrooperador.Cuandoelcompiladorencuentraunaexpresiónnewparacrearunobjetodeunaclase,elige,siexiste,unmétododelaclasellamadooperatornew()enlugardelnewglobal.Paraelrestodetiposoclasesseusanlosoperadoresglobales(amenosquetengandenidoslossuyospropios).Enelsiguienteejemploseusaunprimitivosistemadeasignacióndealmacena-mientoparalaclaseFramis.SereservaunbloquedememoriaeneláreadedatosestáticaFIXME,yseusaesamemoriaparaasignaralojamientoparalosobjetosdetipoFramis.Paradeterminarquébloquessehanasignado,seusaunsencillovectordebytes,unbyteporbloque.
//:C13:Framis.cpp
//Localoverloadednew&delete
#include std;�//Size_t
#includestr;êm0;
#include&#xiost;&#xream;
#includenew&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;
usingnamespacestd;
ofstreamout("Framis.out");
classFramis{
enum{sz=10};
charc[sz];//Totakeupspace,notused
staticunsignedcharpool[];
staticboolalloc_map[];
public:
enum{psize=100};//framiallowed
Framis(){out"Framis()\n";}
~Framis(){out"~Framis()...";}
void*operatornew(size_t)throw(bad_alloc);
voidoperatordelete(void*);
};
unsignedcharFramis::pool[psize*sizeof(Framis)];
boolFramis::alloc_map[psize]={false};
//Sizeisignored--assumeaFramisobject
void*
Framis::operatornew(size_t)throw(bad_alloc){
for(inti=0;ipsize;i++)
if(!alloc_map[i]){
out"usingblock"i"...";
alloc_map[i]=true;//Markitused
3Provocaríaunaseriecontinuadellamadasanewhastaagotarlapilayabortaríaelprograma.
393
i
i
“Volumen1”—2012/1/12—13:52—page396—#434i
i
i
i
i
i
Capítulo13.Creacióndinámicadeobjetos
#includestr;êm0;
usingnamespacestd;
ofstreamtrace("ArrayOperatorNew.out");
classWidget{
enum{sz=10};
inti[sz];
public:
Widget(){trace"*";}
~Widget(){trace"~";}
void*operatornew(size_tsz){
trace"Widget::new:"
sz"bytes"endl;
return::newchar[sz];
}
voidoperatordelete(void*p){
trace"Widget::delete"endl;
::delete[]p;
}
void*operatornew[](size_tsz){
trace"Widget::new[]:"
sz"bytes"endl;
return::newchar[sz];
}
voidoperatordelete[](void*p){
trace"Widget::delete[]"endl;
::delete[]p;
}
};
intmain(){
trace"newWidget"endl;
Widget*w=newWidget;
trace"\ndeleteWidget"endl;
deletew;
trace"\nnewWidget[25]"endl;
Widget*wa=newWidget[25];
trace"\ndelete[]Widget"endl;
delete[]wa;
}///:~
Siexceptuamoslainformaciónderastreoqueseañadeaquí,lasllamadasalasversionesglobalesdenewydeletecausanelmismoefectoquesiestosoperado-resnosehubieransobrecargado.Comosehavistoanteriormente,esposibleusarcualquieresquemaconvenientedeasignacióndememoriaenestosoperadoresmo-dicados.Sepuedeobservarquelasintaxisdenewydeleteparavectoreseslamismaquelausadaparaobjetossimplesañadiéndoleseloperadorsubíndice[].Enamboscasosselepasaanewcomoargumentoeltamañodelbloquedememoriasolicitado.Alaversiónparavectoresselepasaeltamañonecesarioparaalbergartodossuscomponentes.Convienetenerencuentaqueloúnicoqueserequieredeloperatornew()esquedevuelvaunpunteroaunbloquedememoriasucientementegrande.Aunqueesposibleinicializarelbloquereferido,esoestrabajodelconstructor,quesellamaráautomáticamenteporelcompilador.
396
i
i
“Volumen1”—2012/1/12—13:52—page398—#436i
i
i
i
i
i
Capítulo13.Creacióndinámicadeobjetos
cout"NoMemory::operatornew"endl;
throwbad_alloc();//"Outofmemory"
}
};
intmain(){
NoMemory*nm=0;
try{
nm=newNoMemory;
}catch(bad_alloc){
cerr"Outofmemoryexception"endl;
}
cout"nm="nmendl;
}///:~
Cuandoseejecuta,elprogramaimprimelosmensajesdeloperatornew()ydelmanejadordeexcepción,peronoeldelconstructor.Comonewnuncaretorna,nosellamaalconstructoryportantonoseimprimesumensaje.Paraasegurarquenoseusaindebidamente,Esimportanteinicializarnmacero,debidoaquenewnosecompleta.Elcódigodemanejodeexcepcionesdebehaceralgomásqueimprimirunmensajeycontinuarcomosielobjetohubierasidocreadoconéxito.Idealmente,deberíahaceralgoquepermitieraalprogramarecuperarsedelfallo,oalmenos,provocarlasalidadespuésderegistrarunerror.EnlasprimerasversionesdeC++,elcomportamientoestándarconsistíaenhacerquenewretornaraunpunteronulosilaasignacióndememoriafallaba.Estopodíaimpedirquesellamaraalconstructor.Siseintentahacerestoconuncompiladorqueseaconformealestándaractual,leinformarádequeenlugardedevolverunvalornulo,debedispararunaexcepcióndetipobad_alloc.Operadoresnewydeletede[FIXMEemplazamiento(situación)]Heaquíotrosdosusos,menoscomunes,paralasobrecargadeoperadornew-():1.Puedeocurrirquenecesiteemplazarunobjetoenunlugarespecícodelamemoria.Estopuedeserimportanteenprogramasenlosquealgunosdelosobjetossereerenosonsinónimosdecomponenteshardwaremapeadossobreunazonadelamemoria.2.Sisequierepermitirlaelecciónentrevariosasignadoresdememoria(alloca-tors)enlallamadaanew.Ambassituacionesseresuelvenmedianteelmismomecanismo:lafunciónop-eratornew()puedetomarmásdeunargumento.Comosehavisto,elprimerargumentodenewessiempreeltamañodelobjeto,calculadoensecretoypasadoporelcompilador.Elrestodeargumentospuedeserdecualquierotrotipoquesenecesite:ladirecciónenlaquequeremosemplazarelobjeto,unareferenciaaunafuncióndeasignacióndememoria,ocualquieraotracosaqueseconsidereconve-niente.Alprincipiopuedeparecercuriosoelmodoenquesepasanlosargumentosextraaloperatornew().Despuésdelapalabraclavenewyantesdelnombredeclasedelobjetoquesepretendecrear,seponelalistadeargumentos,sincontarconel
398
i
i
“Volumen1”—2012/1/12—13:52—page399—#437i
i
i
i
i
i
13.3.newydeleteparavectores
correspondientealsize_tdelobjeto,quelepasaelcompilador.Porejemplo,laexpresión:
X*xp=new(a)X;
pasaráacomosegundoargumentoaloperadoroperatornew().Porsupues-to,sólofuncionarásihasidodeclaradoeloperatornew()adecuado.Heaquíunejemplodemostrativodecómoseusaestoparacolocarunobjetoenunaposiciónparticular:
//:C13:PlacementOperatorNew.cpp
//Placementwithoperatornew()
#include std;�//Size_t
#include&#xiost;&#xream;
usingnamespacestd;
classX{
inti;
public:
X(intii=0):i(ii){
cout"this="thisendl;
}
~X(){
cout"X::~X():"thisendl;
}
void*operatornew(size_t,void*loc){
returnloc;
}
};
intmain(){
intl[10];
cout"l="lendl;
X*xp=new(l)X(47);//Xatlocationl
xp�-X::~X();//Explicitdestructorcall
//ONLYusewithplacement!
}///:~
Observequeloúnicoquehaceeloperadornewesretornarelpunteroquesepasa.Portanto,esposibleespecicarladirecciónenlaquesequiereconstruirelobjeto.Aunqueesteejemplomuestrasólounargumentoadicional,nadaimpideañadirotros,siseconsideraconvenienteparasuspropósitos.Altratardedestruirestosobjetossurgeunproblema.Sólohayunaversióndeloperadordelete,demodoquenohayformadedecir:"Usamifuncióndeliberacióndememoriaparaesteobjeto".Serequierellamaraldestructor,perosinutilizarelmecanismodememoriadinámica,yaqueelobjetonoestáalojadoenelmontículo.Lasolucióntieneunasintaxismuyespecial.Sedebellamarexplícitamentealdestructor,talcomosemuestra:
xp�-X::~X();//Llamadaíexplcitaaldestructor
399
i
i
“Volumen1”—2012/1/12—13:52—page405—#443i
i
i
i
i
i
14.2.Sintaxisdelaherencia
y.permute();
}///:~
Aquí,lafunciónpermute()sehaañadidoalainterfazdelaclase,peroelrestofuncionesdeXsonutilizadasdentrodelosmiembrosdeY.14.2.SintaxisdelaherenciaLasintaxisenlacomposiciónesbastanteobvia,encambioenlaherencia,lasin-taxisesnuevaydiferente.Cuandohereda,realmenteseexpresa"Estanuevaclaseescomoestaotraviejaclase".Secomienzaelcódigoproporcionandoelnombredelaclase,comosereali-zanormalmente,peroantesdeabrirlallavedelcuerpodelaclase,secolocandospuntosyelnombredelaclasebase(odelasclasesbases,separadasporcomas,pa-raherenciamúltiple).Unavezrealizado,automáticamenteseconsiguentodoslosmiembrosylasfuncionesdelaclasebase.Ejemplo:
//:C14:Inheritance.cpp
//Simpleinheritance
#include"Useful.h"
#include&#xiost;&#xream;
usingnamespacestd;
classY:publicX{
inti;//DifferentfromX'si
public:
Y(){i=0;}
intchange(){
i=permute();//Differentnamecall
returni;
}
voidset(intii){
i=ii;
X::set(ii);//Same-namefunctioncall
}
};
intmain(){
cout"sizeof(X)="sizeof(X)endl;
cout"sizeof(Y)="
sizeof(Y)endl;
YD;
D.change();
//Xfunctioninterfacecomesthrough:
D.read();
D.permute();
//Redefinedfunctionshidebaseversions:
D.set(12);
}///:~
Comosepuedeobservar,YheredadeX,quesignicaqueYcontendrátodoslosmiembrosdeXytodaslasfuncionesdeX.Dehecho,YcontieneunsubobjetoXcomo
405
i
i
“Volumen1”—2012/1/12—13:52—page406—#444i
i
i
i
i
i
Capítulo14.HerenciayComposición
sisehubiesecreadounobjetoXdentrodelaclaseYenvezdeheredardeX.Tantolosmiembrosobjetosylaclasebasesonconocidoscomosubobjetos.TodosloselementosprivadosdeXcontinúansiendoprivadosenY;estoes,aun-queYheredadeXnosignicaqueYpuedaromperelmecanismodeprotección.LoselementosprivadosdeXcontinúanexistiendo,ocupandosuespacio-sóloquenosepuedeaccederaellosdirectamente.Enmain()observamosquelosdatosdeYestáncombinadosconlosdatosdeXporquesizeof(Y)eseldobledegrandequeelsizeof(X).Observaráquelaclasebaseesprecedidaporpublic.Durantelaherencia,porde-fecto,todoesprivado.Silaclasebasenoestuvieseprecedidaporpublic,signicaríaquetodoslosmiembrospúblicosdelaclasebaseseríanprivadosenlaclasederiva-da.Esto,enlamayoríadeocasionesnoeslodeseado[51];elresultadoquesedeseaesmantenertodoslosmiembrospúblicosdelaclasebaseenlaclasederivada.Parahaceresto,seusalapalabraclavepublicdurantelaherencia.Enchange(),seutilizaalafuncióndelaclasebasepermute().Laclasederivadatieneaccesodirectoatodaslasfuncionespúblicasdelaclasebase.Lafunciónset()enlaclasederivadaredenelafunciónset()delaclasebase.Estoes,sillamaalasfuncionesread()ypermute()deunobjetoY,conseguirálasversionesdelaclasebase(estoesloqueestaocurriendodentrodemain()).Perosillamamosaset()enunobjetoY,conseguiremoslaversiónredenida.Estosignicaquesinodeseamosuncomportamientodeunafuncióndurantelaherencia,sepuedecambiar.(Tambiénsepuedenañadirfuncionescompletamentenuevascomochange().)Sinembargo,cuandoredenimosunafunción,puedeserquedeseellamaralaversióndelaclasebase.Si,dentrodeset(),simplementellamaaset(),conseguiremosunaversiónlocaldelafunción-unafunciónrecursiva.Parallamaralaversióndelaclasebase,sedebeexplícitamenteutilizarelnombredelaclasebaseyeloperadorderesolucióndealcance.14.3.ListadeinicializadoresdeunconstructorHemosvistoloimportantequeesenC++garantizarunacorrectainicialización,yestonovaacambiarenlacomposiciónnienlaherencia.Cuandosecreaunobjeto,elcompiladorgarantizalaejecucióntodoslosconstructoresparacadaunodelossubobjetos.Hastaahora,enlosejemplos,todoslossubobjetostienenunconstructorpordefecto,queesejecutadoporelcompiladorautomáticamente.Peroqueocurresiunodenuestrossubobjetosnotieneconstructorespordefecto,osiqueremoscambiarlosparámetrospordefectodeunconstructor.Estosuponeunproblema,porqueelconstructordelanuevaclasenotienepermisoparaaccederalosmiembrosprivadosdelsubobjetoyporello,nopuedeinicializarlosdirectamente.Lasoluciónessimple:ejecutarelconstructordelsubobjeto.C++proporcionaunasintaxisespecialparaello,lalistadeinicializadoresdeunconstructor.Laformadelalistadeinicializadoresdeunconstructordemuestracomoactúacomolaheren-cia.Conlaherencia,lasclasesbasessoncolocadasdespuésdedospuntosyjustodespués,puedeabrirlallaveparaempezarconelcuerpodelaclase.Enlalistadeinicializadoresdeunconstructor,secolocalasllamadasalosconstructoresdelossubobjetos,despuéslosargumentosdelconstructorylosdospuntos,perotodoesto,antesdeabrirelbrazodelcuerpodelafunción.EnunaclaseMyType,queheredadeBar,seríadelasiguientemanera:
406
i
i
“Volumen1”—2012/1/12—13:52—page407—#445i
i
i
i
i
i
14.3.Listadeinicializadoresdeunconstructor
MyType::MyType(inti):Bar(i){//...
sielconstructordeBartuvieraunsoloparámetrodeltipoint.14.3.1.InicializacióndeobjetosmiembrosLainicializacióndeobjetosmiembrosdeunaclaseutilizalamismasintaxiscuan-doseusalacomposición.Paralacomposición,seproporcionanlosnombresdelosobjetosenlugardelosnombresdelasclases.Sisetienemásdeunallamadaalcons-tructorenlalistadeinicializadores,lasllamadasseseparanconcomas:
MyType2::MyType2(inti):Bar(i),m(i+1){//...
EstaseríalaformadeunconstructordelaclaseMyType2,lacualheredadeBarycontieneunmiembroobjetollamadom.Fíjesequemientraspodemosvereltipodelaclasebaseenlalistadeinicializadoresdelconstructor,sólopodemosverelmiembroidenticadorobjeto.14.3.2.TipospredenidosenlalistadeinicializadoresLalistadeinicializadoresdelconstructorpermiteinvocarexplícitamentealosconstructoresdelosobjetosmiembros.Dehecho,noexisteotraformadellamaraesosconstructores.Laideaesquelosconstructoressonllamadosantesdelaejecu-cióndelcuerpodelconstructordelanuevaclase.Deestaforma,cualquierllamadaquehagamosalasfuncionesmiembrosdelossubobjetossiempreseránobjetosini-cializados.Noexisteotramaneradeaccederalcuerpodelconstructorsinqueningúnconstructorllameatodoslosmiembrosobjetosylosobjetosdelaclasebase,esmás,elcompiladorcreaunconstructorocultopordefecto.EstoesotracaracterísticadeC++,quegarantizaqueningúnobjeto(opartedeunobjeto)puedanestardesdeunprincipiosinquesuconstructorseallamado.Laideadequetodoslosobjetosmiembrosesténinicializadosaliniciodelcons-tructoresunabuenaayudaparaprogramar.Unavezeneliniciodelconstructor,puedeasumirquetodoslossubobjetosestáncorrectamenteinicializadosycentrarseenlastareasquesedeseanrealizarenelconstructor.Sinembargo,existeuncontra-tiempo:¿Quéocurreconlosobjetospredenidos,aquellosquenotienenconstruc-tor?Parahacerunasintaxissólida,pienseenlostipospredenidoscomosituviesenunsoloconstructor,conunsoloparámetro:unavariabledelmismotipocomoelqueestainicializando.Estoes
//:C14:PseudoConstructor.cpp
classX{
inti;
floatf;
charc;
char*s;
public:
X():i(7),f(1.4),c('x'),s("howdy"){}
};
intmain(){
407
i
i
“Volumen1”—2012/1/12—13:52—page409—#447i
i
i
i
i
i
14.3.Listadeinicializadoresdeunconstructor
intmain(){
Cc(47);
}///:~
CheredadeBytieneunobjetomiembro("estacompuestode")deltipoA.Puedecompararquelalistadeinicializadorescontienelasllamadasalconstructordelaclasebaseylasconstructoresdelosobjetosmiembros.LafunciónC::f()redeneB::f(),queeraheredada,ytambiénllamaalaversióndelaclasebase.Además,sellamaaa.f().Fíjesequedurantetodoestetiempoestamoshablandoacercadelaredenicióndefuncionesdurantelaherencia;conunobjetomiembroquesólosepuedemanipularsuinterfazpública,noredenirla.Además,alllamaraf()enunobjetodelaclaseCnopodrállamaraa.f()siC::f()nohasidodenido,mientrasqueseríaposiblellamaraB::f().LlamadasautomáticasaldestructorAunquemuyamenudoseanecesariorealizarllamadasexplicitasalosconstruc-toresenlainicialización,nuncaseránecesariorealizarunallamadaexplicitaalosdestructoresporquesóloexisteundestructorparacadaclaseyéstenotieneparáme-tros.Sinembargo,elcompiladoraseguraquetodoslosdestructoressonllamados,estosignicaquetodoslosdestructoresdelajerarquía,desdeeldestructordelaclasederivadayretrocediendohastalaraíz,seránejecutados.Esnecesariodestacarquelosconstructoresydestructoressonunpocoinusualesenelmodoenquellamanasujerarquía,enunafunciónmiembronormalsólolafunciónensiesllamada,ningunaversióndelaclasebase.Siusteddeseallamaralaversióndelaclasebasedeunafunciónmiembronormal,deberásobrecargarlaydeberállamarlaexplícitamente.14.3.4.OrdendellamadadeconstructoresydestructoresEsimportanteconocerelordendelasllamadasdelosconstructoresydestructo-resdeunobjetoconvariossubobjetos.Elsiguienteejemplomuestracómofunciona:
//:C14:Order.cpp
//Constructor/destructororder
#includestr;êm0;
usingnamespacestd;
ofstreamout("order.out");
#defineCLASS(ID)classID{\
public:\
ID(int){out#ID"constructor\n";}\
~ID(){out#ID"destructor\n";}\
};
CLASS(Base1);
CLASS(Member1);
CLASS(Member2);
CLASS(Member3);
CLASS(Member4);
classDerived1:publicBase1{
409
i
i
“Volumen1”—2012/1/12—13:52—page415—#453i
i
i
i
i
i
14.5.Funcionesquenoheredanautomáticamente
//:C14:SynthesizedFunctions.cpp
//Functionsthataresynthesizedbythecompiler
#include&#xiost;&#xream;
usingnamespacestd;
classGameBoard{
public:
GameBoard(){cout"GameBoard()\n";}
GameBoard(constGameBoard&){
cout"GameBoard(constGameBoard&)\n";
}
GameBoard&operator=(constGameBoard&){
cout"GameBoard::operator=()\n";
return*this;
}
~GameBoard(){cout"~GameBoard()\n";}
};
classGame{
GameBoardgb;//Composition
public:
//DefaultGameBoardconstructorcalled:
Game(){cout"Game()\n";}
//YoumustexplicitlycalltheGameBoard
//copy-constructororthedefaultconstructor
//isautomaticallycalledinstead:
Game(constGame&g):gb(g.gb){
cout"Game(constGame&)\n";
}
Game(int){cout"Game(int)\n";}
Game&operator=(constGame&g){
//YoumustexplicitlycalltheGameBoard
//assignmentoperatorornoassignmentat
//allhappensforgb!
gb=g.gb;
cout"Game::operator=()\n";
return*this;
}
classOther{};//Nestedclass
//Automatictypeconversion:
operatorOther()const{
cout"Game::operatorOther()\n";
returnOther();
}
~Game(){cout"~Game()\n";}
};
classChess:publicGame{};
voidf(Game::Other){}
classCheckers:publicGame{
public:
//Defaultbase-classconstructorcalled:
Checkers(){cout"Checkers()\n";}
//Youmustexplicitlycallthebase-class
//copyconstructororthedefaultconstructor
415
i
i
“Volumen1”—2012/1/12—13:52—page416—#454i
i
i
i
i
i
Capítulo14.HerenciayComposición
//willbeautomaticallycalledinstead:
Checkers(constCheckers&c):Game(c){
cout"Checkers(constCheckers&c)\n";
}
Checkers&operator=(constCheckers&c){
//Youmustexplicitlycallthebase-class
//versionofoperator=()ornobase-class
//assignmentwillhappen:
Game::operator=(c);
cout"Checkers::operator=()\n";
return*this;
}
};
intmain(){
Chessd1;//Defaultconstructor
Chessd2(d1);//Copy-constructor
//!Chessd3(1);//Error:nointconstructor
d1=d2;//Operator=synthesized
f(d1);//Type-conversionISinherited
Game::Othergo;
//!d1=go;//Operator=notsynthesized
//fordifferingtypes
Checkersc1,c2(c1);
c1=c2;
}///:~
Losconstructoresyeloperator=deGameBoardyGamesedescribenporsisolosyporellodistinguirácuandosonutilizadosporelcompilador.Además,eloperadorOther()ejecutaunaconversiónautomáticadetipodesdeunobjetoGameaunobjetodelaclaseanidadaOther.LaclaseChesssimplementeheredadeGameynocreaningunafunción(sóloparavercomorespondeelcompilador)Lafunciónf()cogeunobjetoOtherparacomprobarlaconversiónautomáticadeltipo.Enmain(),elconstructorcreadopordefectoyelconstructorcopiadelaclasederivadaChesssonejecutados.LasversionesdeGamedeestosconstructoressonllamadoscomopartedelajerarquíadellamadasalosconstructores.Auncuandoestoesparecidoalaherencia,losnuevosconstructoressonrealmentecreadosporelcompilador.Comoesdeesperar,ningúnconstructorconargumentosesejecutadoautomáticamenteporqueesdemasiadotrabajoparaelcompiladorynoescapazdeintuirlo.Eloperator=estambiénescreadocomounanuevafunciónenChessusandolaasignación(así,laversióndelaclasebaseesejecutada)porqueestafunciónnofueexplícitamenteescritaenlanuevaclase.Y,porsupuestoeldestructorescreadoautomáticamenteporelcompilador.Elporquédetodasestasreglasacercadelareescrituradefuncionesenrelaciónalacreacióndeunobjetopuedenparecerunpocoextrañasenunaprimeraimpresiónycomoseheredanlasconversionesautomáticasdetipo.Perotodoestotienesentido-siexistensucientepiezasenGamepararealizarunobjetoOther,aquellaspiezasestántodavíaencualquierobjetoderivadodeGameyeltipodeconversiónesválido(auncuandopuede,silodesea,redenirlo).Eloperator=escreadoautomáticamentesóloparaasignarobjetodelmismotipo.Sideseaasignarotrotipo,deberáescribireloperator=ustedmismo.
416
i
i
“Volumen1”—2012/1/12—13:52—page418—#456i
i
i
i
i
i
Capítulo14.HerenciayComposición
implementarcaracterísticasensuclase,peroelusuariodesuclaseveelinterfazquesehadenido,envezdelinterfazdelaclaseoriginal.Parahaceresto,sesigueeltípicopatróndealojarobjetosprivadosdeclasesexistentesensunuevaclase.Enocasiones,sinembargo,tienesentidopermitirqueelusuariodelaclaseac-cedaalacomposicióndesuclase,estoes,hacerpúblicoslosmiembrosobjeto.Losmiembrosobjetousansucontroldeaccesos,entoncesesseguroycuandoelusuarioconocequeestaformandounconjuntodepiezas,hacequelainterfazseamásfácildeentender.UnbuenejemploeslaclaseCar:
//:C14:Car.cpp
//Publiccomposition
classEngine{
public:
voidstart()const{}
voidrev()const{}
voidstop()const{}
};
classWheel{
public:
voidinflate(intpsi)const{}
};
classWindow{
public:
voidrollup()const{}
voidrolldown()const{}
};
classDoor{
public:
Windowwindow;
voidopen()const{}
voidclose()const{}
};
classCar{
public:
Engineengine;
Wheelwheel[4];
Doorleft,right;//2-door
};
intmain(){
Carcar;
car.left.window.rollup();
car.wheel[0].inflate(72);
}///:~
ComolacomposicióndeCarespartedelanálisisdelproblema(ynounasimplecapadeldiseño),hacepúblicoslosmiembrosyayudanalprogramadoraentendercomoseutilizalaclaseyrequieremenoscomplejidaddecódigoparaelcreadordelaclase.Sipiensaunpoco,observaráquenotienesentidocomponerunCarusandoun
418
i
i
“Volumen1”—2012/1/12—13:52—page419—#457i
i
i
i
i
i
14.5.Funcionesquenoheredanautomáticamente
objeto"vehículo"-uncochenocontieneunvehículo,esunvehículo.Larelación"es-un"esexpresadoconlaherenciaylarelación"tieneun"esexpresadoconlacomposición.SubtipadoAhorasupongaquedeseacrearunobjetodeltipoifstreamquenosoloabreuncherosinoquetambiénguardeelnombredelchero.Puedeusarlacomposiciónealojarunobjetoifstreamyunstringenlanuevaclase:
//:C14:FName1.cpp
//Anfstreamwithafilename
#include"../require.h"
#include&#xiost;&#xream;
#includestr;êm0;
#include&#xstri;&#xng00;
usingnamespacestd;
classFName1{
ifstreamfile;
stringfileName;
boolnamed;
public:
FName1():named(false){}
FName1(conststring&fname)
:fileName(fname),file(fname.c_str()){
assure(file,fileName);
named=true;
}
stringname()const{returnfileName;}
voidname(conststring&newName){
if(named)return;//Don'toverwrite
fileName=newName;
named=true;
}
operatorifstream&(){returnfile;}
};
intmain(){
FName1file("FName1.cpp");
coutfile.name()endl;
//Error:close()notamember:
//!file.close();
}///:~
Sinembargo,existeunproblema.SeintentapermitirelusodeunobjetoFName1encualquierlugardóndeseutiliceunobjetoifstream,incluyendounaconversiónautomáticadeltipodesdeFName1aifstream&.Peroenmain,lalínea
file.close();
nocompilaráporquelaconversiónautomáticadetiposóloocurrecuandosella-maalafunción,nodurantelaseleccióndelmiembro.Porello,estamaneranofun-cionará.
419
i
i
“Volumen1”—2012/1/12—13:52—page421—#459i
i
i
i
i
i
14.5.Funcionesquenoheredanautomáticamente
mentequelocontiene.Estoesuntemamuyimportantequeseráexploradoalnaldeestecapituloyelsiguiente.HerenciaprivadaPuedeheredarutilizandounaclasebasedeformaprivadaborrandopublicenlalistadelaclasebaseoexplícitamenteutilizandoprivate(denitivamentelamejorpolíticaatomarpuesindicaalusuarioloquedeseahacer).Cuandoseheredadefor-maprivada,esta"implementadoentérminosde",estoes,seestacreandounanuevaclasequetienetodoslosdatosyfuncionalidaddelaclasebase,perolafuncionalidadestaoculta,solounapartedecapadeimplementación.Laclasederivadanotieneac-cesoalacapadefuncionalidadyunobjetonopuedesercreadocomoinstanciadelaclasebase(comoocurrióenFName2.cpp).Sesorprenderádelpropósitodelaherenciaprivada,porquelaalternativa,usarlacomposiciónparacrearunobjetoprivadoenlanuevaclaseparecemásapropia-da.Laherenciaprivadaestaincluidaparacompletarellenguajeperoparareducirconfusión,normalmenteseusarálacomposiciónenvezdelaherenciaprivada.Sinembargo,existenocasionesdondesedeseaelmismointerfazcomolaclasebaseyanulartratamientodelobjeto.Laherenciaprivadaproporcionaestahabilidad.14.5.2.2.1.PublicarlosmiembrosheredadosdeformaprivadaCuandosehe-redadeformaprivada,todoslosmiembrospúblicosdelaclasebasellegancomoprivados.Sideseaquecualquieradeellosseavisible,solousesusnombres(sinargu-mentosovaloresderetorno)juntoconlapalabraclaveusingenunasecciónpúblicadelaclasederivada:
//:C14:PrivateInheritance.cpp
classPet{
public:
chareat()const{return'a';}
intspeak()const{return2;}
floatsleep()const{return3.0;}
floatsleep(int)const{return4.0;}
};
classGoldfish:Pet{//Privateinheritance
public:
usingPet::eat;//Namepublicizesmember
usingPet::sleep;//Bothmembersexposed
};
intmain(){
Goldfishbob;
bob.eat();
bob.sleep();
bob.sleep(1);
//!bob.speak();//Error:privatememberfunction
}///:~
Así,laherenciaprivadaesútilsideseaesconderpartedelafuncionalidaddelaclasebase.Fíjesequesiexponeelnombredeunafunciónsobrecargada,exponetodaslasversionessobrecargadasdelafunciónenlaclasebase.
421
i
i
“Volumen1”—2012/1/12—13:52—page422—#460i
i
i
i
i
i
Capítulo14.HerenciayComposición
Debepensardetenidamenteantesdeutilizarlaherenciaprivadaenvezdelacomposición;laherenciaprivadatienecomplicacionesparticularescuandosoncom-binadasconlaidenticacióndetiposentiempodeejecución(esuntemadeunca-pítuloenelvolumen2,disponibleenwww.BruceEckel.com)14.6.ProtectedAhoraqueyasabequeeslaherencia,lapalabrareservadaprotectedyatienesig-nicado.Enunmundoideal,losmiembrosprivadossiempreserianjos-y-rápidos,peroenlosproyectosrealeshayocasionescuandosedeseaocultaralgoatodoelmundoytodavíapermitiraccesosalosmiembrosdelaclasederivada.Lapalabraclaveprotectedesunmovimientoalpragmatismo:estedice"Estoesprivadocomolaclaseusuarioencuestión,perodisponibleparacualquieraqueheredadeestaclase.Lamejormaneraesdejarlosmiembrosdedatosprivados-siempredebepre-servarsuderechodecambiarlacapadeimplementación.Entoncespuedepermitiraccesocontroladoalosherederosdesuclaseatravésdefuncionesmiembroprote-gidas:
//:C14:Protected.cpp
//Theprotectedkeyword
#includestr;êm0;
usingnamespacestd;
classBase{
inti;
protected:
intread()const{returni;}
voidset(intii){i=ii;}
public:
Base(intii=0):i(ii){}
intvalue(intm)const{returnm*i;}
};
classDerived:publicBase{
intj;
public:
Derived(intjj=0):j(jj){}
voidchange(intx){set(x);}
};
intmain(){
Derivedd;
d.change(10);
}///:~
Encontraráejemplosdelanecesidaddeusodeprotectedmásadelanteyenelvolumen2.14.6.1.HerenciaprotegidaCuandosehereda,pordefectolaclasebaseesprivada,quesignicaquetodoslasfuncionesmiembropublicassonprivadasparaelusuarioenlanuevaclase.Nor-
422
i
i
“Volumen1”—2012/1/12—13:52—page423—#461i
i
i
i
i
i
14.7.Herenciaysobrecargadeoperadores
malmente,heredarápúblicamenteparaqueelinterfazdelaclasebaseseatambiénelinterfazdelaclasederivada.Sinembargo,puedeusarlapalabraclaveprotecteddurantelaherencia.Derivardeformaprotegidasignica"implementadoentérminosde"paraotrasclasespero"es-una"paralasclasesderivadasyamigas.Esalgoquenoutilizarámuyamenudo,peroestaenellenguajeparacompletarlo.14.7.HerenciaysobrecargadeoperadoresExceptoeloperadordeasignación,elrestodeoperadoressonheredadosautomá-ticamenteenlaclasederivada.EstosepuededemostrarheredandodeC12:Byte.h:
//:C14:OperatorInheritance.cpp
//Inheritingoverloadedoperators
#include"../C12/Byte.h"
#includestr;êm0;
usingnamespacestd;
ofstreamout("ByteTest.out");
classByte2:publicByte{
public:
//Constructorsdon'tinherit:
Byte2(unsignedcharbb=0):Byte(bb){}
//operator=doesnotinherit,but
//issynthesizedformemberwiseassignment.
//However,onlytheSameType=SameType
//operator=issynthesized,soyouhaveto
//maketheothersexplicitly:
Byte2&operator=(constByte&right){
Byte::operator=(right);
return*this;
}
Byte2&operator=(inti){
Byte::operator=(i);
return*this;
}
};
//SimilartestfunctionasinC12:ByteTest.cpp:
voidk(Byte2&b1,Byte2&b2){
b1=b1*b2+b2%b1;
#defineTRY2(OP)\
out"b1=";b1.print(out);\
out",b2=";b2.print(out);\
out";b1"#OP"b2produces";\
(b1OPb2).print(out);\
outendl;
b1=9;b2=47;
TRY2(+)TRY2(-)TRY2(*)TRY2(/)
TRY2(%)TRY2(^)TRY2(&)TRY2(|)
TRY2()TRY2&#x-600;&#x-600;()TRY2(+=)TRY2(-=)
TRY2(*=)TRY2(/=)TRY2(%=)TRY2(^=)
TRY2(&=)TRY2(|=)TRY2��(=)TRY2()
423
i
i
“Volumen1”—2012/1/12—13:52—page425—#463i
i
i
i
i
i
14.10.Upcasting
cionesmiembros(yredeniendolasfuncionesexistentesdurantelaherencia)puededejarelcódigoexistente-porotroquetodavíaseestausando-quealguientodavíaloesteutilizando.Siocurrealgúnerror,ahorasabedondeestaelnuevocódigo,yentoncespodráleerlomasrápidoyfácilmentequesilohubieramodicadoenelcuerpodelcódigoexistente.Essorprendentecomolasclasessonlimpiamenteseparadas.Inclusonoesne-cesarioañadirelcódigofuenteconfuncionesmiembrosparareutilizarelcódigo,solamenteelcherodecabeceradescribiendolaclaseyelcheroobjetooelche-rodelibreríaconlasfuncionesmiembroscompiladas.(Estoesválidotantoparalaherenciacomoparalacomposición.)Estoesimportanteparahacerqueeldesarrolloseaunprocesoincremental,comoelaprendizajedeunapersona.Puedehacertantosanálisiscomodeseeperotodavíanosabrátodaslasrespuestascuandocongureunproyecto.Tendrámáséxitoyunprogresióninmediata-sisuproyectoempiezaacrecercomounacriaturaorgáni-ca,evolutiva,parecerámasbienqueesaconstruyendoalgocomounrascacielosdecristal[52]Sinembargolaherenciaesunatécnicaútilparalaexperimentación,enalgúnpuntodondelascosasestánestabilizadas,necesitaecharunnuevovistazoalajerar-quíadeclasesparacolapsarladentrodeunaestructurasensible[53].Recuerdeque,porencimadetodo,laherenciasignicaexpresarunarelaciónquedice"Estanuevaclaseesuntipodeestavieja".Suprogramanodebepreocuparsedecómomuevepedazosdebitsporalrededor,envezdebecrearymanipularobjetosdevariostiposparaexpresarunmodeloenlostérminosdadosparasuproblema.14.10.UpcastingAnteriormenteenestecapitulo,observocomounobjetodeunaclasequederi-vabadeifstreamteniatodaslascaracterísticasyconductasdeunobjetoifstream.EnFName2.cpp,cualquierfunciónmiembrodeifstreampodríaserllamadaporcual-quierobjetoFName2.Elaspectomásimportantedelaherencianoesproporcionarnuevasfuncionesmiembroalanuevaclase.Eslarelaciónexpresadaentrelanuevaclaseylaclasebase.Estarelaciónpuedeserresumidadiciendo"Lanuevaclaseesdeuntipodeunaclaseexistente".Estanoesunadescripciónfantasiosadeexplicarlaherencia-estadirectamentesoportadaporelcompilador.Unejemplo,considereunaclasebasellamadaInstru-mentquerepresentainstrumentosmusicalesyunaclasederivadallamadaWind.Dadoquelaherenciasignicaquetodaslasfuncionesenlaclasebaseestántambiéndisponiblesenlaclasederivada,cualquiermensajequeenviéalaclasebasepue-desertambiénenviadodesdeladerivada.EntoncessilaclaseInstrumenttieneunafunciónmiembroplay(),tambiénexistiráenlosinstrumentosdeWind.Estosigni-caprecisamentequeunobjetoWindesuntipodeInstrument.Elsiguienteejemplomuestracomoelcompiladorsoportaestaidea:
//:C14:Instrument.cpp
//Inheritance&upcasting
enumnote{middleC,Csharp,Cflat};//Etc.
classInstrument{
public:
voidplay(note)const{}
425
i
i
“Volumen1”—2012/1/12—13:52—page427—#465i
i
i
i
i
i
14.10.Upcasting
constructorescopiaparatodoslosmiembrosobjeto(orealizaráunacopiadebitsenlostipospredenidos)entoncesconseguirálaconductacorrecta:
//:C14:CopyConstructor.cpp
//Correctlycreatingthecopy-constructor
#include&#xiost;&#xream;
usingnamespacestd;
classParent{
inti;
public:
Parent(intii):i(ii){
cout"Parent(intii)\n";
}
Parent(constParent&b):i(b.i){
cout"Parent(constParent&)\n";
}
Parent():i(0){cout"Parent()\n";}
friendostream&
operator(ostream&os,constParent&b){
returnos"Parent:"b.iendl;
}
};
classMember{
inti;
public:
Member(intii):i(ii){
cout"Member(intii)\n";
}
Member(constMember&m):i(m.i){
cout"Member(constMember&)\n";
}
friendostream&
operator(ostream&os,constMember&m){
returnos"Member:"m.iendl;
}
};
classChild:publicParent{
inti;
Memberm;
public:
Child(intii):Parent(ii),i(ii),m(ii){
cout"Child(intii)\n";
}
friendostream&
operator(ostream&os,constChild&c){
returnos(Parent&)cc.m
"Child:"c.iendl;
}
};
intmain(){
Childc(2);
cout"callingcopy-constructor:"endl;
Childc2=c;//Callscopy-constructor
cout"valuesinc2:\n"c2;
427
i
i
“Volumen1”—2012/1/12—13:52—page428—#466i
i
i
i
i
i
Capítulo14.HerenciayComposición
}///:~
EloperadordeChildesinteresanteporlaformaenquellamaaloperadordelpadredentrodeéste:convirtiendoelobjetoChildaParent&(siloconvierteaunobjetodelaclasebaseenvezdeunareferencia,probablementeobtendráresultadosnodeseados)
returnos(Parent&)cc.m
DadoqueelcompiladorlovecomoParent,éstellamaaloperadorParent.PuedeobservarqueChildnotieneexplícitamentedenidounconstructorcopia.Elcompiladorcreaelconstructorcopia(esunadelascuatrofuncionesquesintetiza,juntoconelconstructordeldefecto-sinocreasaningunaconstructores-elopera-tor=yeldestructor)llamandoelconstructorcopiadeParentyelconstructorcopiadeMember.Estomuestralasiguientesalida
Parent(intii)
Member(intii)
Child(intii)
callingcopy-constructor:
Parent(constParent&)
Member(constMember&)
valuesinc2:
Parent:2
Member:2
Child:2
Sinembargo,siescribesupropioconstructorcopiaparaChildpuedetenerunerrorinocenteyfuncionarincorrectamente:
Child(constChild&c):i(c.i),m(c.m){}
entonceselconstructorpordefectoserállamadoautomáticamenteporlaclasebaseporpartedeChild,aquíesdóndeelcompiladormuestraunerrorcuandonotienenotra(recuerdequesiemprealgunconstructorseejecutaparacadaobjeto,sinimportarsiesunsubobjetodeotraclase).Lasalidaseráentonces:
Parent(intii)
Member(intii)
Child(intii)
callingcopy-constructor:
Parent()
Member(constMember&)
valuesinc2:
Parent:0
Member:2
Child:2
Estoprobablementenoesloqueespera,generalmentedesearáquelapartedelaclasebaseseacopiadadelobjetoexistentealnuevoobjetocomopartedelconstructorcopia.Paraarreglarelproblemadeberecordarcomofuncionalallamadaalconstruc-torcopiadelaclasebase(comoelcompiladorlohace)paraqueescribasupropioconstructorcopia.Estepuedeparecerunpocoextrañoaprimeravistaperoesotroejemplodeupcasting.
428
i
i
“Volumen1”—2012/1/12—13:52—page431—#469i
i
i
i
i
i
14.11.Resumen
14.11.ResumenHerenciaycomposiciónlepermitencrearnuevostiposdesdetiposexistentesyambosincluyensubobjetosdetiposexistentesdentrodelnuevotipo.Sinembargo,normalmente,utilizaralacomposiciónparareutilizartiposexistentescomopartedelacapadeimplementacióndeunnuevotipoylaherenciacuandodeseaforzaralnuevotipoaserdelmismotipoquelaclasebase(laequivalenciadetiposgarantizalaequivalenciadelainterfaz).Comolasclasesderivadastienenelinterfazdelaclasebase,estapuedeserconvertidasalabase,locualescríticoparaelpoliformismocomoveráelCapítulo15.Aunquelareutilizacióndecódigoatravésdelacomposiciónylaherenciaesmuyútilparaeldesarrollorápidodeproyectos,generalmentedeseararediseñarsujerarquíadeclasesantesdepermitiraotrosprogramadoresdependandeella.Suobjetivoescrearunajerarquíaenquecadaclasetengaunusoespecicoysinserdemasiadogrande(esforzándosemásenlafuncionalidadqueenladicultaddelareutilización...),nipequeña,(nosepodráusarporsimismoosinañadirfuncionali-dad).14.12.EjerciciosLassolucionesalosejerciciossepuedenencontrareneldocumentoelectróni-cotitulado«TheThinkinginC++AnnotatedSolutionGuide»,disponibleporpocodineroenwww.BruceEckel.com.1.ModicarCar.cppparaqueherededesdeunaclasellamadaVehicle,colocandocorrectamentelasfuncionesmiembroenVehicle(estoes,añadiralgunasfun-cionesmiembro).Añadaunconstructor(noeldepordefecto)aVehicle,quedebeserllamadodesdedentrodelconstructorCar2.Creardosclases,AyB,conconstructorpordefectosnoticándoseellosmis-mos.UnanuevaclasellamadaCqueheredadeA,ycreeunobjetomiembroBdentrodeC,peronocreeunconstructorparaC.CreeunobjetodelaclaseCyobservelosresultados.3.Crearunajerarquíadeclasesdetresnivelesconconstructorespordefectoycondestructores,ambosnoticándoseutilizandocout.Vericarqueelobjetomásaltodelajerarquía,lostresconstructoresydestructoressonejecutadosautomáticamente.Explicarelordenenquehansidorealizados.4.ModicarCombined.cppparaañadirotroniveldeherenciayunnuevoobjetomiembro.Añadirelcódigoparamostrarcuandolosconstructoresydestructo-ressonejecutados.5.EnCombined.cpp,crearunaclaseDqueherededeByquetengaunobjetomiembrodelaclaseC.Añadirelcódigoparamostrarcuandolosconstructoresylosdestructoressonejecutados.6.ModicarOrder.cppparaañadirotroniveldeherenciaDerived3conobjetosmiembrodelaclaseMember4yMember5.Compruebelasalidadelprograma.7.EnNameHidding.cpp,vericarqueDerived2,Derived3yDerived4,ningunaversióndelaclasebasedef()estadisponible.
431
i
i
“Volumen1”—2012/1/12—13:52—page436—#474i
i
i
i
i
i
Capítulo15.PolimorsmoyFuncionesvirtuales
compilador.Ustedsepuedeencontraratascadoenelnivelde"programaciónbasadaenobje-tos"debidoaqueesdefácilaccesoynorequieremuchoesfuerzomental.Estambiénsencillosentircómoestácreandotiposdedatos-ustedhaceclasesyobjetos,envíamensajesaesosobjetos,ytodoesbonitoypulcro.Peronoseatonto.Siseparaaquí,seestáperdiendounadelasmásimportantespartesdellenguaje,quesignicaelsaltoalaverdaderaprogramaciónorientadaaobjetos.Yestoseconsigueúnicamenteconlasfuncionesvirtuales.Lasfuncionesvirtualesrealzanelconceptodetipoenlugardesimplementeen-capsularcódigodentrodeestructurasydejarlodetrásdeunmuro,porloqueson,sinlugaradudas,elconceptomásdifíciladesentrañarporlosnuevosprogramadoresenC++.Sinembargo,sontambiénelpuntodecisivoparacomprenderlaprograma-ciónorientadaaobjetos.Sinousafuncionesvirtuales,todavíanoentiendelaPOO.Debidoaquelasfuncionesvirtualesestánintimamenteunidasalconceptodetipo,ylostipossonelnúcleodelaprogramaciónorientadaaobjetos,noexisteana-logíaalasfuncionesvirtualesdentrodeloslenguajesprocedurales.Comoprograma-dorprocedural,ustednotienereferenteconelquecompararlasfuncionesvirtuales,alcontrariodelasotrascaracterísticasdellenguaje.Lascaracterísticasdeunlen-guajeproceduralpuedenserentendidasenunnivelalgorítmico,perolasfuncionesvirtualesdebenserentendidasdesdeelpuntodevistadeldiseño.15.2.UpcastingEnelCapítulo14seviócomounobjetopuedeserusadocomounobjetodesupropiotipoocomounobjetodesutipobase.Ademáselobjetopuedesermanejadoatravésdesutipobase.Tomarladireccióndeunobjeto(ounpunteroounareferencia)ytratarlocomoladireccióndesutipobaseseconocecomoupcasting1debidoalcaminoquesegeneraenlosárbolesdeherenciaquesesuelenpintarconlaclasebaseenlacima.Tambiénseviósurgirunproblemaelcuálestáencarnadoenelsiguientecódigo:
//:C15:Instrument2.cpp
//Inheritance&upcasting
#include&#xiost;&#xream;
usingnamespacestd;
enumnote{middleC,Csharp,Eflat};//Etc.
classInstrument{
public:
voidplay(note)const{
cout"Instrument::play"endl;
}
};
//WindobjectsareInstruments
//becausetheyhavethesameinterface:
classWind:publicInstrument{
1NdelT:Pordesgraciaupcastingesotrodelostérminosalosquenoheencontradounatraduc-ciónconvincente(¿¿amoldarhaciaarriba??)ytieneelagravantequederivadeunaexpresiónampliamen-teusadaporlosprogramadoresdeC(¿Quiénnohahechonuncauncastavoid*;-)?.Seaceptansugerencias.
436
i
i
“Volumen1”—2012/1/12—13:52—page437—#475i
i
i
i
i
i
15.3.Elproblema
public:
//Redefineinterfacefunction:
voidplay(note)const{
cout"Wind::play"endl;
}
};
voidtune(Instrument&i){
//...
i.play(middleC);
}
intmain(){
Windflute;
tune(flute);//Upcasting
}///:~
Lafuncióntune()acepta(porreferencia)unInstrument,perotambiénaceptacualquiercosaquederivedeInstrument.Enelmain(),sepuedeverestecompor-tamientocuandosepasaunobjetoWindaafinar()sinquesenecesiteningúnmolde.LainterfazdeInstrumenttienequeexistirenWind,porqueWindheredasuspropiedadesdeInstrument.Moldearensentidoascendente(Upcasting)deW-indaInstrumentpuede"reducir"lainterfaz,peronuncapuedesermenorquelainterfazdeInstrument.Losmismosargumentossonciertoscuandotrabajamosconpunteros;laúnicadiferenciaesqueelusuariodebeindicarladireccióndelosobjtosdeformaexplícitacuandosepasenaunafunción.15.3.ElproblemaElproblemaconInstrument2.cpppuedeversealejecutarelprograma.Lasa-lidaesInstrument::play.Claramente,estanoeslasalidadeseada,porqueelobjetoesactualmenteunWindynosolounInstrument.Lallamadadeberíapro-ducirunWind::play.Porestemotivo,cualquierobjetodeunaclasequederivedelaclaseInstrumentdeberíausarsupropiaversióndeplay(),deacuerdoalasituación.ElcomportamientodeInstrument2.cppnoessorprendentedadalaaproxi-macióndeCalasfunciones.Paraentenderelresultadoesnecesariocomprenderelconceptodebinding(ligadura).15.3.1.LigaduradelasllamadasafuncionesConectarunallamadaaunafunciónalcuerpodelafunciónseconocecomobin-ding(vincular).Cuandolavinculaciónserealizaantesdeejecutarelprograma(porelcompiladoryellinker),selaconocecomoearlybinding(ligaduratemprana).Puedenohaberescuchadoanteriormenteestetérminodebidoaquenoesposibleconloslenguajesprocedurales:loscompiladoresdeCsóloadmitenuntipodevinculaciónqueeslavinculaciónanticipada.ElproblemaenelprogramaanteriorescausadoporlavinculaciónanticipadaporqueelcompiladornoconocelacorrectafunciónalaquedeberíallamarcuandosóloesunadireccióndeInstrument.
437
i
i
“Volumen1”—2012/1/12—13:52—page439—#477i
i
i
i
i
i
15.4.Funcionesvirtuales
};
voidtune(Instrument&i){
//...
i.play(middleC);
}
intmain(){
Windflute;
tune(flute);//Upcasting
}///:~
EstearchivoesidénticoaInstrument2.cppexceptoporlaadicióndelapa-labrareservadavirtualy,sinembargo,elcomportamientoessignicativamentediferente:AhoralasalidaesWind::play.15.4.1.ExtensibilidadConplay()denidocomovirtualenlaclasebase,sepuedenañadirtan-tosnuevostiposcomosequierasincambiarlafunciónplay().Enunprogramaorientadoaobjetosbiendiseñado,lamayoríadelasfuncionesseguiránelmodelodeplay()ysecomunicaránúnicamenteatravésdelainterfazdelaclasebase.Lasfuncionesqueusenlainterfazdelaclasebasenonecesitaránsercambiadasparasoportaralasnuevasclases.Aquíestáelejemplodelosinstrumentosconmásfuncionesvirtualesyunmayornúmerodenuevasclases,lascualestrabajandemaneracorrectaconlaantigua(sinmodicaciones)funciónplay():
//:C15:Instrument4.cpp
//ExtensibilityinOOP
#include&#xiost;&#xream;
usingnamespacestd;
enumnote{middleC,Csharp,Cflat};//Etc.
classInstrument{
public:
virtualvoidplay(note)const{
cout"Instrument::play"endl;
}
virtualchar*what()const{
return"Instrument";
}
//Assumethiswillmodifytheobject:
virtualvoidadjust(int){}
};
classWind:publicInstrument{
public:
voidplay(note)const{
cout"Wind::play"endl;
}
char*what()const{return"Wind";}
voidadjust(int){}
};
439
i
i
“Volumen1”—2012/1/12—13:52—page440—#478i
i
i
i
i
i
Capítulo15.PolimorsmoyFuncionesvirtuales
classPercussion:publicInstrument{
public:
voidplay(note)const{
cout"Percussion::play"endl;
}
char*what()const{return"Percussion";}
voidadjust(int){}
};
classStringed:publicInstrument{
public:
voidplay(note)const{
cout"Stringed::play"endl;
}
char*what()const{return"Stringed";}
voidadjust(int){}
};
classBrass:publicWind{
public:
voidplay(note)const{
cout"Brass::play"endl;
}
char*what()const{return"Brass";}
};
classWoodwind:publicWind{
public:
voidplay(note)const{
cout"Woodwind::play"endl;
}
char*what()const{return"Woodwind";}
};
//Identicalfunctionfrombefore:
voidtune(Instrument&i){
//...
i.play(middleC);
}
//Newfunction:
voidf(Instrument&i){i.adjust(1);}
//Upcastingduringarrayinitialization:
Instrument*A[]={
newWind,
newPercussion,
newStringed,
newBrass,
};
intmain(){
Windflute;
Percussiondrum;
Stringedviolin;
Brassflugelhorn;
Woodwindrecorder;
440
i
i
“Volumen1”—2012/1/12—13:52—page441—#479i
i
i
i
i
i
15.5.CómoimplementaC++laligaduradinámica
tune(flute);
tune(drum);
tune(violin);
tune(flugelhorn);
tune(recorder);
f(flugelhorn);
}///:~
SepuedeverquesehaañadidootroniveldeherenciadebajodeWind,peroelmecanismovirtualfuncionacorrectamentesinimportarcuantosniveleshaya.Lafunciónadjust()noestáredenida(override)porBrassyWoodwind.Cuandoestoocurre,seusaladeniciónmás"cercana"enlajerarquíadeherencia-elcompiladorgarantizaqueexistaalgunadeniciónparaunafunciónvirtual,porloquenuncaacabaráenunallamadaquenoestéenlazadaconelcuerpodeunafunción(locualseríadesatroso).ElarrayA[]contienepunterosalaclasebaseInstrument,loqueimplicaqueduranteelprocesodeinicializacióndelarrayhabráupcasting.Estearrayylafunciónf()seránusadosenposterioresdiscusiones.Enlallamadaatune(),elupcastingserealizaencadatipodeobjeto,haciendoqueseobtengasiempreelcomportamientodeseado.Sepuededescribircomo"en-viarunmensajeaunobjetoydejaralobjetoquesepreocupesobrequéhacerconél".Lafunciónvirtualeslalenteausarcuandoseestáanalizandounproyecto:¿Dón-dedebenestarlasclasesbaseycómosedeseaextenderelprograma?Sinembargo,inclusosinosedescubrelainterfazapropiadaparalaclasebaseylasfuncionesvir-tualesdurantelacreacióndelprograma,amenudosedescubriránmástarde,inclusomuchomástardecuandosedeseeampliarosevayaahacerfuncionesdemanteni-mientoenelprograma.Estonoimplicaunerrordeanálisisodediseño;simplementesignicaquenoseconocíaonosepodíaconocertodalainformaciónalprincipio.DebidoalafuertemodularizacióndeC++,noesmuchoproblemaqueestosucedaporqueloscambiosquesehaganenunapartedelsistemanotiendenapropagarseaotraspartescomosucedeenC.15.5.CómoimplementaC++laligaduradinámica¿Cómofuncionalaligaduradinámica?Todoeltrabajoserealizadetrásdeltelóngraciasalcompilador,queinstalalosmecanismosnecesariosdelaligaduradinámi-cacuandosecreanfuncionesvirtuales.DebidoaquelosprogramadoressesuelenbeneciardelacomprensióndelmecanismodelasfuncionesvirtualesenC++,estasecciónmostrarálaformaenqueelcompiladorimplementaestemecanismo.Lapalabrareservadavirtualledicealcompiladorquenodeberealizarliga-duraestática.Alcontrario,debeinstalarautomáticamentetodoslosmecanismosne-cesariospararealizarlaligaduradinámica.Estosignicaquesisellamaaplay()paraunobjetoBrassatravésunadirecciónalaclasebaseInstrument,seusarálafunciónapropiada.Paraquefuncione,elcompiladortípico2creaunaúnicatabla(llamadaVTABLE)porcadaclasequecontengafuncionesvirtuales.Elcompiladorcolocalasdirec-cionesdelasfuncionesvirtualesdeesaclaseenconcretoenlaVTABLE.Encada
2Loscompiladorespuedenimplementarelcomportamientovirtualcomoquieran,peroelmodoaquídescritoesunaaproximacióncasiuniversal.
441
i
i
“Volumen1”—2012/1/12—13:52—page444—#482i
i
i
i
i
i
Capítulo15.PolimorsmoyFuncionesvirtuales
tringed,yBrassencajanenestacategoríaporquederivandeInstrument(estohacequetenganlamismainterfazdeInstrument,ypuedanresponderalosmis-mosmensajes),loqueimplicaquesusdireccionespuedensermetidasenelarray.Sinembargo,elcompiladornosabequeseanotracosaqueobjetosdetipoInstrume-nt,porloquenormalmentellamaráalasversionesdelasfuncionesqueesténenlaclasebase.Peroenestecaso,todaslasfuncioneshansidodeclaradasconlapalabrareservadavirtual,porloqueocurrealgodiferente.Cadavezquesecreaunaclasequecontienefuncionesvirtuales,osederivadeunaclasequecontienefuncionesvir-tuales,elcompiladorcreaparacadaclaseunaúnicaVTABLE,quesepuedeveraladerechaeneldiagrama.Enéstatablasecolocanlasdireccionesdetodaslasfuncio-nesquesondeclaradasvirtualesenlaclaseoenlaclasebase.Sinosesobreescribeunafunciónquehasidodeclaradacomovirtual,elcompiladorusaladireccióndelaversiónqueseencuentraenlaclasebase(estosepuedeverenlaentradaadjustadelaVTABLEdeBrass).Además,secolocaelVPTR(descubiertoenSizes.cpp)enlaclase.HayunúnicoVPTRporcadaobjetocuandoseusaherenciasimplecomoeselcaso.ElVPTRdebeestarinicializadoparaqueapuntealadireccióninicialdelaVTABLEapropiada(estosucedeenelconstructorqueseverámástardeconmayordetalle).UnavezqueelVPTRhasidoinicializadoalaVTABLEapropiada,elobjeto"sabe"dequetipoes.Peroesteautoconocimientonotienevaloramenosqueseausadoenelmomentoenquesellamaalafunciónvirtual.Cuandosellamaaunafunciónvirtualatravésdelaclasebase(lasituaciónquesedacuandoelcompiladornotienetodalainformaciónnecesariapararealizarlaliga-duraestática),ocurrealgoespecial.Envezderealizarselatípicallamadaafunción,queenlenguajeensambladoressimplementeunCALLaunadirecciónenconcreto,elcompiladorgeneracódigodiferenteparaejecutarlallamadaalafunción.Aquísemuestraaloquesepareceunallamadaaadjust()paraunobjetoBrass,sisehaceatravésdeunpunteroaInstrument(unareferenciaaInstrumentproduceelmismoefecto):
Figura15.2:TabladepunterosvirtualesElcompiladorempiezaconelpunteroaInstrument,queapuntaaladireccióninicialdelobjeto.TodoslosobjetosInstrumentolosobjetosderivadosdeInstr-umenttienensuVPTRenelmismolugar(amenudoalprincipiodelobjeto),detalformaqueelcompiladorpuedeconseguirelVPTRdelobjeto.ElVPTRapuntaalaladireccióninicialdeVTABLE.TodaslasdireccionesdefuncionesdelasVTABLEestándispuestasenelmismoorden,apesardeltipoespecícodelobjeto.play()eselprimero,what()eselsegundoyadjust()eseltercero.Elcompiladorsabequeapesardeltipoespecícodelobjeto,lafunciónadjust()seencuentralocalizadaenVPTR+2.Debidoaesto,envezdedecir,"LlamaalafunciónenladirecciónabsolutaInstrument::adjust()(ligaduraestáticayacciónequivocada),segeneracódigoquedice"LlamaalafunciónqueseencuentreenVPTR+2".ComolabúsquedadelVPTRyladeterminacióndeladireccióndelafunciónactualocurreentiempodeejecución,seconsigueladeseadaligaduradinámica.Seenvíaunmensajealobjeto,
444
i
i
“Volumen1”—2012/1/12—13:52—page445—#483i
i
i
i
i
i
15.5.CómoimplementaC++laligaduradinámica
yelobjetoseguraquedebehacerconél.15.5.3.DetrásdeltelónPuedeserútilverelcódigoensambladorquesegeneraconlallamadaaunafunciónvirtual,parapodervercomofuncionalaligaduradinámica.Aquíestálasalidadeuncompiladoralallamada
i.adjust(1);
dentrodelafunciónf(Instrument&i):
push1
pushsi
movbx,wordptr[si]
callwordptr[bx+4]
addsp,4
LosargumentosdeunallamadaaunafunciónC++,comolosdeaunafunciónC,soncolocadosenlapiladederechaaizquierda(esteordenesnecesarioparapodersoportarlaslistasdeargumentosvariablesdeC),porloqueelargumento1seponealprincipioenlapila.Enestepuntoenlafunción,elregistrosi(queespartedelaarquitecturadelprocesadorIntel™X86)contieneladireccióndei.Tambiénseintroduceenlapilaporqueesladireccióndecomienzodelobjetodeinterés.Hayquerecordarqueladireccióndelcomienzodelobjetocorrespondealvalordeth-is,ythisesintroducidoenlapilademaneraocultaantesdecualquierllamadaafunción,porloquelafunciónmiembrosabesobrequéobjetoenconcretoestátrabajando.Debidoaestoseverásiempreunomásqueelnúmerodeargumentosintroducidosenlapilaantesdeunallamadaaunafunciónmiembro(exceptoparalasfuncionesmiembrostatic,quenotienenthis).Ahorasepuedeejecutarlallamadaalafunciónvirtual.PrimerohayqueproducirelVPTRparapoderencontrarlaVTABLE.ParaelcompiladorelVPTRseinsertaalprincipiodelobjeto,porloqueelcontenidodethiscorrespondealVPTR.Lalínea
movbx,wordptr[si]
buscaladirección(word)alaqueapuntasi,queeselVPTRylacolocadentrodelregistrobx.ElVPTRcontenidoenbxapuntaaladireccióninicialdelaVTABLE,peroelpun-terodelafunciónallamarnoseencuentraenlaposicióncerodelaVTABLE,sinoenlasegundaposición(debidoaqueeslatercerafunciónenlalista).Debidoalmo-delodememoriacadapunteroafunciónocupadosbytes,porloqueelcompiladorsumacuatroalVPTRparacalculardondeestáladireccióndelafunciónapropiada.Hayquehacernotarqueesteesunvalorconstanteestablecidoentiempodecom-pilación,porloqueloúnicoqueocurreesqueelpunteroafunciónqueestáenlaposicióndosapuntaaadjust().Afortunadamente,elcompiladorseencargadetodoyseaseguradequetodoslospunterosafuncionesentodaslasVTABLEsdeunajerarquíaparticularsecreenconelmismoorden,apesardelordenenquesehayansobreescritolasfuncionesenlasclasesderivadas.UnavezsehacalculadoenlaVTABLEladireccióndelpunteroapropiado,sellamaalafunciónalaqueapuntaelpuntero.Estoes,sebuscaladirecciónysellama
445
i
i
“Volumen1”—2012/1/12—13:52—page449—#487i
i
i
i
i
i
15.7.Clasesbaseabstractasyfuncionesvirtualespuras
Figura15.3:ClaseabstractaLaúnicarazónparaestablecerunainterfazcomúnesquedespuéssepuedaex-presardeformadiferenteencadasubtipo.Secreaunaformabásicaquetieneloqueestáencomúncontodaslasclasesderivadasynadamás.Poresto,Instrumentesuncandidatoperfectoparaserunaclaseabstracta.Secreaunaclaseabstractasólocuandosequieremanipularunconjuntodeclasesatravésdeunainterfazcomún,perolainterfazcomúnnonecesitatenerunaimplementación(ocomomucho,nonecesitaunaimplementacióncompleta).SisetieneunconceptocomoInstrumentquefuncionacomoclaseabstracta,losobjetosdeesaclasecasinuncatendránsentido.Esdecir,Instrumentsirvesola-menteparaexpresarlainterfaz,ynounaimplementaciónparticular,porloquecrearunobjetoqueseaúnicamenteunInstrumentnotendrásentido,yprobablementesequierapreveniralusuariodehacerlo.SepuedesolucionarhaciendoquetodaslasfuncionesvirtualesenInstrumentmuestrenmensajesdeerror,peroretrasalaaparicióndeloserroresaltiempodeejecuciónloqueobligaráauntesteoexhausti-voporpartedelusuario.Esmuchomásproductivocazarelproblemaentiempodecompilación.Aquíestálasintaxisusadaparaunafunciónvirtualpura:
virtualvoidf()=0;
Haciendoesto,seindicaalcompiladorquereserveunhuecoparaunafunciónenlaVTABLE,peroquenopongaunadirecciónenesehueco.Inclusoaunquesólouna
449
i
i
“Volumen1”—2012/1/12—13:52—page450—#488i
i
i
i
i
i
Capítulo15.PolimorsmoyFuncionesvirtuales
funciónenunaclaseseadeclaradacomovirtualpura,laVTABLEestaráincompleta.SilaVTABLEdeunaclaseestáincompleta,¿quésesuponequedebehacerelcompiladorcuandoalguienintentecrearunobjetodeesaclase?Noseríasegurocrearunobjetodeesaclaseabstracta,porloqueseobtendríaunerrordepartedelcompilador.Dichodeotraforma,elcompiladorgarantizalapurezadeunaclaseabstracta.Hacerclasesabstractasaseguraqueelprogramadorclientenopuedehacermalusodeellas.AquítenemosInstrument4.cppmodicadoparausarfuncionesvirtualespu-ras.Debidoaquelaclasenotieneotracosaquenoseafuncionesvirtuales,selallamaclaseabstractapura:
//:C15:Instrument5.cpp
//Pureabstractbaseclasses
#include&#xiost;&#xream;
usingnamespacestd;
enumnote{middleC,Csharp,Cflat};//Etc.
classInstrument{
public:
//Purevirtualfunctions:
virtualvoidplay(note)const=0;
virtualchar*what()const=0;
//Assumethiswillmodifytheobject:
virtualvoidadjust(int)=0;
};
//Restofthefileisthesame...
classWind:publicInstrument{
public:
voidplay(note)const{
cout"Wind::play"endl;
}
char*what()const{return"Wind";}
voidadjust(int){}
};
classPercussion:publicInstrument{
public:
voidplay(note)const{
cout"Percussion::play"endl;
}
char*what()const{return"Percussion";}
voidadjust(int){}
};
classStringed:publicInstrument{
public:
voidplay(note)const{
cout"Stringed::play"endl;
}
char*what()const{return"Stringed";}
voidadjust(int){}
};
classBrass:publicWind{
public:
450
i
i
“Volumen1”—2012/1/12—13:52—page451—#489i
i
i
i
i
i
15.7.Clasesbaseabstractasyfuncionesvirtualespuras
voidplay(note)const{
cout"Brass::play"endl;
}
char*what()const{return"Brass";}
};
classWoodwind:publicWind{
public:
voidplay(note)const{
cout"Woodwind::play"endl;
}
char*what()const{return"Woodwind";}
};
//Identicalfunctionfrombefore:
voidtune(Instrument&i){
//...
i.play(middleC);
}
//Newfunction:
voidf(Instrument&i){i.adjust(1);}
intmain(){
Windflute;
Percussiondrum;
Stringedviolin;
Brassflugelhorn;
Woodwindrecorder;
tune(flute);
tune(drum);
tune(violin);
tune(flugelhorn);
tune(recorder);
f(flugelhorn);
}///:~
Lasfuncionesvirtualespurassonútilesporquehacenexplícitalaabstraccióndeunaclaseeindicanalusuarioyalcompiladorcómodebenserusadas.Hayquehacernotarquelasfuncionesvirtualespurasprevienenaunaclaseabs-tractadeserpasadasaunafunciónporvalor,loqueesunamaneradeprevenirelobjectslicing(queserádescritodeformareducida).Convertirunaclaseenabstractatambiénpermitegarantizarqueseusesiempreunpunteroounareferenciacuandosehagaupcastingaesaclase.SóloporqueunafunciónvirtualpuraimpidaalaVTABLEestarcompletanoimplicaquenosequieracrearcuerposdefunciónparaalgunadelasotrasfunciones.Amenudosequerrállamaralaversióndelafunciónqueestéenlaclasebase,inclusoaunqueéstaseavirtual.Esunabuenaideaponersiempreelcódigocomúntancercacomoseaposibledelaraizdelajerarquía.Nosóloahorracódigo,sinoquepermitefácilmentelapropagacióndecambios.
451
i
i
“Volumen1”—2012/1/12—13:52—page452—#490i
i
i
i
i
i
Capítulo15.PolimorsmoyFuncionesvirtuales
15.7.1.DenicionesvirtualespurasEsposibleproveerunadeniciónparaunafunciónvirtualpuraenlaclaseba-se.Todavíaimplicadecirlealcompiladorquenopermitacrearobjetosdeesaclasebaseabstracta,yquelasfuncionesvirtualespurasdebenserdenidasenlasclasesderivadasparapodercrearobjetos.Sinembargo,puedehaberuntrozodecódigoencomúnquesequierallamardesdetodas,oalgunasdelasclasesderivadasenvezdeestarduplicandocódigoentodaslasfunciones.Esteesunejemplodedenicióndefuncionesvirtuales.
//:C15:PureVirtualDefinitions.cpp
//Purevirtualbasedefinitions
#include&#xiost;&#xream;
usingnamespacestd;
classPet{
public:
virtualvoidspeak()const=0;
virtualvoideat()const=0;
//Inlinepurevirtualdefinitionsillegal:
//!virtualvoidsleep()const=0{}
};
//OK,notdefinedinline
voidPet::eat()const{
cout"Pet::eat()"endl;
}
voidPet::speak()const{
cout"Pet::speak()"endl;
}
classDog:publicPet{
public:
//UsethecommonPetcode:
voidspeak()const{Pet::speak();}
voideat()const{Pet::eat();}
};
intmain(){
Dogsimba;//Richard'sdog
simba.speak();
simba.eat();
}///:~
ElhuecoenlaVTABLEdePettodavíaestávacío,perotienefuncionesalasquesepuedellamardesdelaclasederivada.Otraventajadeestacaracterísticaesqueperimitecambiardeunafunciónvirtualcorrienteaunavirtualpurasindestrozarelcódigoexistente(esunaformaparalocalizarclasesquenosobreescribanaesafunciónvirtual).
452
i
i
“Volumen1”—2012/1/12—13:52—page455—#493i
i
i
i
i
i
15.8.HerenciaylaVTABLE
equivocadohabráproblemas.RTTIsedescribeposteriormenteenestecapítulo,yelVolumen2deestelibrotieneuncapítulodedicadoaltema.15.8.1.FIXME:ObjectslicingExisteunagrandiferenciaentrepasarunadireccióndeunobjetoapasarelobjetoporvalorcuandoseusaelpolimorsmo.Todoslosejemplosquesehanvisto,yprácticamentetodoslosejemplosqueseverán,sepasanporreferenciaynoporvalor.Estosedebeaquetodaslasdireccionestienenelmismotamaño6,porloquepasarladireccióndeuntipoderivado(quenormalmenteseráunobjetomásgrande)eslomismoquepasarladireccióndeunobjetodeltipobase(queesnormalmentemáspequeño).Comoseexplicóanteriormente,ésteeselobjetivocuandoseusaelpolimorsmo-elcódigoquemanejauntipobasepuede,tambiénmanejarobjetosderivadosdeformatransparenteSisehaceunupcastdeunobjetoenvezdeusarunpunteroounareferencia,pa-saráalgoquepuederesultarsorprendente:elobjetoes"truncado",recortado,hastaqueloquequedeseaunsubobjetoquecorrespondaaltipodestinodelmolde.Enelsiguienteejemplosepuedeverqueocurrecuandounobjetoestruncado(objectslicing):
//:C15:ObjectSlicing.cpp
#include&#xiost;&#xream;
#include&#xstri;&#xng00;
usingnamespacestd;
classPet{
stringpname;
public:
Pet(conststring&name):pname(name){}
virtualstringname()const{returnpname;}
virtualstringdescription()const{
return"Thisis"+pname;
}
};
classDog:publicPet{
stringfavoriteActivity;
public:
Dog(conststring&name,conststring&activity)
:Pet(name),favoriteActivity(activity){}
stringdescription()const{
returnPet::name()+"likesto"+
favoriteActivity;
}
};
voiddescribe(Petp){//Slicestheobject
coutp.description()endl;
}
6Actualmente,notodoslospunterostienenelmismotamañoentodoslasmáquinas.Sinembargo,enelcontextodeestadiscusiónsepuedenconsiderariguales.
455
i
i
“Volumen1”—2012/1/12—13:52—page456—#494i
i
i
i
i
i
Capítulo15.PolimorsmoyFuncionesvirtuales
intmain(){
Petp("Alfred");
Dogd("Fluffy","sleep");
describe(p);
describe(d);
}///:~
Lafuncióndescribe()recibeunobjetodetipoPetporvalor.Despuésllamaalafunciónvirtualdescription()delobjetoPet.Enelmain(),sepuedeesperarquelaprimerallamadaproduzca"ThisisAlfred",yquelasegundaproduzca"Fluffylikestosleep".Dehecho,ambasusanlaversióndescription()delaclasebase.Enesteprogramaestánsucediendodoscosas.Primero,debidoaquedescribe-()aceptaunobjetoPet(envezdeunpunteroounareferencia),cualquierllamadaadescribe()crearáunobjetodeltamañodePetqueserápuestoenlapilayposteriormentelimpiadocuandoacabelallamada.Estosignicaquesisepasaad-escribe()unobjetodeunaclaseheredadadePet,elcompiladorloacepta,perocopiaúnicamenteelfragmentodelobjetoquecorrespondaaunaPet.Sedeshechaelfragmentoderivadodelobjeto:
Figura15.5:ObjectslicingAhoraquedalacuestióndelallamadaalafunciónvirtual.Dog::descript-ion()haceusodetrozosdePet(quetodavíaexiste)ydeDog,¡elcualnoexisteporquefuetruncado!.Entonces,¿Quéocurrecuandosellamaalafunciónvirtual?Eldesastreesevitadoporqueelobjetoespasadoporvalor.Debidoaesto,elcom-piladorconoceeltipoexactodelobjetoporqueelobjetoderivadohasidoforzadoatransformarseenunobjetodelaclasebase.Cuandosepasaporvalor,seusaelcons-tructordecopiadelobjetoPet,queseencargadeinicializarelVPTRalaVTABLEdePetycopiasólolaspartesdelobjetoquecorrespondanaPet.Enelejemplonohayunconstructordecopiaexplícitoporloqueelcompiladorgenerauno.Quitandointerpretaciones,elobjetoseconvierterealmenteenunaPetduranteeltruncado.ElObjectSlicingquitapartedelobjetoexistenteysecopiaenunnuevoobjeto,envezdesimplementecambiarelsignicadodeunadireccióncuandoseusaunpunteroounareferencia.Debidoaesto,elupcastingaunobjetonoseusaamenudo;dehecho,normalmente,esalgoacontrolaryprevenir.Hayqueresaltarqueenesteejemplo,sidescription()fueraunafunciónvirtualpuraenlaclasebase(locualesbastanterazonabledebidoaquerealmentenohacenadaenlaclasebase),entonceselcompiladorimpediráelobjectslicingdebidoaquenosepuede"crear"unobjetodelaclasebase(quealnyalcaboesloquesucedecuandosehaceunupcastporvalor).éstopodríaserelvalormásimportantedelasfuncionesvirtualespuras:prevenirelobjectslicinggenerandounerrorentiempodecompilaciónsialguienlo
456
i
i
“Volumen1”—2012/1/12—13:52—page458—#496i
i
i
i
i
i
Capítulo15.PolimorsmoyFuncionesvirtuales
Derived1d1;
intx=d1.f();
d1.f(s);
Derived2d2;
x=d2.f();
//!d2.f(s);//stringversionhidden
Derived4d4;
x=d4.f(1);
//!x=d4.f();//f()versionhidden
//!d4.f(s);//stringversionhidden
Base&br=d4;//Upcast
//!br.f(1);//Derivedversionunavailable
br.f();//Baseversionavailable
br.f(s);//Baseversionabailable
}///:~
LaprimeracosaaresaltaresqueenDerived3,elcompiladornopermitirácam-biareltipoderetornodeunafunciónsobreescrita(lopermitiríasif()nofueravir-tual).éstaesunarestricciónimportanteporqueelcompiladordebegarantizarquesepuedallamardeforma"polimórca"alafunciónatravésdelaclasebase,ysilaclasebaseestáesperandoquef()devuelvaunint,entonceslaversióndef()delaclasederivadadebemanteneresecompromisoosinoalgofallará.Lareglaqueseenseñoenelcapítulo14todavíafunciona:sisesobreescribeunadelasfuncionesmiembrosobrecargadasdelaclasebase,lasotrasversionessobre-cargadasestaránocultasenlaclasederivada.Enelmain()elcódigodeDerived4muestraloqueocurreinclusosilanuevaversióndef()noestáactualmentesobre-escribiendounafunciónvirtualexistentedelainterfaz-ambasversionesdef()enlaclasebaseestanocultasporf(int).Sinembargo,sisehaceunupcastded4aB-ase,entoncesúnicamentelasversionesdelaclasebaseestarándisponibles(porqueeselcompromisodelaclasebase)ylaversióndelaclasederivadanoestádisponible(debidoaquenoestáespecicadaenlaclasebase).15.9.1.TipoderetornovarianteLaclaseDerived3dearribavieneasugerirquenosepuedemodicareltipoderetornodeunafunciónvirtualcuandoessobreescrita.Engeneralesverdad,perohayuncasoespecialenelquesepuedemodicarligeramenteeltipoderetorno.Siseestádevolviendounpunteroounareferenciaaunaclasebase,entonceslaversiónsobreescritadelafunciónpuededevolverunpunteroounareferenciaaunaclasederivada.Porejemplo:
//:C15:VariantReturn.cpp
//Returningapointerorreferencetoaderived
//typeduringovverriding
#include&#xiost;&#xream;
#include&#xstri;&#xng00;
usingnamespacestd;
classPetFood{
public:
virtualstringfoodType()const=0;
};
458
i
i
“Volumen1”—2012/1/12—13:52—page459—#497i
i
i
i
i
i
15.9.Sobrecargaryredenir
classPet{
public:
virtualstringtype()const=0;
virtualPetFood*eats()=0;
};
classBird:publicPet{
public:
stringtype()const{return"Bird";}
classBirdFood:publicPetFood{
public:
stringfoodType()const{
return"Birdfood";
}
};
//Upcasttobasetype:
PetFood*eats(){return&bf;}
private:
BirdFoodbf;
};
classCat:publicPet{
public:
stringtype()const{return"Cat";}
classCatFood:publicPetFood{
public:
stringfoodType()const{return"Birds";}
};
//Returnexacttypeinstead:
CatFood*eats(){return&cf;}
private:
CatFoodcf;
};
intmain(){
Birdb;
Catc;
Pet*p[]={&b,&c,};
for(inti=0;isizeofp/sizeof*p;i++)
coutp[i&#x-600;]-type()"eats"
p[i&#x-600;]-eats&#x-600;()-foodType()endl;
//Canreturntheexacttype:
Cat::CatFood*cf=c.eats();
Bird::BirdFood*bf;
//Cannotreturntheexacttype:
//!bf=b.eats();
//Mustdowncast:
bf=dynamic_castBird::BirdFood*�(b.eats());
}///:~
LafunciónmiembroPet::eats()devuelveunpunteroaPetFood.EnBird,éstafunciónmiembroessobreescritaexactamentecomoenlaclasebase,incluyendoeltipoderetorno.Estoes,Bird::eats()haceun�upcastdeBirdFoodaPetFoodenelretornodelafunción.PeroenCat,eltipodevueltoporeats()esunpunteroaCatFood,queesuntipoderivadodePetFood.Elhechodequeeltipoderetornoestéheredadodeltipo
459
i
i
“Volumen1”—2012/1/12—13:52—page460—#498i
i
i
i
i
i
Capítulo15.PolimorsmoyFuncionesvirtuales
deretornolafuncióndelaclasebaseeslaúnicarazónquehacequeestocompile.Deestaformaelacuerdosecumpletotalmente:eats()siempredevuelveunpunteroaPetFood.Sisepiensadeformapolimórcaloanteriornoparecenecesario.¿PorquénosimplementesehacenupcastdetodoslostiposretornadosaPetFood*comolohaceBird::eats()?Normalmenteesaesunabuenasolución,peroalnaldelmain()sepuedeverladiferencia:Cat::eats()puededevolvereltipoexactodePetFoo-d,mientrasquealvalorretornadoporBird::eats()hayquehacerleundowncastaltipoexacto.Devolvereltipoexactoesunpocomásgeneralyademásnopierdelainforma-ciónespecícadetipodebidaalupcastautomático.Sinembargo,devolveruntipodelaclasebasegeneralmenteresuelveelproblemaporloqueestoesunacaracterísticabastanteespecíca.15.10.funcionesvirtualesyconstructoresCuandosecreaunobjetoquecontienefuncionesvirtuales,suVPTRdebeserini-cializadoparaapuntaralacorrectaVTABLE.Estodebeserhechoantesdequeexistalaoportunidaddellamaraunafunciónvirtual.Comosepuedeadivinar,debidoaqueelconstructortieneeltrabajodetraeralaexistenciaalobjeto,tambiénserátraba-jodelconstructorinicializarelVPTR.ElcompiladordeformasecretaañadecódigoalprincipiodelconstructorparainicializarelVPTR.Ycomosedescribeenelcapí-tulo14,sinosecreaunconstructordeunaclasedeformaexplícita,elcompiladorgeneraunodeformaautomática.Silaclasetienefuncionesvirtuales,elconstruc-torincluiráelcódigoapropidadoparalainicializacióndelVPTR.Estotienevariasconsecuencias.Laprimeraconciernealaeciencia.Larazóndequeexistanfuncionesinli-neesreducirlasobrecargaqueproducellamarafuncionespequeñas.SiC++noproporcionafuncionesinline,elpreprocesadordebeserusadoparacrearestas"macros".Sinembargo,elpreprocesadornotienelosconceptosdeaccesosoclases,yademásnopuedeserusadoparacrearmacrosconfuncionesmiembro.Además,conlosconstructoresquedebentenercódigoocultoinsertadoporelcompilador,unamacrodelpreprocesadornofuncionaríadeltodo.Hayqueestarprecavidoscuandoseesténbuscandoagujerosdeecienciaporqueelcompiladorestáinsertandocódigoocultoenlosconstructores.NosólohayqueinicializarelVPTR,tambiénhayquecomprobarelvalordethis(encasodequeeloperadornewdevuelvacero),yllamaralconstructordelaclasebase.Todojunto,éstecódigopuedetenerciertoimpactocuandosepensabaqueeraunasimplefuncióninline.Enparticular,eltamañodelconstructorpuedeaplastaralahorroqueseconsiguealreducirlasobrecargaenlasllamadas.Sisehacenunmontondellamadasaconstructoresinline,eltamañodelcódigopuedecrecersinningúnbenecioenlavelocidad.Cuandoestéanandoelcódigorecuerdeconsiderarelquitarlosconstructoresenlínea.15.10.1.OrdendelasllamadasalosconstructoresLasegundafacetainteresantedelosconstructoresylasfuncionesvirtualestie-nequeverconelordendelasllamadasalosconstructoresyelmodoenquelasllamadasvirtualessehacendentrodelosconstructores.
460
i
i
“Volumen1”—2012/1/12—13:52—page461—#499i
i
i
i
i
i
15.10.funcionesvirtualesyconstructores
Todoslosconstructoresdelaclasebasesonsiemprellamadosenelconstructordeunaclaseheredada.Estotienesentidoporqueelconstructortieneuntrabajoespecial:verqueelobjetoestáconstruidodeformaapropiada.Unaclasederivadasólotieneaccesoasuspropiosmiembros,ynoalosdelaclasebase.únicamenteelconstructordelaclasebasepuedeinicializardeformaadecuadaasuspropioselementos.Porlotantoesesencialquesellameatodoslosconstructores;deotraformaelobjetonoestaráconstruidodeformaadecuada.Estoesporloqueelcompiladorobligaahacerunallamadaporcadatrozoenunaclasederivada.Sellamaráalconstructorpordefectosinosehaceunallamadaexplícitaaunconstructordelaclasebase.Sinoexisteconstructorpordefecto,elcompiladorlocreará.Elordendelasllamadasalconstructoresimportante.Cuandosehereda,sesabetodosobrelaclasebaseysepuedeaccederatodoslosmiembrospúblicosyprotegi-dos(publicyprotected)delaclasebase.éstosignicaquesepuedeasumirquetodoslosmiembrosdelaclasebasesonválidoscuandoseestáenlaclasederivada.Enunafunciónmiembronormal,laconstrucciónyahaocurrido,porloquetodoslosmiembrosdetodaslaspartesdelobjetoyahansidoconstruidos.Dentrodelcons-tructor,sinembargo,hayqueasumirquetodoslosmiembrosqueseusenhansidoconstruidos.Laúnicamaneradegarantizarloesllamandoprimeroalconstructordelaclasebase.Entoncescuandoseestéenelconstructordelaclasederivada,todoslosmiembrosalosquesepuedaaccederenlaclasebasehansidoinicializados."Saberquetodoslosmiembrossonválidos"dentrodelconstructorestambiénlarazónporlaque,dentrodeloposible,sedebeinicializartodoslosobjetosmiembros(esdecir,losobjetospuestosenlaclasemediantecomposición).Sisesigueéstapráctica,sepuedeasumirquetodoslosmiembrosdelaclasebaseylosmiembrosobjetosdelobjetoactualhansidoinicializados.15.10.2.ComportamientodelasfuncionesvirtualesdentrodelosconstructoresLajerarquíadelasllamadasalosconstructoresplanteauninteresantedilema.¿Quéocurresiseestádentrodeunconstructorysellamaaunafunciónvirtual?Dentrodeunafunciónmiembroordinariasepuedeimaginarqueocurrirá-lalla-madavirtualesresueltaentiempodeejecuciónporqueelobjetonopuedeconocersilafunciónmiembroesdelaclaseenlaqueestáoesdeunaclasederivada.Porconsistencia,sepodríapensarquetambiénesloquedeberíaocurrirdentrodelosconstructores.Noeselcaso.Sisellamaaunafunciónvirtualdentrodeunconstructor,sóloseusalaversiónlocaldelafunción.Esdecir,elmecanismovirtualnofuncionadentrodelconstructor.éstecomportamientotienesentidopordosmotivos.Conceptualmente,eltrabajodelconstructoresdaralobjetounaexistencia.Dentrodecualquierconstructor,elobjetopuedeserformadosóloparcialmente-sepuedesabersóloquelosobjetosdelaclasebasehansidoinicializados,peronosepuedesaberqueclasesheredandeésta.Unafunciónvirtual,sinembargo,semueve"arriba"y"abajo"dentrodelajerarquíadeherencia.Llamaaunafuncióndeunaclasederivada.Sisepudierahacerestodentrodeunconstructor,seestaríallamandoaunafunciónquedebemanejarmiembrosquetodavíanohansidoinicializados,unarecetaseguraparaeldesastre.Elsegundomotivoesmecánico.Cuandosellamaaunconstructor,unadelasprimerascosasquehaceesinicializarsuVPTR.Sinembargo,sólopuedesaberqueesdeltipo"actual"-eltipoparaelquesehaescritoelconstructor.Elcódigodelconstructorignoracompletamentesielobjetoestáenlabasedeotraclase.Cuando
461
i
i
“Volumen1”—2012/1/12—13:52—page463—#501i
i
i
i
i
i
15.10.funcionesvirtualesyconstructores
funcionanconlosdestructorescomolohacenparalasotrasfuncionesexceptolosconstructores.
//:C15:VirtualDestructors.cpp
//Behaviorofvirtualvs.non-virtualdestructor
#include&#xiost;&#xream;
usingnamespacestd;
classBase1{
public:
~Base1(){cout"~Base1()\n";}
};
classDerived1:publicBase1{
public:
~Derived1(){cout"~Derived1()\n";}
};
classBase2{
public:
virtual~Base2(){cout"~Base2()\n";}
};
classDerived2:publicBase2{
public:
~Derived2(){cout"~Derived2()\n";}
};
intmain(){
Base1*bp=newDerived1;//Upcast
deletebp;
Base2*b2p=newDerived2;//Upcast
deleteb2p;
}///:~
Cuandoseejecutaelprograma,sevequedeletebpsólollamaaldestructordelaclasebase,mientrasquedeleteb2pllamaaldestructordelaclasederivadaseguidoporeldestructordelaclasebase,queeselcomportamientoquedeseamos.Olvidarhacervirtualaundestructoresunerrorpeligrosoporqueamenudonoafectadirectamentealcomportamientodelprograma,peropuedeintroducirdefor-maocultaagujerosdememoria.Además,elhechodequealgunadestrucciónestáteniendolugarpuedeenmascararelproblema.Esposiblequeeldestructorseavirtualporqueelobjetosabedequetipoes(loquenoocurredurantelaconstruccióndelobjeto).Unavezqueelobjetohasidoconstruido,suVPTResinicializadoysepuedenusarlasfuncionesvirtuales.15.10.4.DestructoresvirtualespurosMientrasquelosdestructoresvirtualespurossonlegalesenelStandardC++,hayunarestricciónañadidacuandoseusan:hayqueproveerdeuncuerpodefunciónalosdestructoresvirtualespuros.Estopareceantinatural;¿Cómopuedeunafunciónvirtualser"pura"sinecesitaelcuerpodeunafunción?Perosisetieneencuentaquelosconstructoresylosdestructoressonoperacionesespecialestienemássentido,especialmentesiserecuerdaquetodoslosdestructoresenunajerarquíadeclases
463
i
i
“Volumen1”—2012/1/12—13:52—page464—#502i
i
i
i
i
i
Capítulo15.PolimorsmoyFuncionesvirtuales
sonllamadossiempre.Sisequitaladenicióndeundestructorvirtualpuro,¿aquécuerpodefunciónsellamaráduranteladestrucción?Poresto,esabsolutamentenecesarioqueelcompiladoryelenlazadorrequieranlaexistenciadelcuerpodeunafunciónparaundestructorvirtualpuro.Siespuro,perolafuncióntienecuerpo¿cuálessuvalor?Laúnicadiferenciaqueseveráentreeldestructorvirtualpuroyelno-puroesqueeldestructorvirtualpuroconviertealaclasebaseenabstracta,porloquenosepuedecrearunobjetodelaclasebase(aunqueestotambiénseríaverdadsicualquierotrafunciónmiembrodeesaclasebasefueravirtualpura).Sinembargo,lascosassonunpococonfusascuandoseheredaunaclasedeotraquecontengaundestructorpurovirtual.Alcontrarioqueenelrestodelasfuncionesvirtualespuras,noesnecesariodarunadenicióndeundestructorvirtualpuroenlaclasederivada.Elhechodequeelsiguientecódigocompileeslaprueba:
//:C15:UnAbstract.cpp
//Purevirtualdestructors
//seemtobehavestrangely
classAbstractBase{
public:
virtual~AbstractBase()=0;
};
AbstractBase::~AbstractBase(){}
classDerived:publicAbstractBase{};
//Nooverridingofdestructornecessary?
intmain(){Derivedd;}///:~
Normalmente,unafunciónvirtualpuraenunaclasebasecausaráquelaclasede-rivadaseaabstractaamenosqueesa(ytodaslasdemásfuncionesvirtualespuras)tenganunadenición.Peroaquí,nopareceserelcaso.Sinembargo,hayquerecor-darqueelcompiladorcreaautomáticamenteunadenicióndeldestructorentodaslasclasessinosecreaunadeformaexplícita.Estoesloquesucedeaquí-eldestructordelaclasebaseessobreescritodeformaoculta,yunadeniciónespuestaporelcompiladorporloqueDerivednoesabstracta.Estonosbrindaunacuestióninteresante:¿Cuáleselsentidodeundestructorvirtualpuro?Alcontrarioqueconlasfuncionesvirtualespurasordinariasenlasquehayquedarelcuerpodeunafunción,enunaclasederivadadeotraconundestructorvirtualpuro,noseestáobligadoaimplementarelcuerpodelafunciónporqueelcompiladorgeneraautomáticamenteeldestructor.Entonces¿Cuálesladiferenciaentreundestructorvirtualnormalyundestructorvirtualpuro?Laúnicadiferenciaocurrecuandosetieneunaclasequesólotieneunafunciónvirtualpura:eldestructor.Enestecaso,elúnicoefectodelapurezadeldestructoresprevenirlainstanciacióndelaclasebase,perosinoexistenotrosdestructoresenlasclaseheredadas,eldestructorvirtualseejecutará.Poresto,mientrasqueelañadirundestructorvirtualesesencial,elhechodequeseapuroonoloseanoestanimportante.Cuandoseejecutaelsiguienteejemplo,sepuedeverquesellamaalcuerpodelafunciónvirtualpuradespuésdelaversiónqueestáenlaclasederivada,igualque
464
i
i
“Volumen1”—2012/1/12—13:52—page466—#504i
i
i
i
i
i
Capítulo15.PolimorsmoyFuncionesvirtuales
classDerived:publicBase{
public:
~Derived(){cout"~Derived()\n";}
voidf(){cout"Derived::f()\n";}
};
intmain(){
Base*bp=newDerived;//Upcast
deletebp;
}///:~
Durantelallamadaaldestructorvirtual,nosellamaaDerived::f(),inclusoaunquef()esvirtual.¿Aquéesdebidoésto?Supongamosquefuerausadoelmecanismovirtualdentrodeldestructor.Entoncesseríaposibleparalallamadavirtualresolverunafunciónqueestá"lejana"(másderivada)enlajerarquíadeherenciaqueeldestructoractual.Perolosdestructoressonllamadosde"afueraadentro"(desdeeldestructormásderivadohaciaeldestructordelaclasebase),porloquelallamadaactualalafunciónpuedeintentaraccederafragmentosdeunobjetoque!yahasidodestruido!Envezdeeso,elcompiladorresuelvelallamadaentiempodecompilaciónyllamasóloalaversiónlocaldelafunción.Hayqueresaltarquelomismoestambiénverdadparaelconstructor(comoseexplicóanteriormente),peroenelcasodelconstructoreltipodeinformaciónnoestabadisponible,mientrasqueeneldestructorlainformaciónestáahí(esdecir,elVPTR)peronoesaccesible.15.10.6.CreaciónunajerarquíabasadaenobjetosUnasuntoquehaaparecidodeformarecurrentealolargodetodoellibrocuan-doseusabanlasclasesStackyStashesel"problemadelapropiedad".El"pro-pietario"sereereaquienoalquesearesponsabledellamaraldeletedeaquellosobjetosquehayansidocreadosdinámicamente(usandonew).Elproblemacuandoseusancontenedoresesqueesnecesarioserlosucientementeexibleparamane-jardistintostiposdeobjetos.Paraconseguirlo,loscontenedoresmanejanpunterosavoidporloquenopuedensabereltipodelobjetoqueestánmanejando.Borrarunpunteroavoidnollamaaldestructor,porloqueelcontenedornopuedeserresponsabledeborrarsusobjetos.UnasoluciónfuepresentadaenelejemploC14:InheritStack.cpp,enelqueStackeraheredadoenunanuevaclasequeaceptabayproducíaúnicamenteobjetosstring,porloqueselespodíaborrardemaneraadecuada.Eraunabuenasoluciónperorequeríaheredarunanuevaclasecontenederaporcadatipoquesequisieramanejarenelcontenedor.(Aunquesueneunpocotediosofuncionabastantebiencomoseveráenelcapítulo16cuandolasplantillasotemplatesseanintroducidos).Elproblemaesquesequierequeelcontenedormanejemásdeuntipo,perosólosequierenusarpunterosavoid.Otrasoluciónesusarpolimorsmoforzandoatodoslosobjetosincluidosenelcontenedoraserheredadosdelamismaclasebase.Esdecir,elcontenedormanejalosobjetosdelaclasebase,ysólohayqueusarfuncionesvirtuales-enparticular,sepuedenllamaradestructoresvirtualesparasolucionarelproblemadepertenencia.Estasoluciónusaloqueseconocecomo"jerarquíaderaizúnica"(singly-rootedhie-rarchy)o"jerarquíabasadaenobjetos"(object-basedhierarchy),siendoelúltimonom-
466
i
i
“Volumen1”—2012/1/12—13:52—page469—#507i
i
i
i
i
i
15.11.Sobrecargadeoperadores
endl;
}///:~
AunqueessimilaralaversiónanteriordelprogramadepruebasdeStack,sepuedeverquesólosehansacado10elementosdelapila,loqueimplicaqueproba-blementequedealgúnelemento.ComolapilaahoramanejaObjects,eldestructorpuedelimpiarlosdeformaadecuada,comosepuedeverenlasalidadelprogramagraciasaquelosobjetosMyStringmuestranunmensajecuandosondestruidos.CrearcontenedoresquemanejenObjectsesunaaproximaciónrazonable-sisetieneunajerarquíaderaizúnica(debidoallenguajeoporalgúnrequerimientoqueobligueaquetodaslasclasesheredendeObject).Enestecaso,estágarantizadoquetodoesunObjectynoesmuycomplicadousarcontenedores.Sinembargo,enC++nosepuedeesperarestecomportamientodetodaslasclases,porloqueseestáabocadoausarherenciamúltiplesisequiereusarestaaproximación.Severáenelcapítulo16quelasplantillassolucionanesteproblemadeunaformamássimpleyelegante.15.11.SobrecargadeoperadoresSepuedencrearoperadoresvirtualesdeformaanálogaaotrasfuncionesmiem-bro.Sinembargoimplementaroperadoresvirtualessevuelveamenudoconfusoporqueseestáoperandosobredosobjetos,ambossintiposconocidos.Estosueleserelcasodeloscomponentesmatemáticos(paraloscualessesueleusarlasobrecar-gadeoperadores).Porejemplo,considereunsistemaqueusamatrices,vectoresyvaloresescalares,todosellosheredadosdelaclaseMath:
//:C15:OperatorPolymorphism.cpp
//Polymorphismwithoverloadedoperators
#include&#xiost;&#xream;
usingnamespacestd;
classMatrix;
classScalar;
classVector;
classMath{
public:
virtualMath&operator*(Math&rv)=0;
virtualMath&multiply(Matrix*)=0;
virtualMath&multiply(Scalar*)=0;
virtualMath&multiply(Vector*)=0;
virtual~Math(){}
};
classMatrix:publicMath{
public:
Math&operator*(Math&rv){
returnrv.multiply(this);//2nddispatch
}
Math&multiply(Matrix*){
cout"Matrix*Matrix"endl;
return*this;
}
469
i
i
“Volumen1”—2012/1/12—13:52—page470—#508i
i
i
i
i
i
Capítulo15.PolimorsmoyFuncionesvirtuales
Math&multiply(Scalar*){
cout"Scalar*Matrix"endl;
return*this;
}
Math&multiply(Vector*){
cout"Vector*Matrix"endl;
return*this;
}
};
classScalar:publicMath{
public:
Math&operator*(Math&rv){
returnrv.multiply(this);//2nddispatch
}
Math&multiply(Matrix*){
cout"Matrix*Scalar"endl;
return*this;
}
Math&multiply(Scalar*){
cout"Scalar*Scalar"endl;
return*this;
}
Math&multiply(Vector*){
cout"Vector*Scalar"endl;
return*this;
}
};
classVector:publicMath{
public:
Math&operator*(Math&rv){
returnrv.multiply(this);//2nddispatch
}
Math&multiply(Matrix*){
cout"Matrix*Vector"endl;
return*this;
}
Math&multiply(Scalar*){
cout"Scalar*Vector"endl;
return*this;
}
Math&multiply(Vector*){
cout"Vector*Vector"endl;
return*this;
}
};
intmain(){
Matrixm;Vectorv;Scalars;
Math*math[]={&m,&v,&s};
for(inti=0;i3;i++)
for(intj=0;j3;j++){
Math&m1=*math[i];
Math&m2=*math[j];
m1*m2;
}
}///:~
470
i
i
“Volumen1”—2012/1/12—13:52—page471—#509i
i
i
i
i
i
15.12.Downcasting
Parasimplicarsólosehasobrecargadoeloperator*.ElobjetivoessercapazdemultiplicardosobjetosMathcualquierayproducirelresultadodeseado-hayquedarsecuentaquemultiplicarunamatrizporunvectoresunaoperacióntotalmentedistintaalademultiplicarunvectorporunamatriz.Elproblemaesque,enelmain(),laexpresiónm1*m2contienedosreferenciasMath,ysondosobjetosdetipodesconocido.Unafunciónvirtualessólocapazdehacerunaúnicallamada-esdecir,determinareltipodeunúnicoobjeto.Parade-terminarambostiposenesteejemploseusaunatécnicaconocidacomodespachadomúltiple(multipledispatching),dondeloquepareceserunaúnicallamadaaunafun-ciónvirtualseconvierteenunasegundallamadaaunafunciónvirtual.Cuandolasegundallamadasehaejecutado,yasehandeterminadoambostiposdeobjetosysepuedeejecutarlaactividaddeformacorrecta.Enunprincipionoestransparante,perodespuésdeunratomirandoelcódigoempiezaacobrarsentido.EstamateriaestratadaconmásprofundidadenelcapítulodelospatronesdediseñoenelVolumen2quesepuedebajarde�www.BruceEckel.com.15.12.DowncastingComosepuedeadivinar,desdeelmomentoqueexistealgoconocidocomoup-casting-moverensentidoascendenteporunajerarquíadeherencia-debeexistireldowncastingparamoverensentidodescendenteenunajerarquía.Peroelupcastingessencilloporquealmovernosensentidoascendenteenlajerarquíadeclasessiem-preconvergemosenclasesmásgenerales.Esdecir,cuandosehaceunupcastsiempreseestáenunaclaseclaramentederivadadeunascendente(normalmentesolouno,exceptoenelcasodeherenciamúltiple)perocuandosehacedowncasthaynormal-mentevariasposibilidadesalasqueamoldarse.Masconcretamente,unCirculoesuntipodeFigura(queseríasuupcast),perosiseintentahacerundowncastdeunaFigurapodríaserunCirculo,unCuadrado,unTriángulo,etc.Elproblemaesencontrarunmodosegurodehacerdowncast(aunqueesinclusomásimportantepreguntarseporquéseestáusandodowncastingenvezdeusarelpolimorsmoparaqueadivineautomáticamenteeltipocorrecto.EnelVolumen2deestelibrosetratacomoevitareldowncasting.C++proporcionaunmoldeadoexplícitoespecial(introducidoenelcapítulo3)llamado"moldeadodinámico"(dynamic_cast)queesunaoperaciónsegura.Cuandoseusamoldeadodinámicoparaintentarhacerunmoldeauntipoenconcreto,elvalorderetornoseráunpunteroaltipodeseadosólosielmoldeesadecuadoytieneéxito,deotraformadevuelveceroparaindicarquenoesdeltipocorrecto.Aquítenemosunejemplomínimo:
//:C15:DynamicCast.cpp
#include&#xiost;&#xream;
usingnamespacestd;
classPet{public:virtual~Pet(){}};
classDog:publicPet{};
classCat:publicPet{};
intmain(){
Pet*b=newCat;//Upcast
//TrytocastittoDog*:
471
i
i
“Volumen1”—2012/1/12—13:52—page474—#512i
i
i
i
i
i
Capítulo15.PolimorsmoyFuncionesvirtuales
sentenciasconstyswitch),perosinembargofuncionaúnicamentedeformacon-junta,comounapartedeun"grancuadro"derelacionesentreclases.LagentesevuelveamenudoconfusaconotrascaracterísticasnoorientadasaobjetosdeC++comoeslasobrecargaylosargumentospordefecto,loscualessonpresentadosavecescomoorientadoaobjetos.Nonosliemos;sinohayligaduradinámica,nohaypolimorsmo.Parausarelpolimorsmo-yporlotanto,técnicasorientadasaobjetos-enlosprogramashayqueampliarlavisióndelaprogramaciónparaincluirnosolomiem-brosymensajesentreclasesindividuales,sinotambiénsuspuntosencomúnylasrelacionesentreellas.Aunquerequiereunesfuerzosignicativo,esrecompensadograciasaqueseconsiguemayorvelocidadeneldesarrollo,mejororganizacióndecódigo,programasextensibles,ymayormantenibilidad.Elpolimorsmocompletalascaracterísticasdeorientaciónaobjetosdellenguaje,perohaydoscaracterísticasfundamentalesmásenC++:plantillas(introducidasenelcapítulo16ycubiertasenmayordetalleenelsegundovolumendeestelibro),ymanejodeexcepciones(cubiertoenelVolumen2).Estascaracterísticasnospropor-cionanunincrementodepoderdecadaunadelascaracterísticasdelaorientaciónaobjetos:tipadoabstractodedatos,herencia,ypolimorsmo.15.14.EjerciciosLassolucionesalosejerciciossepuedenencontrareneldocumentoelectróni-cotitulado«TheThinkinginC++AnnotatedSolutionGuide»,disponibleporpocodineroenwww.BruceEckel.com.1.Creeunajerarquíasimple"gura":unaclasebasellamadaFigurayunaclasesderivadasllamadasCirculo,Cuadrado,yTriangulo.Enlaclasebase,hayquehacerunafunciónvirtualllamadadibujar(),ysobreescribirlaenlasclasesderivadas.HacerunarraydepunterosaobjetosFiguraquesecreenenelmontón(heap)yqueobliguearealizarupcastingdelospunteros,yllamaradibujar()atravésdelaclasebaseparavericarelcomportamientodelasfuncionesvirtuales.Sieldepuradorlosoporta,intenteverelprogramapasoapaso.2.ModiqueelEjercicio1detalformaquedibujar()seaunafunciónvirtualpura.IntentecrearunobjetodetipoFigura.Intentellamaralafunciónvirtualpuradentrodelconstructorymireloqueocurre.Dejándolocomounafunciónvirtualpuracreeunadeniciónparadibujar().3.AumentandoelEjercicio2,creeunafunciónqueuseunobjetoFiguraporvaloreintentehacerunupcastdeunobjetoderivadocomoargumento.Vealoqueocurre.ArreglelafunciónusandounareferenciaaunobjetoFigura.4.ModiqueC14:Combined.cppparaquef()seavirtualenlaclasebase.Cambieelmain()paraquesehagaunupcastyunallamadavirtual.5.ModiqueInstrument3.cppañadiendounafunciónvirtualpreparar().Llameapreparar()dentrodetune().6.CreeunajerarquíadeherenciadeRoedores:Raton,Gerbo,Hamster,etc.Enlaclasebase,proporcionelosmétodosquesoncomunesatodoslosroedores,yredenaaquellosenlasclasesderivadasparaquetengandiferentescomporta-mientosdependiendodeltipoespecícoderoedor.Creeunarraydepunteros
474
i
i
“Volumen1”—2012/1/12—13:52—page475—#513i
i
i
i
i
i
15.14.Ejercicios
aRoedor,rellenelocondistintostiposderoedoresyllamealosmétodosdelaclasebaseparaverloqueocurre.7.ModiqueelEjercicio6paraqueuseunvector*&#xRoed;&#xor]T;&#xJ 77;&#x.709;&#x -1.;݃ ;&#xTd [;envezdeunarraydepunteros.Aseguresequesehaceunlimpiadocorrectodelamemoria.8.EmpezandoconlajerarquíaanteriordeRoedor,heredeunHamsterAzuldeHamster(si,existealgoasí,tuveunocuandoeraniño),sobreescribalosméto-dosdelaclasebaseymuestrequeelcódigoquellamaalosmétodosdeclasebasenonecesitancambiarparaadecuarseelnuevotipo.9.ApartirdelajerarquíaRoedoranterior,añadaundestructornovirtual,creeunobjetodelaHamsterusandonew,hagaunupcastdelpunteroaRoedor*,yborreelpunterocondeleteparaversinosellamaalosdestructoresenlajerarquía.Cambieeldestructoravirtualydemuestrequeelcomportamientoesahoracorrecto.10.ModiqueRoedorparaconvertirloenunaclasebasepuraabstracta.11.CreeunsistemadecontrolaéreoconlaclasebaseAvionyvariostiposderiva-dos.CreeunaclaseTorreconunvector* vio;&#xn]TJ;&#x 71.;ܱ ;&#x-1.7;C T; [0;queenvielosmensajesadecuadosalosdistintosavionesqueestánbajosucontrol.12.CreeunmodelodeinvernaderoheredandovariostiposdePlantasyconstru-yendomecanismosenelinvernaderoqueseocupendelasplantas.13.EnEarly.cpp,hagaaPetunaclasebaseabstractapura.14.EnAddingVirtuals.cpp,hagaatodaslasfuncionesmiembrodePetvir-tualespuras,peroproporcioneunadeniciónparaname().ArregleDogcomoseanecesario,usandoladenicióndename()queseencuentraenlaclaseba-se.15.Escribaunpequeñoprogramaparamostrarladiferenciaentrellamaraunafunciónvirtualdentrodeunafunciónmiembronormalyllamaraunafunciónvirtualdentrodeunconstructor.Elprogramadeprobarquelasdosllamadasproducendiferentesresultados.16.ModiqueVirtualsInDestructors.cppporheredandounaclasedeD-erivedysobreescribiendof()yeldestructor.Enmain(),creeyhagaunupcastdeunobjetodesunuevotipo,despuésborrelo.17.UseelEjercicio16yañadallamadasaf()encadadestructor.Expliquequeocurre.18.Creeunclasequetengaundatomiembroyunaclasederivadaqueañadaotrodatomiembro.Escribaunafunciónnomiembroqueuseunobjetodelaclasebaseporvaloreimprimaeltamañodelobjetousandosizeof.Enelmain()creeunobjetodelaclasederivada,imprimasutamaño,yllameasufunción.Expliqueloqueocurre.19.Creeunejemplosencillodeunallamadaaunafunciónvirtualygeneresusalidaenensamblador.Localizeelcódigoenensambladorparalallamadaalafunciónvirtualyexpliqueelcódigo.20.Escribaunaclaseconunafunciónvirtualyunafunciónnovirtual.Heredeunanuevaclase,hagaunobjetodeesaclase,yunupcastaunpunterodeltipodelaclasebase.Uselafunciónclock()queseencuentraen&#x-500;ctime(necesitaráecharunvistazoasulibreríC)paramedirladiferenciaentreunallamada
475
i
i
“Volumen1”—2012/1/12—13:52—page476—#514i
i
i
i
i
i
Capítulo15.PolimorsmoyFuncionesvirtuales
virtualyunallamadanovirtual.Seránecesariorealizarmultiplesllamadasacadafunciónparapoderverladiferencia.21.ModiqueC14:Order.cppañadiendounafunciónvirtualenlaclasebasedelamacroCLASS(quepintealgo)yhaciendoeldestructorvirtual.Creeob-jetosdelasdistintassubclasesyhagalesunupcastalaclasebase.Veriquequeelcomportamientovirtualfuncionayqueserealizadeformacorrectalaconstrucciónyladestruccióndelobjeto.22.Escribaunaclasecontresfuncionesvirtualessobrecargadas.Heredeunanue-vaclaseysobreescribaunadelasfunciones.Creeunobjetodelaclasederiva-da.¿Sepuedellamaratodaslasfuncionesdelaclasebaseatravésdelobjetoderivado?Hagaunupcastdeladireccióndelobjetoalabase.¿Sepuedenlla-maralastresfuncionesatravésdelabase?Elimineladeniciónsobreescritaenlaclasederivada.Ahora¿Sepuedellamaratodaslasfuncionesdelaclasebaseatravésdelobjetoderivado?.23.ModiqueVariantReturn.cppparaquemuestrequesucomportamientofuncionaconreferenciasigualqueconpunteros.24.EnEarly.cpp,¿Cómoselepuedeindicaralcompiladorquehagalallama-dausandoligaduraestáticaoligaduradinámica?Determineelcasoparasupropiocompilador.25.Creeunaclasebasequecontengaunafunciónclone()quedevuelvaunpun-teroaunacopiadelobjetoactual.Derivedossubclasesquesobreescribanclo-ne()paradevolvercopiasdesustiposespecícos.Enelmain(),creeyhagaupcastdesusdostiposderivados,yllameaclone()paracadaunoyveri-quequelascopiasclonadassondelossubtiposcorrectos.Experimenteconsufunciónclone()paraquesepuedairaltipobase,ydespuésintenteregresaraltipoexactoderivado.¿Seleocurrealgunasituaciónenlaqueseanecesarioestaaproximación?26.ModiqueOStackTest.cppcreandosupropiaclase,despuéshagamultipleherenciaconObjectparacrearalgoquepuedaserintroducidoenlapila.Pruebesuclaseenelmain().27.AñadauntipollamadoTensoraOperartorPolymorphism.cpp.28.(Intermedio)CreeunaclasebaseXsindatosmiembroysinconstructor,peroconunafunciónvirtual.CreeunaYqueherededeX,perosinunconstructorexplícito.GenerecódigoensambladoryexamineloparadeteriminarsisecreaysellamaunconstructordeXy,siesoocurre,quécódigolohace.Expliqueloquehayadescubierto.Xnotieneconstructorpordefecto,entonces¿porquénosequejaelcompilador?29.(Intermedio)ModiqueelEjercicio28escribiendoconstructoresparaambasclasesdetalformaquecadaconstructorllameaunafunciónvirtual.Genereelcódigoensamblador.DeterminedondeseencuentraasignadoelVPTRdentrodelconstructor.¿Elcompiladorestáusandoelmecanismovirtualdentrodelconstructor?Expliqueporquésesigueusandolaversionlocaldelafunción.30.(Avanzado)Siunafunciónllamaaunobjetopasadoporvalorsiligaduraes-tática,unallamadavirtualaccedeapartesquenoexisten.¿Esposible?Escribauncódigoparaforzarunallamadavirtualyveasiseproduceuncuelguedelaaplicación.Paraexplicarelcomportamiento,observequeocurresisepasaunobjetoporvalor.
476
i
i
“Volumen1”—2012/1/12—13:52—page477—#515i
i
i
i
i
i
15.14.Ejercicios
31.(Avanzado)Encuentreexactamentecuantotiempomásesnecesarioparaunallamadaaunafunciónvirtualbuscandoenlainformacióndellenguajeensam-bladordesuprocesadorocualquierotromanualtécnicoyencontrandolospulsosderelojnecesariosparaunasimplellamadafrentealnúmeronecesariodelasinstruccionesdelasfuncionesvirtuales.32.DetermineeltamañodelVPTR(usandosizeof)ensuimplementación.Aho-raheredededosclases(herenciamúltiple)quecontenganfuncionesvirtuales.¿SetieneunaodosVPTRenlaclasederivada?33.Creeunaclasecondatosmiembrosyfuncionesvirtuales.Escribaunafunciónquemireenlamemoriadeunobjetodesuclaseyqueimprimasusdistintosfragmentos.ParahacerestoseránecesarioexperimentarydeformaiterativadescubrirdondeseencuentraalojadoelVPTRdelobjeto.34.Imaginequelasfuncionesvirtualesnoexisten,ymodiqueInstrument4.cppparaqueusemoldeadodinámicoparahacerelequivalentedelasllamadasvirtuales.Espliqueporqueesunamalaidea.35.ModiqueStaicHierarchyNavigation.cppparaqueenvezdeusarelRTTIdeC++usesupropioRTTIviaunafunciónvirtualenlaclasebasellama-dawhatAmI()yunenumtype{Circulos,Cuadrados};.36.ComienceconPointerToMemberOperator.cppdelcapítulo12ydemues-trequeelpolimorsmotodavíafuncionaconpunterosamiembros,inclusosi�operator-*estásobrecargado.
477
i
i
“Volumen1”—2012/1/12—13:52—page478—#516i
i
i
i
i
i
i
i
“Volumen1”—2012/1/12—13:52—page479—#517i
i
i
i
i
i
16:IntroducciónalasPlantillasLaherenciaylacomposiciónproporcionanunaformaderetilizarcódigoobjeto.LasplantillasdeC++proporcionanunamaneradereutilizarelcódigofuente.Aunquelasplantillas(otemplates)sonunaherramientadeprogramacióndepropósitogeneral,cuandofueronintroducidosenellenguaje,parecíanoponersealusodelasjerarquíasdeclasescontenedorasbasadasenobjetos(demostradoalnaldelCapítulo15).Además,loscontenedoresyalgoritmosdelC++Standard(explicadosendoscapítulosdelVolumen2deestelibro,quesepuedebajardewww.BruceEckel.com)estánconstruidosexclusivamenteconplantillasysonrela-tivamentefácilesdeusarporelprogramador.Estecapítulonosólomuestralosfundamentosdelostemplates,tambiénesunaintroducciónaloscontenedores,quesoncomponentesfundamentalesdelaprogra-maciónorientadaaobjetoslocualseevidenciaatravésdeloscontenedoresdelalibreríaestándardeC++.Severáqueestelibrohaestadousandoejemplosconte-nedores-StashyStack-parahacermássencilloelconceptodeloscontenedores;enestecapítulosesumaráelconceptodeliterator.Aunqueloscontenedoressonelejemploidealparausarlosconlasplantillas,enelVolumen2(quetieneunca-pítuloconplantillasavanzadas)seaprenderáquetambiénhayotrosusosparalostemplates.16.1.ContenedoresSupóngasequesequierecrearunapila,comosehaestadohaciendoatravésdeestelibro.Parahacerlosencillo,estaclasemanejaráenteros.
//:C16:IntStack.cpp
//Simpleintegerstack
//{L}fibonacci
#include"fibonacci.h"
#include"../require.h"
#include&#xiost;&#xream;
usingnamespacestd;
classIntStack{
enum{ssize=100};
intstack[ssize];
inttop;
public:
IntStack():top(0){}
voidpush(inti){
479
i
i
“Volumen1”—2012/1/12—13:52—page480—#518i
i
i
i
i
i
Capítulo16.IntroducciónalasPlantillas
require(topssize,"Toomanypush()es");
stack[top++]=i;
}
intpop(){
require(top�0,"Toomanypop()s");
returnstack[--top];
}
};
intmain(){
IntStackis;
//AddsomeFibonaccinumbers,forinterest:
for(inti=0;i20;i++)
is.push(fibonacci(i));
//Pop&printthem:
for(intk=0;k20;k++)
coutis.pop()endl;
}///:~
LaclaseIntStackesunejemplotrivialdeunapila.Paramantenerlasimplici-dadhasidocreadaconuntamañojo,perosepodríamodicarparaqueautomáti-camenteseexpandausandolamemoriadelmontón,comoenlaclaseStackquehasidoexaminadaatravésdellibro.main()añadealgunosenterosalapila,yposteriormentelosextrae.Parahacerelejemplomásinteresante,losenterossoncreadosconlafunciónfibonacci(),quegeneralostradicionalesnúmerosdelareproduccióndelconejo.Aquíestáelarchivodecabeceraquedeclaralafunción:
//:C16:fibonacci.h
//Fibonaccinumbergenerator
intfibonacci(intn);///:~
Aquíestálaimplementación:
//:C16:fibonacci.cpp{O}
#include"../require.h"
intfibonacci(intn){
constintsz=100;
require(nsz);
staticintf[sz];//Initializedtozero
f[0]=f[1]=1;
//Scanforunfilledarrayelements:
inti;
for(i=0;isz;i++)
if(f[i]==0)break;
while(in){
f[i]=f[i-1]+f[i-2];
i++;
}
returnf[n];
}///:~
480
i
i
“Volumen1”—2012/1/12—13:52—page481—#519i
i
i
i
i
i
16.2.Unvistazoalasplantillas
Estaesunaimplementaciónbastanteeciente,porquenuncasegeneranlosnú-merosmásdeunavez.Seusaunarraystaticdeint,ysebasaenelhechodequeelcompiladorinicializaráelarrayestáticoacero.Elprimerbucleformueveelíndiceialaprimeraposicióndelarrayqueseacero,entoncesunbuclewhileaña-denúmerosFibonaccialarrayhastaquesealcanceelelementodeseado.HayquehacernotarquesilosnúmerosFibonaccihastaelelementonyaestáninicializados,entoncestambiénsesaltaelbuclewhile.16.1.1.LanecesidaddeloscontenedoresObviamente,unapiladeenterosnoesunaherramientacrucial.Lanecesidadrealdeloscontenedoresvienecuandoseempizanacrearobjetosenelmontón(heap)usandonewysedestruyencondelete.Enunproblemageneraldeprogramaciónnosesabencuantosobjetosvanasernecesarioscuandoseestáescribiendoelpro-grama.Porejemplo,enunsistemadecontroldetrácoaéreonosequierelimitarelnúmerodeavionesqueelsistemapuedagestionar.Nopuedeserqueelprogramaseabortesóloporqueseexcedealgúnnúmero.Enunsistemadediseñoasistidoporcomputadora,seestánmanejandomontonesdeformas,peroúnicamenteelusuariodetermina(entiempodeejecución)cuantasformasseránnecesarias.Unavezapre-ciemosestastendencias,sedescubriránmontonesdeejemplosenotrassituacionesdeprogramación.LosprogramadoresdeCquedependendelamemoriavirtualparamanejarsu"gestióndememoria"encuentranamenudocomoperturbantenteslasideasdelnew,deleteydeloscontenedoresdeclases.Aparentemente,unaprácticaenCescrearunenormearrayglobal,másgrandequecualquiercosaqueelprogramaparezcanecesitar.Paraestonoesnecesariopensardemasiado(ohayquemeterseenelusodemalloc()yfree()),peroseproducenprogramasquenosepuedenportarbienyqueescondensutileserrores.Además,sisecreaunenormearrayglobaldeobjetosenC++,lasobrecargadelosconstructoresydelosdestructorespuedenenlentecerlascosasdeformasignicati-va.LaaproximacióndeC++funcionamuchomejor:Cuandosenecesiteunobjeto,secreaconnew,yseponesupunteroenuncontenedor.Mástarde,sesacaysehacealgoconél.Deestaforma,sólosecreanlosobjetoscuandoseanecesario.Ynormalmentenosedantodaslascondicionesparalainicializaciónalprincipiodelprograma.newpermiteesperarhastaquesucedaalgoenelentornoparapodercrearelobjeto.Así,enlasituaciónmáscomún,secrearáuncontenedorquealmacenelospun-terosdealgunosobjetosdeinterés.Secrearánesosobjetosusandonewysepon-dráelpunteroresultanteenelcontenedor(potencialmetehaciendoupcastingenelproceso),mástardeelobjetosepuederecuperarcuandoseanecesario.Estatécnicaproduceeltipodeprogramasmásexibleygeneral.16.2.UnvistazoalasplantillasAhorasurgeunnuevoproblema.TenemosunIntStack,quemanejaenteros.Peroqueremosunapilaquemanejeformas,ootasdeaviones,oplantasocualquierotracosa.Reinventarelcódigofuentecadaveznopareceunaaproximaciónmuyinteligenteconunlenguajequepropugnalareutilización.Debehaberuncaminomejor.Haytrestécnicasparareutilizarcódigoenestasituación:elmododeC,presen-
481
i
i
“Volumen1”—2012/1/12—13:52—page482—#520i
i
i
i
i
i
Capítulo16.IntroducciónalasPlantillas
tadoaquícomocontraste;laaproximacióndeSmalltalk,queafectódeformasigni-cativaaC++,ylaaproximacióndeC++:lostemplates.LasolucióndeC.PorsupuestohayqueescapardelaaproximacióndeCporqueesdesordenadayprovocaerrores,almismotiempoquenoesnadaelegante.Enestaaproximación,secopiaelcódigodeunaStackysehacenmodicacionesamano,introduciendonuevoserroresenelproceso.Estanoesunatécnicamuyproductiva.LasolucióndeSmalltalk.Smalltalk(yJavasiguiendosuejemplo)optóporunasoluciónsimpleydirecta:Sequierereutilizarcódigo,puesutiliceselaherencia.Paraimplementarlo,cadaclasecontenedoramanejaelementosdeunaclasebasegenéricallamadaObject(similaralejemplodelnaldelcapítulo15).PerodebidoaquelalibreríadeSmalltalkesfundamental,nosepuedecrearunaclasedesdelanada.Ensulugar,siemprehayqueheredardeunaclaseexistente.Seencuentraunaclaselomáscercanaposiblealoquesedesea,seheredadeella,ysehacenunpardecambios.Obviamente,estoesunbenecioporqueminimizaeltrabajo(yexplicaporquesepierdeunmontóndetiempoaprendiendolalibreríaantesdeserunprogramadorefectivoenSmalltalk).PerotambiénsignicaquetodaslasclasesdeSmalltalkacabansiendopartedeunúnicoárboldeherencia.Hayqueheredardeunaramadeesteárbolcuandoseestácreandounanuevaclase.Lamayoríadelárbolyaestaallí(eslalibreríadeclasesdeSmalltalk),ylaraizdelárbolesunaclasellamadaObject-lamismaclasequeloscontenedoresdeSmalltalkmanejan.EsuntrucoingeniosoporquesignicaquecadaclaseenlajerarquíadeherenciadeSmalltalk(yJava1)sederivadeObject,porloquecualquierclasepuedeseralmacenadaencualquiercontenedor(incluyendoalospropioscontenedores).Estetipodejerarquíadeárbolúnicabasadaenuntipogenéricofundamental(amenu-dollamadoObject,comotambiéneselcasoenJava)esconocidocomo"jerarquíabasadaenobjectos".SepuedehaberoidoestetéminoyasumidoqueesunnuevoconceptofundamentaldelaPOO,comoelpolimorsmo.Sinembargo,simplementesereerealaraízdelajerarquíacomoObject(oalgúntéminosimilar)yaconte-nedoresquealmacenanObjects.DebidoaquelalibreríadeclasesdeSmalltalkteníamuchamásexperienciaehistoriadetrásdelaqueteníaC++,yporqueloscompiladoresdeC++originalesnoteníanlibreríasdeclasescontenedoras,parecíaunabuenaideaduplicarlalibre-ríadeSmalltalkenC++.EstosehizocomoexperimentoconunadelasprimerasimplementaciónesdeC++2,ycomorepresentabaunsignicativoahorrodecódigomuchagenteempezoausarlo.Enelprocesodeintentarusarlasclasescontenedoras,descubrieronunproblema.ElproblemaesqueenSmalltalk(yenlamayoríadeloslenguajesdePOOqueyoconozco),todaslasclasesderivanautomáticamentedelajerarquíaúnica,peroestonoesciertoenC++.Sepuedetenerunamagnicajerarquíabasadaenobjetosconsusclasescontenedoras,peroentoncessecompraunconjuntodeclasesdeguras,odeavionesdeotrovendedorquenousaesajerarquía.(Estosedebeaqueusarunajerarquíasuponesobrecarga,rechazadaporlosprogramadoresdeC).¿Cómoseinsertaunárboldeclasesindependientesennuestrajerarquía?Elproblemaseparecealosiguiente:
1Conlaexcepción,enJava,delostiposdedatosprimitivos,quesehicieronnoObjectsporeciencia.2LalibreríaOOPS,porKeithGorlen,mientrasestabaenelNIH.
482
i
i
“Volumen1”—2012/1/12—13:52—page483—#521i
i
i
i
i
i
16.2.Unvistazoalasplantillas
Figura16.1:ContenedoresDebidoaqueC++suportamúltiplesjerarquíasindependientes,lajerarquíaba-sadaenobjetosdeSmalltalknofuncionatanbien.Lasoluciónparaceobvia.Sisepuedentenermúltiplesjerarquíasdeherencia,entonceshayquesercapacesdeheredardemásdeunaclase:Laherenciamúltipleresuelveelproblema.Porloquesepuedehacerlosiguiente(unejemplosimilarsedióalnaldelCapítulo15).
Figura16.2:HerenciamúltipleAhoraOShapetienelascaracterísticasyelcomportamientodeShape,perocomotambiénestáderivadodeObjectpuedeserinsertadoenelcontenedor.LaherenciaextradadaaOCircle,OSquare,etc.esnecesariaparaqueesasclasespuedanhacerupcasthaciaOShapeypuedanmantenerelcomportamientocorrecto.Sepuedevercomolascosasseestánvolviendoconfusasrápidamente.Losvendedoresdecompiladoresinventaroneincluyeronsuspropiasjerarquíasyclasescontenedoras,muchasdelascualeshansidoreemplazadasdesdeentoncesporversionesdetemplates.Sepuedeargumentarquelaherenciamúltipleesnecesariapararesolverproblemasdeprogramacióngeneral,perocomoseveráenelVolumen2deestelibroesmejorevitarestacomplejidadexceptoencasosespeciales.16.2.1.LasolucióndelaplantillaAunqueunajerarquíabasadaenobjetosconherenciamúltipleesconceptual-mentecorrecta,sevuelvedifícildeusar.Ensulibro3,Stroustrupdemostróloqueelconsiderabaunaalternativapreferiblealajerarquíabasadaenobjetos.Clasescon-tenedorasquefuerancreadascomograndesmacrosdelpreprocesadorconargu-mentosquepudieransersustituidosconeltipodeseado.Cuandosequieracrearun
3TheC++ProgrammingLanguagebyBjarneStroustrup(1ªedición,Addison-Wesley,1986)
483
i
i
“Volumen1”—2012/1/12—13:52—page484—#522i
i
i
i
i
i
Capítulo16.IntroducciónalasPlantillas
contenedorquemanejeuntipoenconcreto,sehacenunpardellamadasamacros.Desafortunadamente,estaaproximacióneraconfusaparatodalaliteraturaexis-tentedeSmalltalkyparalaexperienciadeprogramación,yeraunpocoinmanejable.Básicamente,nadielaentendía.Mientrastanto,StroustrupyelequipodeC++delosLaboratoriosBellhabíanmodicadosuaproximacióndelasmacros,simplicándolaymoviéndoladeldomi-niodelpreprocesadoralcompilador.Estenuevodispositivodesustitucióndecódigoseconocecomotemplate4(plantilla),yrepresentaunmodocompletamentedife-rentedereutilizarelcódigo.Envezdereutilizarcódigoobjeto,comoenlaherenciayenlacomposición,untemplatereutilizacódigofuente.ElcontenedornomanejaunaclasebasegenéricallamadaObject,sinoquegestionaunparámetronoespecica-do.Cuandoseusauntemplate,elparámetroessustituidoporelcompilador,parecidoalaantiguaaproximacióndelasmacros,peromásclaroyfácildeusar.Ahora,envezdepreocuparseporlaherenciaolacomposicióncuandosequierausarunaclasecontenedora,seusalaversiónenplantilladelcontenedorysecreaunaversiónespecícaparaelproblema,comolosiguiente:
Figura16.3:ContenedordeobjetosFiguraElcompiladorhaceeltrabajopornosotros,yseobtieneelcontenedornecesarioparahacereltrabajo,envezdeunajerarquíadeherenciainmanejable.EnC++,eltemplateimplementaelconceptodetipoparametrizado.Otrobeneciodelaaproxi-macióndelasplantillasesqueelprogramadornovatoquenotengafamiliaridadoestéincómodoconlaherenciapuedeusarlasclasescontenedorasdemaneraade-cuada(comosehaestadohaciendoalolargodellibroconelvector).16.3.SintaxisdelTemplateLapalabrareservadatemplateledicealcompiladorqueladenicióndeclasesquesiguemanipularáunoomástiposnoespecicados.Enelmomentoenqueelcódigodelaclaseactualesgenerado,lostiposdebenserespecicadosparaqueelcompiladorpuedasustituirlos.Parademostrarlasintaxis,aquíestáunpequeñoejemploqueproduceunarrayconlímitescomprobados:
//:C16:Array.cpp
#include"../require.h"
#include&#xiost;&#xream;
usingnamespacestd;
templateclassT&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;
4LainspiracióndelostemplatesparecevenirdelosgenericsdeADA
484
i
i
“Volumen1”—2012/1/12—13:52—page485—#523i
i
i
i
i
i
16.3.SintaxisdelTemplate
classArray{
enum{size=100};
TA[size];
public:
T&operator[](intindex){
require(index�=0&&indexsize,
"Indexoutofrange");
returnA[index];
}
};
intmain(){
Arrayint&#x]TJ/;ྖ ;.96;d T; 32;&#x.279;&#x 0 T; [0;ia;
Arrayfloat&#x]TJ/;ྖ ;.96;d T; 32;&#x.279;&#x 0 T; [0;fa;
for(inti=0;i20;i++){
ia[i]=i*i;
fa[i]=float(i)*1.414;
}
for(intj=0;j20;j++)
coutj":"ia[j]
","fa[j]endl;
}///:~
Sepuedeverquepareceunaclasenormalexceptoporlalínea.
templateclassT�
queindicaqueTesunparámetrodesustitución,yquerepresentaunnombredeuntipo.Además,sepuedeverqueTesusadoentodaslaspartesdelaclasedondenormalmenteseveríaaltipoespecícoqueelcontenedorgestiona.EnArrayloselementossoninsertadosyextraidosconlamismafunción:elope-radorsobrecargadooperator[].Devuelveunareferencia,porloquepuedeserusadoenambosladosdelsignoigual(esdecir,tantocomolvaluecomorvalue).Hayquehacernotarquesielíndicesesaledeloslímitesseusalafunciónrequir-e()paramostrarunmensaje.Comooperator[]esinline,sepuedeusarestaaproximaciónparagarantizarquenoseproducenviolacionesdellímitedelarrayparaentonceseliminarelrequire().Enelmain(),sepuedeverlofácilqueescrearArraysquemanejendistintostiposdeobjetos.Cuandosedice:
Arrayint�ia;
Arrayfloat�fa;
elcompiladorexpandedosveceslaplantilladelArray(queseconocecomoinstantiationocrearunainstancia),paracreardosnuevasclasesgeneradas,lascualespuedenserinterpretadascomoArray_intyArray_float.Diferen-tescompiladorespuedencrearlosnombresdediferentesmaneras.Estasclasessonidénticasalasquehubieranproducidodeestarhechasamano,exceptoqueelcom-piladorlascreapornosotroscuandosedenenlosobjetosiayfa.Tambiénhayquenotarquelasdenicionesdeclasesduplicadassoneludidasporelcompilador.
485
i
i
“Volumen1”—2012/1/12—13:52—page488—#526i
i
i
i
i
i
Capítulo16.IntroducciónalasPlantillas
débilrequiereúnicamentequelafunciónmiembroalaquesequierellamarestédis-ponibleparaunobjetoenparticular.Esdecir,elcódigodébilmentetipadopuedeseraplicadoacualquierobjetoqueacepteesasllamadasafuncionesmiembro,loquelohacemuchomásexible5.Aquítenemoselobjetorevisadoparacomprobarlaplantilla:
//:C16:StackTemplateTest.cpp
//Testsimplestacktemplate
//{L}fibonacci
#include"fibonacci.h"
#include"StackTemplate.h"
#include&#xiost;&#xream;
#includestr;êm0;
#include&#xstri;&#xng00;
usingnamespacestd;
intmain(){
StackTemplateint&#x]TJ/;ྖ ;.96;d T; 75;&#x.319;&#x 0 T; [0;is;
for(inti=0;i20;i++)
is.push(fibonacci(i));
for(intk=0;k20;k++)
coutis.pop()endl;
ifstreamin("StackTemplateTest.cpp");
assure(in,"StackTemplateTest.cpp");
stringline;
StackTemplatestring�strings;
while(getline(in,line))
strings.push(line);
while(strings.size()�0)
coutstrings.pop()endl;
}///:~
Laúnicadiferenciaestáenlacreacióndeis.Dentrodelalistadeargumentosdeltemplatehayqueespecicareltipodeobjetoquelapilayeliteradordeberánmanejar.Parademostrarlagenericidaddelaplantilla,secreaunStackTemplateparamanejarstring.Elejemploleelaslíneasdelarchivoconelcódigofuente.16.3.3.ConstantesenlosTemplatesLosargumentosdelostemplatesnorestrigensuusoatiposclass;sepuedentam-biénusartiposempotrados.Losvaloresdeestosargumentosseconviertenencons-tantesentiempodecompilaciónparaunainstanciaciónenparticulardelaplantilla.Sepuedenusarinclusovalorespordefectoparaesosargumentos.Elsiguienteejem-plonospermiteindicareltamañodelaclaseArraydurantelainstanciación,perotambiénproporcionaunvalorpordefecto:
//:C16:Array3.cpp
//Built-intypesastemplatearguments
#include"../require.h"
#include&#xiost;&#xream;
usingnamespacestd;
5TodoslosmétodosenSmalltalkyPythonestándébilmentetipados,yeseeselmotivoporloquees-toslenguajesnonecesitanelmecanismodelostemplates.Enefecto,seconsiguenplantillassintemplates.
488
i
i
“Volumen1”—2012/1/12—13:52—page489—#527i
i
i
i
i
i
16.3.SintaxisdelTemplate
templateclassT,intsize=&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;100
classArray{
Tarray[size];
public:
T&operator[](intindex){
require(index�=0&&indexsize,
"Indexoutofrange");
returnarray[index];
}
intlength()const{returnsize;}
};
classNumber{
floatf;
public:
Number(floatff=0.0f):f(ff){}
Number&operator=(constNumber&n){
f=n.f;
return*this;
}
operatorfloat()const{returnf;}
friendostream&
operator(ostream&os,constNumber&x){
returnosx.f;
}
};
templateclassT,intsize=&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;20
classHolder{
ArrayT,size�*np;
public:
Holder():np(0){}
T&operator[](inti){
require(0i&&isize);
if(!np)np=newArrayT,size�;
returnnp�-operator[](i);
}
intlength()const{returnsize;}
~Holder(){deletenp;}
};
intmain(){
HolderNumber�h;
for(inti=0;i20;i++)
h[i]=i;
for(intj=0;j20;j++)
couth[j]endl;
}///:~
Comoantes,Arrayesunarraydeobjetosqueprevienederebasarloslímites.LaclaseHolderesmuyparecidaaArrayexceptoquetieneunpunteroaArrayenvezdeuntenerincrustradounobjetodeltipoArray.Estepunteronoseinicializaenelconstructor;lainicializaciónesretrasadahastaelprimeracceso.Estoseconocecomoinicializaciónperezosa;sepuedeusarunatécnicacomoestasiseestáncreandounmontóndeobjetos,peronoseestáaccediendoatodosellosysequiereahorrar
489
i
i
“Volumen1”—2012/1/12—13:52—page490—#528i
i
i
i
i
i
Capítulo16.IntroducciónalasPlantillas
almacenamiento.Hayqueresaltarquenuncasealmacenainternamenteelvalordesizeenlaclase,peroseusacomosifueraundatointernodentrodelasfuncionesmiembro.16.4.StackyStashcomoPlantillasLosproblemasrecurrentesde«propiedad»conlasclasescontenedorasStackyStash(PilayColarespectivamente)quehansidousadasvariasvecesatravésdellibro,vienendelhechodequeestoscontenedoresnosoncapacesdesaberexac-tamentequetipomanejan.Lomáscercaquehanestadoesenel«contenedor»deobjectosStackquesevioalnaldelcapítulo15enOStackTest.cpp.Sielprogramadorclientenoeliminaexplícitamentetodoslospunterosaobjetoqueestánalmacenadosenelcontenedor,entonceselcontenedordeberíasercapazdeeliminaresospunterosdemaneraadecuada.Esdecir,elcontenedor«posee»cual-quieradelosobjetosquenohayansidoeliminados,yeselresponsabledelimpiarlos.Ladicultadradicaenqueellimpiadorequiereconocereltipodelobjeto,ycrearuncontenedorgenériconorequiereconocereltipodeeseobjeto.Conlostemplates,sinembargo,podemosescribircódigoquenoconozcaneltipodeobjeto,yfácilmenteinstanciarunanuevaversióndelcontenedorporcadatipoquequeramosquecon-tenga.Lainstanciacontenedoraindividualconoceeltipodeobjetosquemanejaypuedeportantollamaraldestructorcorrecto(asumiendoquesehayaproporciona-doundestructorvirtual).Paralapilaesbastantesencillodebidoatodaslasfuncionesmiembropuedenserintroducidasenlínea:
//:C16:TStack.h
//TheStackasatemplate
#ifndefTSTACK_H
#defineTSTACK_H
templateclassT&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;
classStack{
structLink{
T*data;
Link*next;
Link(T*dat,Link*nxt):
data(dat),next(nxt){}
}*head;
public:
Stack():head(0){}
~Stack(){
while(head)
deletepop();
}
voidpush(T*dat){
head=newLink(dat,head);
}
T*peek()const{
returnhead?head�-data:0;
}
T*pop(){
if(head==0)return0;
T*result=head�-data;
490
i
i
“Volumen1”—2012/1/12—13:52—page491—#529i
i
i
i
i
i
16.4.StackyStashcomoPlantillas
Link*oldHead=head;
head=head�-next;
deleteoldHead;
returnresult;
}
};
#endif//TSTACK_H///:~
SisecomparaestoalejemplodeOStack.halnaldelcapítulo15,severáqueStackesvirtualmenteidéntica,exceptoqueObjecthasidoreemplazadoconT.Elprogramadepruebatambiénescasiidéntico,exceptoporlanecesidaddemúltipleherenciadestringyObject(inclusoporlanecesidaddeObjectensímismo)quehasidoeliminada.AhoranotenemosunaclaseMyStringparaanunciarsudestrucciónporloqueañadimosunapequeñaclasenuevaparamostrarcomolaclasecontenedoraStacklimpiasusobjetos:
//:C16:TStackTest.cpp
//{T}TStackTest.cpp
#include"TStack.h"
#include"../require.h"
#includestr;êm0;
#include&#xiost;&#xream;
#include&#xstri;&#xng00;
usingnamespacestd;
classX{
public:
virtual~X(){cout"~X"endl;}
};
intmain(intargc,char*argv[]){
requireArgs(argc,1);//Filenameisargument
ifstreamin(argv[1]);
assure(in,argv[1]);
Stackstring�textlines;
stringline;
//ReadfileandstorelinesintheStack:
while(getline(in,line))
textlines.push(newstring(line));
//Popsomelinesfromthestack:
string*s;
for(inti=0;i10;i++){
if((s=(string*)textlines.pop())==0)break;
cout*sendl;
deletes;
}//Thedestructordeletestheotherstrings.
//Showthatcorrectdestructionhappens:
StackX�xx;
for(intj=0;j10;j++)
xx.push(newX);
}///:~
EldestructordeXesvirtual,noporqueseseanecesarioaquí,sinoporquexxpodríaserusadomástardeparamanejarobjetosderivadosdeX.
491
i
i
“Volumen1”—2012/1/12—13:52—page493—#531i
i
i
i
i
i
16.4.StackyStashcomoPlantillas
templateclassT,intincr&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;
T*PStashT,incr�::operator[](intindex)const{
require(index�=0,
"PStash::operator[]indexnegative");
if(index�=next)
return0;//Toindicatetheend
require(storage[index]!=0,
"PStash::operator[]returnednullpointer");
//Producepointertodesiredelement:
returnstorage[index];
}
templateclassT,intincr&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;
T*PStashT,incr�::remove(intindex){
//operator[]performsvaliditychecks:
T*v=operator[](index);
//"Remove"thepointer:
if(v!=0)storage[index]=0;
returnv;
}
templateclassT,intincr&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;
voidPStashT,incr�::inflate(intincrease){
constintpsz=sizeof(T*);
T**st=newT*[quantity+increase];
memset(st,0,(quantity+increase)*psz);
memcpy(st,storage,quantity*psz);
quantity+=increase;
delete[]storage;//Oldstorage
storage=st;//Pointtonewmemory
}
#endif//TPSTASH_H///:~
Eltamañodelincrementopordefectoesmuypequeñoparagarantizarqueseproduzcalallamadaainflate().Estonosaseguraquefuncionecorrectamente.ParacomprobarelcontroldepropiedaddePStackentemplate,lasiguienteclasemuestrainformesdecreaciónydestruccióndeelementos,ytambiéngaran-tizaquetodoslosobjetosquehayansidocreadosseandestruidos.AutoCounterpermitirácrearobjetosenlapilasóloalosobjetosdesutipo:
//:C16:AutoCounter.h
#ifndefAUTOCOUNTER_H
#defineAUTOCOUNTER_H
#include"../require.h"
#include&#xiost;&#xream;
#include&#xset0;//StandardC++Librarycontainer
#include&#xstri;&#xng00;
classAutoCounter{
staticintcount;
intid;
classCleanupCheck{
std::setAutoCounter*�trace;
public:
voidadd(AutoCounter*ap){
trace.insert(ap);
493
i
i
“Volumen1”—2012/1/12—13:52—page494—#532i
i
i
i
i
i
Capítulo16.IntroducciónalasPlantillas
}
voidremove(AutoCounter*ap){
require(trace.erase(ap)==1,
"AttempttodeleteAutoCountertwice");
}
~CleanupCheck(){
std::cout"~CleanupCheck()"std::endl;
require(trace.size()==0,
"AllAutoCounterobjectsnotcleanedup");
}
};
staticCleanupCheckverifier;
AutoCounter():id(count++){
verifier.add(this);//Registeritself
std::cout"created["id"]"
std::endl;
}
//Preventassignmentandcopy-construction:
AutoCounter(constAutoCounter&);
voidoperator=(constAutoCounter&);
public:
//Youcanonlycreateobjectswiththis:
staticAutoCounter*create(){
returnnewAutoCounter();
}
~AutoCounter(){
std::cout"destroying["id
"]"std::endl;
verifier.remove(this);
}
//Printbothobjectsandpointers:
friendstd::ostream&operator(
std::ostream&os,constAutoCounter&ac){
returnos"AutoCounter"ac.id;
}
friendstd::ostream&operator(
std::ostream&os,constAutoCounter*ac){
returnos"AutoCounter"ac&#x-600;-id;
}
};
#endif//AUTOCOUNTER_H///:~
LaclaseAutoCounterhacedoscosas.Primero,numeracadainstanciadeA-utoCounterdeformasecuencial:elvalordeestenúmeroseguardaenid,yelnúmerosegenerausandoeldatomiembrocountqueesstatic.Segundo,ymáscomplejo,unainstanciaestática(llamadaverifier)delaclaseCleanupChecksemantienealtantodetodoslosobjetosAutoCounterquesoncreadosydestruidos,ynosinformasinosehanlimpiadotodos(porejemplosiexisteunagujeroenmemoria).EstecomportamientosecompletaconelusodelaclasesetdelaLibreríaEstándardeC++,locualesunmagnícoejemplodecómolasplantillasbiendiseñadasnospuedenhacerlavidamásfácil(sepodráaprendermásdeloscontenedoresenelVolumen2deestelibro).Laclasesetestáinstanciadaparaeltipoquemaneja;aquíhayunainstanciaquemanejapunterosaAutoCounter.Unsetpermitequeseinsertesólounainstanciadecadaobjeto;enadd()sepuedeverqueestosucedeconlafunción
494
i
i
“Volumen1”—2012/1/12—13:52—page495—#533i
i
i
i
i
i
16.4.StackyStashcomoPlantillas
set::insert().insert()nosinformaconsuvalorderetornosiseestáinten-tandoañadiralgoqueyasehabíaincluido;sinembargo,desdeelmomentoenquelasdireccionesaobjetosseinsertenpodemosconarenC++paraquegaranticequetodoslosobjetostengandireccionesúnicas.Enremove(),seusaset::erase()paraeliminarunpunteroaAutoCou-nterdelset.Elvalorderetornoindicacuantasinstanciasdelelementosehaneliminado;ennuestrocasoelvalorpuedeserúnicamenteunoocero.Sielvalorescero,sinembargo,signicaqueelobjetoyahabíasidoborradodelconjuntoyqueseestáintentandoborrarporsegundavez,locualesunerrordeprogramaciónquedebesermostradomedianterequire().EldestructordeCleanupCheckhaceunacomprobaciónnalasegurándosedequeeltamañodelsetescero-Loquesignicaquetodoslosobjetoshansidoeliminadosdemaneraadecuada.Sinoescero,setieneunagujerodememoria,locualsemuestramedianteelrequire().ElconstructoryeldestructordeAutoCounterseregistraydesregistraconelobjetoverifier.Hayqueresaltarqueelconstructor,elconstructordecopia,yeloperadordeasignaciónsonprivate,porloquelaúnicaformadecrearunobjetoesconlafunciónmiembrostaticcreate()-estoesunejemplosencillodeunafactory,ygarantizaquetodoslosobjetosseancreadosenelmontón(heap),porloqueverifiernoseveráconfundidoconsobreasignacionesyconstruccionesdecopia.Comotodaslasfuncionesmiembrohansidodenidasinline,laúnicarazónparaelarchivodeimplementaciónesquecontengalasdenicionesdelosdatosmiembro:
//:C16:AutoCounter.cpp{O}
//Definitionofstaticclassmembers
#include"AutoCounter.h"
AutoCounter::CleanupCheckAutoCounter::verifier;
intAutoCounter::count=0;
///:~
ConelAutoCounterenlamano,podemoscomprobarlasfacilidadesquepro-porcionaelPStash.ElsiguienteejemplonosólomuestraqueeldestructordePS-tashlimpiatodoslosobjetosqueposee,sinoquetambiénmuestracomolaclaseAutoCounterdetectaalosobjetosquenosehanlimpiado.
//:C16:TPStashTest.cpp
//{L}AutoCounter
#include"AutoCounter.h"
#include"TPStash.h"
#include&#xiost;&#xream;
#includestr;êm0;
usingnamespacestd;
intmain(){
PStashAutoCounter�acStash;
for(inti=0;i10;i++)
acStash.add(AutoCounter::create());
cout"Removing5manually:"endl;
for(intj=0;j5;j++)
deleteacStash.remove(j);
cout"Removetwowithoutdeletingthem:"
495
i
i
“Volumen1”—2012/1/12—13:52—page500—#538i
i
i
i
i
i
Capítulo16.IntroducciónalasPlantillas
//:C16:SelfCounter.cpp{O}
#include"SelfCounter.h"
intSelfCounter::counter=0;///:~
//:C16:ValueStackTest.cpp
//{L}SelfCounter
#include"ValueStack.h"
#include"SelfCounter.h"
#include&#xiost;&#xream;
usingnamespacestd;
intmain(){
StackSelfCounter�sc;
for(inti=0;i10;i++)
sc.push(SelfCounter());
//OKtopeek(),resultisatemporary:
coutsc.peek()endl;
for(intk=0;k10;k++)
coutsc.pop()endl;
}///:~
CuandosecreauncontenedorStack,elconstructorpordefectodelobjetoaconteneresejecutadoporcadaobjetoenelarray.Inicialmenteseverán100obje-tosSelfCountercreadossinningúnmotivoaparente,peroestoesjustamentelainicializacióndelarray.Estopuederesultarunpococaro,peronoexisteningúnpro-blemaenundiseñosimplecomoeste.InclusoensituacionesmáscomplejassisehaceaStackmásgeneralpermitiendoquecrezcadinámicamente,porqueenlaimplementaciónmostradaanteriormenteestoimplicaríacrearunnuevoarraymásgrande,copiandoelanterioralnuevoydestruyendoelantiguoarray(dehecho,asíescomolohacelaclasevectordelaLibreríaEstándardeC++).16.7.IntroducciónalositeradoresUniteratoresunobjetoquesemueveatravésdeuncontenedordeotrosobjetosyseleccionaaunodeelloscadavez,sinporporcionarunaccesodirectoalaimplementacióndelcontenedor.Lositeradoresproporcionanunaformaestándardeaccederaloselementos,sinimportarsiuncontenedorproporcionaalgunamarneradeaccederaloselementosdirectamente.Severánalositeradoresusadosfrecuente-menteenasociaciónconclasescontenedoras,ylositeradoressonunconceptofun-damentaleneldiseñoyelusodeloscontenedoresdelStandardC++,loscualessondescritosenelVolumen2deestelibro(quesepuedebajardewww.BruceEckel.com.Uniteradorestambiénuntipodepatróndediseño,locualesmateriadeuncapítulodelVolumen2.Enmuchossentidos,uniteradoresun«punteroelegante»,ydehechoseveráquelositeradoresnormalmenteocultanlamayoríadelasoperacionesdelospunteros.Sinembargo,alcontrarioqueunpuntero,eliteradoresdiseñadoparaserseguroporloqueesmuchomenosprobabledehacerelequivalentedeavanzaratravesandoel
500
i
i
“Volumen1”—2012/1/12—13:52—page501—#539i
i
i
i
i
i
16.7.Introducciónalositeradores
naldeunarray(osisehace,seencontrarámásfácilmente).Considereelprimerejemplodeestecapítulo.Aquíestáperoañadiendounitera-dorsencillo:
//:C16:IterIntStack.cpp
//Simpleintegerstackwithiterators
//{L}fibonacci
#include"fibonacci.h"
#include"../require.h"
#include&#xiost;&#xream;
usingnamespacestd;
classIntStack{
enum{ssize=100};
intstack[ssize];
inttop;
public:
IntStack():top(0){}
voidpush(inti){
require(topssize,"Toomanypush()es");
stack[top++]=i;
}
intpop(){
require(top�0,"Toomanypop()s");
returnstack[--top];
}
friendclassIntStackIter;
};
//Aniteratorislikea"smart"pointer:
classIntStackIter{
IntStack&s;
intindex;
public:
IntStackIter(IntStack&is):s(is),index(0){}
intoperator++(){//Prefix
require(indexs.top,
"iteratormovedoutofrange");
returns.stack[++index];
}
intoperator++(int){//Postfix
require(indexs.top,
"iteratormovedoutofrange");
returns.stack[index++];
}
};
intmain(){
IntStackis;
for(inti=0;i20;i++)
is.push(fibonacci(i));
//Traversewithaniterator:
IntStackIterit(is);
for(intj=0;j20;j++)
coutit++endl;
}///:~
501
i
i
“Volumen1”—2012/1/12—13:52—page502—#540i
i
i
i
i
i
Capítulo16.IntroducciónalasPlantillas
ElIntStackIterhasidocreadoparatrabajarsoloconunIntStack.HayqueresaltarqueIntStackIteresunfrienddeIntStack,loquelodaunaccesoatodosloselementosprivadosdeIntStack.Comounpuntero,eltrabajodeIntStackIterconsisteenmoverseatravésdeunIntStackydevolvervalores.Enestesencilloejemplo,elobjetoIntSta-ckItersepuedemoversólohaciaadelante(usandolaformaprejaysujadeloperador++).Sinembargo,nohaylímitesdelaformaenquesepuededeniruniteradorapartedelasrestriccionesimpuestasporelcontenedorconelquetrabaje.Estoestotalmenteaceptable(incluidoloslímitesdelcontenedorqueseencuentrepordebajo)parauniteradorquesemuevadecualquierformaporsucontenedorasociadoyparaquesepuedanmodicarlosvaloresdelcontenedor.Esusualelqueuniteradorseacreadoconunconstructorqueloasocieaunúnicoobjetocontenedor,yqueeseiteradornopuedaserasociadoaotrocontenedordiferentedurantesuciclodevida.(Lositeradoressonnormalementepequeñosybaratos,porloquesepuedecrearotrofácilmente).Coneliterador,sepuedeatravesarloselementosdelapilasinsacarlosdeella,comounpunterosemueveatravésdeloselementosdelarray.Sinembargo,elite-radorconocelaestructurainternadelapilaycomoatravesarloselementos,dandolasensacióndequeseestámoviendoatravésdeelloscomosifuera«incrementarunpuntero»,aunqueseamáscomplejoloquepasapordebajo.Estaeslaclavedeliterador:Abstraeelprocesocomplicadodemoversedeunelementodelcontenedoralsiguienteyloconvierteenalgoparecidoaunpuntero.Lametadecadaiteradordelprogramaesquetenganlamismainterfazparaquecualquiercódigoqueuseuniteradornosepreocupedeaquéestáapuntando-sólosesabequetodoslositera-doressetratandelamismamanera,porloquenoesimportantealoqueapunteeliterador.Deestaformasepuedeescribircódigomásgenérico.Todosloscontene-doresyalgoritmosenlaLibreríaEstándardeC++sebasanenesteprincipiodelositeradores.Paraayudarahacerlascosasmásgenéricas,seríaagradabledecir«todaslascla-sescontenedorastienenunaclaseasociadallamadaiterator»,peroestocausaránormalmenteproblemasdenombres.Lasoluciónconsiteenañadirunaclaseanida-daparacadacontenedor(enestecaso,«iterator»comienzaconunaletraminús-culaparaqueestéconformealestilodelC++estándar).AquíestáelInterIntStack.cppconuniteratoranidado:
//:C16:NestedIterator.cpp
//Nestinganiteratorinsidethecontainer
//{L}fibonacci
#include"fibonacci.h"
#include"../require.h"
#include&#xiost;&#xream;
#include&#xstri;&#xng00;
usingnamespacestd;
classIntStack{
enum{ssize=100};
intstack[ssize];
inttop;
public:
IntStack():top(0){}
voidpush(inti){
require(topssize,"Toomanypush()es");
stack[top++]=i;
502
i
i
“Volumen1”—2012/1/12—13:52—page507—#545i
i
i
i
i
i
16.7.Introducciónalositeradores
cout"end="endendl;
while(start!=end)
coutstart++endl;
ifstreamin("IterStackTemplateTest.cpp");
assure(in,"IterStackTemplateTest.cpp");
stringline;
StackTemplatestring�strings;
while(getline(in,line))
strings.push(line);
StackTemplatestring�::iterator
sb=strings.begin(),se=strings.end();
while(sb!=se)
coutsb++endl;
}///:~
Elprimerusodeliteradorsimplementelorecorredeprincipioan(ymuestraqueellímitenalfuncionacorrectamente).Enelsegundouso,sepuedevercomolositeradorespermitefácilmenteespecicarunrangodeelementos(loscontenedoresylositeradoresdelStandardC++Libraryusanesteconceptoderangoscasiencual-quierparte).Elsobrecargadooperator+=muevelositeradoresstartyendaposicionesqueestánenelmediodelrangodeelementosdeis,yestoselementossonimprimidos.Hayqueresaltar,comoseveenlasalida,queelelementonalnoestáincluidoenelrango,oseaqueunavezllegadoalelementonal(endsentinel)sesabequesehapasadoelnaldelrango-peronohayquedesreferenciarelelementonalosinosepuedeacabardesreferenciandounpunteronulo.(YohepuestounguardianenelStackTemplate::iterator,peroenlaLibreríaEstándardeC++loscontenedoresylositeradoresnotienenesecódigo-pormotivosdeeciencia-porloquehayqueprestaratención).PorúltimoparavericarqueelStackTemplatefuncionaconobjetosclase,seinstanciaunoparastringsyserellenaconlíneasdelcódigofuente,lascualessonposteriormenteimprimidasenpantalla.16.7.1.StackconiteradoresPodemosrepetirelprocesoconlaclasedetamañodinámicoStackquehasidousadacomounejemploalolargodetodoellibro.AquíestálaclaseStackconuniteradoranidadoentodoelmedio:
//:C16:TStack2.h
//TemplatizedStackwithnestediterator
#ifndefTSTACK2_H
#defineTSTACK2_H
templateclassT&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;classStack{
structLink{
T*data;
Link*next;
Link(T*dat,Link*nxt)
:data(dat),next(nxt){}
}*head;
public:
Stack():head(0){}
~Stack();
507
i
i
“Volumen1”—2012/1/12—13:52—page508—#546i
i
i
i
i
i
Capítulo16.IntroducciónalasPlantillas
voidpush(T*dat){
head=newLink(dat,head);
}
T*peek()const{
returnhead?head�-data:0;
}
T*pop();
//Nestediteratorclass:
classiterator;//Declarationrequired
friendclassiterator;//Makeitafriend
classiterator{//Nowdefineit
Stack::Link*p;
public:
iterator(constStackT�&tl):p(tl.head){}
//Copy-constructor:
iterator(constiterator&tl):p(tl.p){}
//Theendsentineliterator:
iterator():p(0){}
//operator++returnsbooleanindicatingend:
booloperator++(){
if(p�-next)
p=p�-next;
elsep=0;//Indicatesendoflist
returnbool(p);
}
booloperator++(int){returnoperator++();}
T*current()const{
if(!p)return0;
returnp�-data;
}
//Pointerdereferenceoperator:
T*operator�-()const{
require(p!=0,
�"PStack::iterator::operator-returns0");
returncurrent();
}
T*operator*()const{returncurrent();}
//boolconversionforconditionaltest:
operatorbool()const{returnbool(p);}
//Comparisontotestforend:
booloperator==(constiterator&)const{
returnp==0;
}
booloperator!=(constiterator&)const{
returnp!=0;
}
};
iteratorbegin()const{
returniterator(*this);
}
iteratorend()const{returniterator();}
};
templateclassT&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;StackT�::~Stack(){
while(head)
deletepop();
}
508
i
i
“Volumen1”—2012/1/12—13:52—page509—#547i
i
i
i
i
i
16.7.Introducciónalositeradores
templateclassT&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;T*StackT�::pop(){
if(head==0)return0;
T*result=head�-data;
Link*oldHead=head;
head=head�-next;
deleteoldHead;
returnresult;
}
#endif//TSTACK2_H///:~
Hayquehacernotarquelaclasehasidocambiadaparasoportarlaposesión,quefuncionaahoradebidoaquelaclaseconoceahoraeltipoexacto(oalmenoseltipobase,quefuncionaasumiendoquesonusadoslosdestructoresvirtuales).Laopciónpordefectoesqueelcontenedordestruyasusobjetosperonosotrossomosresponsablesdelosobjetosalosquesehagapop().Eliteradoressimple,yfísicamentemuypequeño-eltamañodeunúnicopun-tero.Cuandosecreauniterator,seinicializaalacabezadelalistaenlazada,ysólopuedeserincrementadoavanzandoatravésdelalista.Sisequiereempezardesdeelprincipio,hayquecrearunnuevoiterador,ysisequiererecordarunpuntodelalista,hayquecrearunnuevoiteradorapartirdeliteradorexistentequeestáapuntandoaeseelemento(usandoelconstructordecopiadeliterador).Parallamarafuncionesdelobjetoreferenciadoporeliterador,sepuedeusarlafuncióncurrent(),eloperator*,oladesreferenciadepunterooperator--�(unelementocomúnenlositeradores).Laúltimatieneunaimplementaciónquepareceidénticaacurrent()debidoaquedevuelveunpunteroalobjetoactual,peroesdiferenteporqueeloperadordesreferenciadelpunterorealizanivelesextradedesreferenciación(verCapítulo12).Laclaseiteratorsigueelformatoquesevioenelejemploanterior.clas-siteratorestáanidadadentrodelaclasecontenedora,contieneconstructoresparacrearuniteradorqueapuntaaunelementoenelcontenedoryuniterador«marcadordenal»,ylaclasecontenedoratienelosmétodosbegin()yend(-)paraproducirestositeradores.(CuandoaprendamásdelaLibreríaEstándardeC++,veráquelosnombresiterator,begin()yend()queseusanaquítienencorrespondeciaenlasclasescontenedoras.Alnaldeestecapítulo,severáqueestopermitemanejarestasclasescontenedorascomosifueranclasesdelaSTL).Laimplementacióncompletaseencuentraenelarchivocabecera,porloquenoexisteunarchivocppseparado.Aquítenemosunpequeñotestqueusaeliterador.
//:C16:TStack2Test.cpp
#include"TStack2.h"
#include"../require.h"
#include&#xiost;&#xream;
#includestr;êm0;
#include&#xstri;&#xng00;
usingnamespacestd;
intmain(){
ifstreamfile("TStack2Test.cpp");
assure(file,"TStack2Test.cpp");
Stackstring�textlines;
//ReadfileandstorelinesintheStack:
stringline;
509
i
i
“Volumen1”—2012/1/12—13:52—page511—#549i
i
i
i
i
i
16.7.Introducciónalositeradores
iterator(PStash&pStash)
:ps(pStash),index(0){}
//Tocreatetheendsentinel:
iterator(PStash&pStash,bool)
:ps(pStash),index(ps.next){}
//Copy-constructor:
iterator(constiterator&rv)
:ps(rv.ps),index(rv.index){}
iterator&operator=(constiterator&rv){
ps=rv.ps;
index=rv.index;
return*this;
}
iterator&operator++(){
require(++indexps.next,
"PStash::iterator::operator++"
"movesindexoutofbounds");
return*this;
}
iterator&operator++(int){
returnoperator++();
}
iterator&operator--(){
require(--index�=0,
"PStash::iterator::operator--"
"movesindexoutofbounds");
return*this;
}
iterator&operator--(int){
returnoperator--();
}
//Jumpinteratorforwardorbackward:
iterator&operator+=(intamount){
require(index+amountps.next&&
index+amount�=0,
"PStash::iterator::operator+="
"attempttoindexoutofbounds");
index+=amount;
return*this;
}
iterator&operator-=(intamount){
require(index-amountps.next&&
index-amount�=0,
"PStash::iterator::operator-="
"attempttoindexoutofbounds");
index-=amount;
return*this;
}
//Createanewiteratorthat'smovedforward
iteratoroperator+(intamount)const{
iteratorret(*this);
ret+=amount;//op+=doesboundscheck
returnret;
}
T*current()const{
returnps.storage[index];
}
T*operator*()const{returncurrent();}
511
i
i
“Volumen1”—2012/1/12—13:52—page512—#550i
i
i
i
i
i
Capítulo16.IntroducciónalasPlantillas
T*operator�-()const{
require(ps.storage[index]!=0,
�"PStash::iterator::operator-returns0");
returncurrent();
}
//Removethecurrentelement:
T*remove(){
returnps.remove(index);
}
//Comparisontestsforend:
booloperator==(constiterator&rv)const{
returnindex==rv.index;
}
booloperator!=(constiterator&rv)const{
returnindex!=rv.index;
}
};
iteratorbegin(){returniterator(*this);}
iteratorend(){returniterator(*this,true);}
};
//Destructionofcontainedobjects:
templateclassT,intincr&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;
PStashT,incr�::~PStash(){
for(inti=0;inext;i++){
deletestorage[i];//NullpointersOK
storage[i]=0;//Justtobesafe
}
delete[]storage;
}
templateclassT,intincr&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;
intPStashT,incr�::add(T*element){
if(next�=quantity)
inflate();
storage[next++]=element;
return(next-1);//Indexnumber
}
templateclassT,intincr&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;inline
T*PStashT,incr�::operator[](intindex)const{
require(index�=0,
"PStash::operator[]indexnegative");
if(index�=next)
return0;//Toindicatetheend
require(storage[index]!=0,
"PStash::operator[]returnednullpointer");
returnstorage[index];
}
templateclassT,intincr&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;
T*PStashT,incr�::remove(intindex){
//operator[]performsvaliditychecks:
T*v=operator[](index);
//"Remove"thepointer:
storage[index]=0;
returnv;
}
512
i
i
“Volumen1”—2012/1/12—13:52—page513—#551i
i
i
i
i
i
16.7.Introducciónalositeradores
templateclassT,intincr&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;
voidPStashT,incr�::inflate(intincrease){
constinttsz=sizeof(T*);
T**st=newT*[quantity+increase];
memset(st,0,(quantity+increase)*tsz);
memcpy(st,storage,quantity*tsz);
quantity+=increase;
delete[]storage;//Oldstorage
storage=st;//Pointtonewmemory
}
#endif//TPSTASH2_H///:~
LamayoríadeestearchivoesuntraducciónprácticamentedirectadelanteriorPStashyeliteradoranidadodentrodeuntemplate.Estavez,sinembargo,eloperadordevuelvereferenciasaliteradoractual,lacualesunaaproximaciónmástípicayexible.Eldestructorllamaadeleteparatodoslospunterosquecontiene,ycomoeltipoesobtenidodelaplantilla,seejecutaráladestrucciónadecuada.Hayqueestarprecavidoquesielcontenedorcontrolapunterosaltipodelaclasebase,estetipodebetenerundestructorvirtualparaasegurarunlimpiadoadecuadodelosobjetosderivadosquehayanusadounupcastcuandoselosalojóenelcontenedor.ElPStash::iteratormantieneelmodelodeengancharseaunúnicoobje-tocontenedordurantesuciclodevida.Además,elconstructordecopiapermitecrearunnuevoiteradorqueapuntealamismaposicióndeliteradordesdeelqueselecreo,creandodeestamaneraunmarcadordentrodelcontenedor.Lasfuncionesmiembrooperator+=yeloperator-=permitenmoveruniteradorunnúmerodeposiciones,mientrasserespetenloslímitesdelcontenedor.Losoperadoressobre-cargadosdeincrementoydecrementomueveneliteradorunaposición.Elopera-tor+produceunnuevoiteradorquesemueveadelantelacantidadañadida.Comoenelejemploanterior,losoperadoresdedesreferenciadepunterossonusadosparamanejarelelementoalqueeliteradorestáreferenciando,yremove()destruyeelobjetoactualllamandoalremove()delcontenedor.Seusalamismaclasedecódigodeantesparacrearelmarcadornal:unsegundoconstructor,lafunciónmiembrodelcontenedorend(),yeloperator==yope-rator!=paracomparaciones.ElsiguienteejemplocreaycompruebadosdiferentesclasesdeobjetosStash,unoparaunanuevaclasellamadaIntqueanunciasuconstrucciónydestrucciónyotraquegestionaobjetosstringdelalibreríaEstándar.
//:C16:TPStash2Test.cpp
#include"TPStash2.h"
#include"../require.h"
#include&#xiost;&#xream;
#include&#xvect;&#xor00;
#include&#xstri;&#xng00;
usingnamespacestd;
classInt{
inti;
public:
Int(intii=0):i(ii){
513
i
i
“Volumen1”—2012/1/12—13:52—page514—#552i
i
i
i
i
i
Capítulo16.IntroducciónalasPlantillas
cout&#x-600;""i'';
}
~Int(){cout"~"i'';}
operatorint()const{returni;}
friendostream&
operator(ostream&os,constInt&x){
returnos"Int:"x.i;
}
friendostream&
operator(ostream&os,constInt*x){
returnos"Int:"x&#x-600;-i;
}
};
intmain(){
{//Toforcedestructorcall
PStashInt�ints;
for(inti=0;i30;i++)
ints.add(newInt(i));
coutendl;
PStashInt�::iteratorit=ints.begin();
it+=5;
PStashInt�::iteratorit2=it+10;
for(;it!=it2;it++)
deleteit.remove();//Defaultremoval
coutendl;
for(it=ints.begin();it!=ints.end();it++)
if(*it)//Remove()causes"holes"
cout*itendl;
}//"ints"destructorcalledhere
cout"\n-------------------\n";
ifstreamin("TPStash2Test.cpp");
assure(in,"TPStash2Test.cpp");
//InstantiateforString:
PStashstring�strings;
stringline;
while(getline(in,line))
strings.add(newstring(line));
PStashstring�::iteratorsit=strings.begin();
for(;sit!=strings.end();sit++)
cout**sitendl;
sit=strings.begin();
intn=26;
sit+=n;
for(;sit!=strings.end();sit++)
coutn++":"**sitendl;
}///:~
PorconvenienciaInttieneasociadounostreamoperatorparaInt&yInt*.Elprimerbloquedecódigoenmain()estárodeadodellavesparaforzarlades-trucciónde&#xInt0;PStashqueproduceunlimpiadoautomáticoporestedestructor.UnoscuantoselementossonsacadosyborradosamanoparamostrarquePStashlimpiaelresto.
514
i
i
“Volumen1”—2012/1/12—13:52—page515—#553i
i
i
i
i
i
16.8.Porquéusariteradores
ParaambasinstanciasdePStash,secreauniteradoryseusaparamoverseatravésdelcontenedor.Notelaeleganciageneradaporelusodeestosconstructores;nohayquepreocuparseporlosdetallesdeimplementacióndeusarunarray.Seledicealcontenedoryaliteradorquéhacerynocómohacerlo.Estoproduceunasoluciónmássencilladeconceptualizar,construirymodicar.16.8.PorquéusariteradoresHastaahorasehanvistolosmecanismosdelositeradores,peroentenderelporquésontanimportantesnecesitaunejemplomáscomplejo.Esnormalverelpolimorsmo,lacreacióndinámicadeobjetos,yloscontenedo-resenunprogramaorientadoaobjetosreal.Loscontendoresylacreacióndinámicadeobjetosresuelvenelproblemadenosabercuantosoquetipodeobjetossenece-sitarán.Ysielcontenedorestáconguradoparamanejarpunterosalaclasebase,cadavezquesepongaunpunteroaunaclasederivadahayunupcast(conlosbe-neciosqueconllevadeclaridaddecódigoyextensibilidad).ComocódigodelnaldelVolumen1,esteejemploreunevariosaspectosdetodoloquesehaaprendido-siescapazdeseguiresteejemplo,entoncesestápreparadoparaelVolumen2.Supongaqueestacreandounprogramaquepermitealusuarioeditaryproducirdiferentesclasesdedibujos.CadadibujoesunobjetoquecontieneunacoleccióndeobjetosShape:
//:C16:Shape.h
#ifndefSHAPE_H
#defineSHAPE_H
#include&#xiost;&#xream;
#include&#xstri;&#xng00;
classShape{
public:
virtualvoiddraw()=0;
virtualvoiderase()=0;
virtual~Shape(){}
};
classCircle:publicShape{
public:
Circle(){}
~Circle(){std::cout"Circle::~Circle\n";}
voiddraw(){std::cout"Circle::draw\n";}
voiderase(){std::cout"Circle::erase\n";}
};
classSquare:publicShape{
public:
Square(){}
~Square(){std::cout"Square::~Square\n";}
voiddraw(){std::cout"Square::draw\n";}
voiderase(){std::cout"Square::erase\n";}
};
classLine:publicShape{
public:
Line(){}
515
i
i
“Volumen1”—2012/1/12—13:52—page516—#554i
i
i
i
i
i
Capítulo16.IntroducciónalasPlantillas
~Line(){std::cout"Line::~Line\n";}
voiddraw(){std::cout"Line::draw\n";}
voiderase(){std::cout"Line::erase\n";}
};
#endif//SHAPE_H///:~
Seusalaestructuraclásicadelasfuncionesvirtualesenlaclasebasequesonsobreescritasenlaclasederivada.HayqueresaltarquelaclaseShapeincluyeundestructorvirtual,algoquesedeberíaañadirautomáticamenteacualquierclaseconfuncionesvirtuales.SiuncontenedormanejapunterosoreferenciasaobjetosS-hape,entoncescuandolosdestructoresvirtualesseanllamadosparaestosobjetostodoserácorrectamentelimpiado.Cadatipodiferentededibujoenelsiguienteejemplohaceusodeunaplantilladeclasecontenedoradiferente:elPStashyelStackquehansidodenidoenestecapítulo,ylaclasevectordelaLibreríaEstándardeC++.El«uso»deloscontenedoresesextremadamentesimple,yengenerallaherencianoeslamejoraproximación(composiciónpuedetenermássentido),peroenestecasolaherenciaesunaaproximaciónmássimple.
//:C16:Drawing.cpp
#include&#xvect;&#xor00;//UsesStandardvectortoo!
#include"TPStash2.h"
#include"TStack2.h"
#include"Shape.h"
usingnamespacestd;
//ADrawingisprimarilyacontainerofShapes:
classDrawing:publicPStashShape�{
public:
~Drawing(){cout"~Drawing"endl;}
};
//APlanisadifferentcontainerofShapes:
classPlan:publicStackShape�{
public:
~Plan(){cout"~Plan"endl;}
};
//ASchematicisadifferentcontainerofShapes:
classSchematic:publicvectorShape*�{
public:
~Schematic(){cout"~Schematic"endl;}
};
//Afunctiontemplate:
templateclassIter&#x]TJ/;ྖ ;.96;d T; 5.;8 0;&#x Td ;&#x[000;
voiddrawAll(Iterstart,Iterend){
while(start!=end){
(*start�)-draw();
start++;
}
}
intmain(){
//Eachtypeofcontainerhas
516
i
i
“Volumen1”—2012/1/12—13:52—page519—#557i
i
i
i
i
i
16.10.Ejercicios
16.10.EjerciciosLassolucionesalosejerciciossepuedenencontrareneldocumentoelectróni-cotitulado«TheThinkinginC++AnnotatedSolutionGuide»,disponibleporpocodineroenwww.BruceEckel.com.1.ImplementelajerarquíadeherenciadeldiagramadeOShapedeestecapítu-lo.2.ModiqueelresultadodelEjercicio1delcapítulo15parausarlaStackyeliteratorenTStack2.henvezdeunarraydepunterosaShape.AñadadestructoresalajerarquíadeclasesparaquesepuedaverquelosobjetosSh-apehansidodestruidoscuandolaStacksesaledelámbito.3.ModiqueTPStash.hparaqueelvalordeincrementousadoporinflat-e()puedasercambiadodurantelavidadeunobjetocontenedorparticular.4.ModiqueTPStash.hparaqueelvalordeincrementousadoporinflate-()automáticamentecambiedetamañoparaquereduzcaelnúmerodevecesquedebeserllamado.Porejemplo,cadavezquesellamapodríadoblarelvalordeincrementoparasuusoenlasiguientellamada.Demuestrelafuncionalidadmostrandocadavezquesellamaainflate(),yescribacódigodepruebaenmain().5.Conviertaenplantillalafuncióndefibonacci()conlostiposquepuedeproducir(puedegenerarlong,oat,etc.envezdesóloint).6.UsarelvectordelaSTLcomoimplementaciónsubyacente,paracrearunaplatillaSetqueaceptesolounodecadatipodeobjetoquesealojeenél.Creeuniteradoranidadoquesoporteelconceptode"marcadornal"deestecapítulo.EscribacódigodepruebaparaelSetenelmain(),yentoncessus-tituyaloporlaplantillasetdelaSTLparacomprobarqueelcomportamientoescorrecto.7.ModiqueAutoCounter.hparaquepuedaserusadocomounobjetomiem-brodentrodecualquierclasecuyacreaciónydestrucciónquieracomprobar.Añadaunmiembrostringparaquecontengaelnombredelaclase.Comprue-beestaherramientadentrounaclasesuya.8.CreeunaversióndeOwnerStack.hqueuseunvectordelaLibreríaEs-tándardeC++comosuimplementaciónsubyacente.Seránecesarioconoceralgunasdelasfuncionesmiembrodevectorparapoderhacerlo(sólohayquemirarenelarchivocabecera&#xvect;&#xor00;)9.ModiqueValueStack.hparaquepuedaexpandirsedinámicamentese-gúnseintroduzcanmásobjetosysequedesinespacio.CambieValueStackTest.cppparacomprobarsunuevafuncionalidad.10.Repitaelejercicio9perouseelvectordelaSTLcomolaimplementacióninternadeValueStack.Notelosencilloquees.11.ModiqueValueStackTest.cppparaqueuseunvectordelaSTLenvezdeunStackenelmain().Désecuentadelcomportamientoentiempodeejecución:¿Segeneraungrupodeobjetospordefectocuandosecreaelvector?
519
i
i
“Volumen1”—2012/1/12—13:52—page520—#558i
i
i
i
i
i
Capítulo16.IntroducciónalasPlantillas
12.ModiqueTStack2.hparaqueuseunvectordelaSTL.Aseguresedequenocambialainterfaz,paraqueTStack2Test.cppfuncionesincam-biarse.13.RepitaelEjercicio12usandounastackdelaLibreríaEstándardeC++envezdeunvector.14.ModiqueTPStash2.hparaqueuseunvectordelaSTLcomosuimple-mentacióninterna.Aseguresequenocambialainterfaz,porloqueTPStash2Test.cppfuncionasinmodicarse.15.EnIterIntStack.cpp,modiqueIntStackIterparadarleunconstruc-torde«marcadornal»,yañadaeloperator==yeloperator!=.Enelm-ain(),useuniteradorparamoverseatravésdeloselementosdelcontenedorhastaqueseencuentreelmarcador.16.UseTStack2.h,TPSTash2.h,yShape.h,instancieloscontenedoresPSt-ashyStackparaquecontengaShape*,rellenecadaunoconpunterosaShape,entoncesuseiteradoresparamoverseatravésdecadacontenedoryllameadraw()paracadaobjeto.17.CreeunaplantillaenlaclaseIntparaquepuedaalojarcualquiertipodeobjetos(Siéntaselibredecambiarelnombredelaclaseaalgomásapropiado).18.CreeunaplantilladelaclaseIntArrayenIostreamOperatorOverloading.cppdelcapítulo12,introduzcaenplantillaambostiposdeobjetosqueestáncontenidosyeltamañodelarrayinterno19.ConviertaObjContainerenNestedSmartPointer.cppdelCapítulo12enunaplantilla.Compruebelocondosclasesdiferentes.20.ModiqueC15:OStack.hyC15:OStackTest.cppconsiguiendoquec-lassStackpuedatenermúltipleherenciaautomáticamentedelaclasecon-tenidaydeObject.LaStackcontenidadebeaceptaryproducirsólopun-terosdeltipocontenido.21.Repitaelejercicio20usandovectorenvezdeStack.22.HeredeunaclaseStringVectorde&#xvoid;vectoryredenalasfuncio-nesmiembropush_back()yeloperator[]paraqueaceptenyproduz-canúnicamentestring*(yrealizenelmoldeadoadecuado).Ahoracreeeunaplantillaquehagaautomáticamentelomismoaunaclasecontenedoraparapunterosdecualquiertipo.Estatécnicaesamenudousadaparareducirelcódigoproducidopormuchasinstanciacionesdetemplates.23.EnTPStash2.h,añadaycompruebeunoperator-paraPStash::iter-ator,siguiendolalógicadeoperator+.24.EnDrawing.cpp,añadaycompruebeunaplantilladefunciónquellameafuncionesmiembroerase().25.(Avanzado)ModiquelaclaseStackenTStack2.hparapermitirunagra-nularidaddelapropiedad:Añadaunabanderaparacadaenlaceindicandosielenlaceposeeelobjetoalqueapunta,ydesoporteaestainformaciónlafunciónpush()yeneldestructor.Añadafuncionesmiembroparaleerycambiarlapropiedaddecadaenlace.
520
i
i
“Volumen1”—2012/1/12—13:52—page521—#559i
i
i
i
i
i
16.10.Ejercicios
26.(Avanzado)ModiquePointerToMemberOperator.cppdelCapítulo12paraquelaFunctionObjectyel�operator-*seanconvertidosenplan-tillasparaquefuncionenconcualquiertipoderetorno(para�operator-*,tendráqueusarplantillasmiembrodescritasenelVolumen2).Añadasoporteycompruebeparacero,unoydosargumentosenlasfuncionesmiembroDog.
521
i
i
“Volumen1”—2012/1/12—13:52—page524—#562i
i
i
i
i
i
ApéndiceA.Estilodecodicación
errorescuandosecompileconunaimplementaciónconformealEstándarC++(notodosloscompiladoressoportantodaslascaracterísticasdellenguaje).Lassenten-ciasquedeberíancausarerroresdecompilaciónestáncomentadascon//!demo-doquesepuedendescubriryprobarfácilmentedemodoautomático.Loserro-resdescubiertosporelautorapareceránprimeroenlaversiónelectrónicadellibro(www.BruceEckel.com)ydespuésenlasactualizacionesdellibro.Unodelosestándaresdeestelibroesquetodoslosprogramascompilarányenla-zaránsinerrores(aunqueavecescausaránadvertencias).Algunosdelosprogramas,quedemuestransólounejemplodecodicaciónynorepresentanprogramascom-pletos,tendránfuncionesmain()vacías,comoésta:
intmain(){}
Estopermitequesepuedaenlazarelprogramasinerrores.Elestándarparamain()esretornarunint,peroC++Estándarestipulaquesinohayunasentenciareturnenmain(),elcompiladorgeneraráautomáticamentecódigoparareturn0.Estaopción(noponerunreturnenmain())seusaenellibro(algunoscompiladoresproducenadvertenciassobreello,peroesporquenosonconformesconC++Estándar).A.2.NombresdecheroEnC,estradiciónnombraraloscherosdecabecera(quecontienenlasdecla-raciones)conunaextensión.hyaloscherosdeimplementación(quegeneranalojamientoenmemoriaycódigo)conunaextensión.c.C++supusounaevolu-ción.PrimerofuedesarrolladoenUnix,dondeelsistemaoperativodistingueentremayúsculasyminúsculasparanombresdecheros.Losnombresoriginalesparaloscherossimplementesepusieronenmayúscula:.Hy.C.Esto,porsupuesto,nofuncionabaensistemasoperativosquenodistinguenentremayúsculasyminúscu-lascomoDOS.LosvendedoresdeC++paraDOSusabanextensioneshxxycxx,ohppycpp.Después,alguiensediocuentaquelaúnicarazónporlaquesepuedenecesitarunextensióndiferenteesqueelcompiladornopuededeterminarsidebecompilarlocomoCoC++.Comoelcompiladornuncacompilacherosdecabece-radirectamente,sóloelcherodeimplementaciónnecesitaunadistinción.Ahora,enprácticamentetodoslossistemas,lacostumbreesusarcppparaloscherosdeimplementacióny.hparaloscherosdecabecera.FíjesequecuandoseincluyeuncherodecabeceraC++,seusalaopcióndenoponerextensiónalnombredelche-ro,porejemplo:#include&#xiost;&#xream;A.3.MarcascomentadasdeinicioynUntemamuyimportanteenestelibroesquetodoelcódigoquepuedeverenellibrohasidosidovericado(conalmenosuncompilador).Estoseconsigueex-trayendoautomáticamenteloslistadosdellibro.Parafacilitarestatarea,todosloslistadosdecódigosusceptiblesdesercompilados(alcontrarioquelosfragmentos,quehaypocos)tienenunasmarcascomentadasalprincipioyalnal.EstasmarcaslasusalaherramientadeextraccióndecódigoExtractCode.cppdelVolumen2deestelibro(yquesepuedeencontrarenelsitiowebwww.BruceEckel.com)paraextraercadalistadodecódigoapartirdelaversiónentextoplanoASCIIdeestelibro.
524
i
i
“Volumen1”—2012/1/12—13:52—page525—#563i
i
i
i
i
i
A.4.Paréntesis,llaveseindentación
LamarcadendelistadosimplementeleindicaaExtractCode.cppqueeseeselnaldellistado,perolamarcadecomienzoincluyeinformaciónsobreelsub-directorioalquecorrespondeelchero(normalmenteorganizadoporcapítulos,asíquesicorrespondealCapítulo8deberíatenerunaetiquetacomoC08),seguidodedospuntosyelnombredelchero.ComoExtractCode.cpptambiéncreaunmakefileparacadasubdirectorio,lainformacióndecómoconstruirelprogramaylalíneadecomandoquesedebeusarparaprobarlotambiénseincorporaaloslistados.Siunprogramaesautónomo(nonecesitaserenlazadoconnadamás)notieneinformaciónextra.Estotambiénesciertoparaloscherosdecabecera.Sinembargo,sinocontieneunmain()ynecesitaenlazarseconalgúnotro,apareceun{O}despuésdelnombredelchero.Sieselistadoeselprogramaprincipalperonecesitaserenlazadoconotroscomponentes,hayunalíneaadicionalquecomienzacon//{L}ycontinúaconelnombredetodosloscherosconlosquedebeenlazarse(sinextensiones,dadoquepuedevariarentreplataformas).Puedeencontrarejemplosalolargodetodoellibro.Cuandouncherodebeextraersesinquelasmarcasdeinicioyndebanincluir-seenelcheroextraído(porejemplo,siesuncherocondatosparaunaprueba)lamarcadeiniciovaseguidadeun'!'.A.4.Paréntesis,llaveseindentaciónHabránotadoqueelestilodeestelibroesdiferentealamayoríadelosestilosCtradicionales.Porsupuesto,cualquierapuedepensarquesupropioestiloesmásracional.Sinembargo,elestiloqueseempleaaquítieneunalógicamássimple,quesepresentarámezcladaconlasdeotrosestilosdesarrollados.Elestiloestámotivadoporunacosa:lapresentación,tantoimpresacomoenunseminario.Quizásusnecesidadesseandiferentesporquenorealizamuchaspresen-taciones.Sinembargo,elcódigorealseleemuchasmásvecesdelasqueseescribe,yporesodeberíaserfácildeleer.Misdoscriteriosmásimportantessonla«esca-neabilidad»(quesereerealafacilidadconlaqueellectorpuedecomprenderelsignicadodeunaúnicalínea)yelnúmerodelíneasquecabenenunapágina.Losegundopuedesonargracioso,perocuandounodaunacharla,distraemuchoalaaudienciaqueelponentetengaqueavanzaryretrocederdiapositivas,ysólounaspocaslíneasdemáspuedeprovocaresteefecto.Todoelmundopareceestardeacuerdoenqueelcódigoqueseponedentrodellavesdebeestarindentado.Enloquelagentenoestádeacuerdo-yeselsitiodondemásinconsistenciatienenlosestilos-es:¿Dóndedebeirlallavedeapertura?Estaúnicacuestión,creoyo,eslaquecausalamayoríadelasvariacionesenlosestilosdecodicación(SiquiereverunaenumeracióndeestilosdecodicaciónveaC++ProgrammingGuidelines,de[FIXME:autores]TomPlumyDanSaks,PlumHall1991),Intentaréconvencerledequemuchosdelosestilosdecodicaciónactualesprovie-nendelarestriccionespreviasalCEstándar(antesdelosprototiposdefunción)demaneraquenosonapropiadasactualmente.Loprimero,mirespuestaaesapreguntaclave:lallavedeaperturadeberíairsiempreenlamismalíneaqueel«precursor»(esdecir«cualquiercosadelaqueseacuerpo:unaclase,función,denicióndeobjeto,sentenciaif,etc».Esunareglaúnicayconsistentequeaplicoatodoelcódigoqueescribo,yhacequeelformateodecódigoseamuchomássencillo.Hacemássencillala«escaneabilidad»-cuandoseleeestalínea:
525
i
i
“Volumen1”—2012/1/12—13:52—page528—#566i
i
i
i
i
i
ApéndiceA.Estilodecodicación
ciasemantiene,comoen:
for(inti=0;i100;i++){
coutiendl;
coutx*iendl;
}
Elestiloesfácildeenseñaryrecordar-useunareglasimpleyconsistentepa-ratodosusformatos,nounaparaclases,dosparafunciones(funcionesinlinedeunalíneavs.multi-línea),yposiblementeotrasparabucles,sentenciasif,etc.Laconsistenciaporsisolamerecesertenidaencuenta.Sobretodo,C++esunlenguajemásnuevoqueC,yaunquedebemoshacermuchasconcesionesaC,nodeberíamosacarreardemasiadosFIXME:artifactsquenoscausenproblemasenelfuturo.Proble-maspequeñosmultiplicadospormuchaslíneasdecódigoseconviertenengrandesproblemas.Paraunexamenminuciosodelasunto,aunquetratadeC,veaCStyle:StandardsandGuidelines,deDavidStraker(Prentice-Hall1992).Laotrarestricciónbajolaquedebotrabajareslalongituddelalínea,dadoqueellibrotieneunalimitaciónde50caracteres.¿Quéocurresialgoesdemasiadolargoparacaberenunalínea?Bien,otravezmeesfuerzoentenerunapolíticaconsisten-teparalaslíneaspartidas,demodoqueseanfácilmentevisibles.Siemprequeseanpartedeunaúnicadenición,listadeargumentos,etc.,laslíneasdecontinuacióndeberíanindentarseunnivelrespectoalcomienzodeladenición,listadeargu-mentos,etc.A.5.NombresparaidenticadoresAquellosqueconozcanJavanotaránqueyomehecambiadoalestiloestándardeJavaparatodoslosidenticadores.Sinembargo,nopuedosercompletamenteconsistenteporquelosidenticadoresenCEstándaryenlibreríasC++nosigueneseestilo.Elestiloesbastantesencillo.Laprimeraletradeunidenticadorsóloseponeenmayúsculasielidenticadoresunaclase.Siesunafunciónovariable,laprimeraletrasiemprevaenminúscula.Elrestodelidenticadorconsisteenunaomáspa-labras,todasjuntasperosedistinguenporquelaprimeraletradecadapalabraesmayúscula.Demodoqueunaclaseesalgoparecidoaesto:
classFrenchVanilla:publicIceCream{
yunobjetoesalgocomoesto:
FrenchVanillamyIceCreamCone(3);
yunafunción:
voideatIceCreamCone();
(tantoparaunmétodocomoparaunfunciónnormal).Laúnicaexcepciónsonlasconstantesentiempodecompilación(consty#de-fine),enlasquetodaslasletrasdelidenticadorsonmayúsculas.
528
i
i
“Volumen1”—2012/1/12—13:52—page529—#567i
i
i
i
i
i
A.6.Ordendelos#includes
Elvalordelestiloesqueelusodemayúsculastienesignicado-viendolapri-meraletrasepuedesabersiesunaclaseounobjeto/método.Estoesespecialmenteútilcuandoseinvocanmiembrosestáticos.A.6.Ordendelos#includesLoscherosdecabeceraseincluyenenorden«delmásespecícoalmásgene-ral».Esdecir,cualquiercherodecabeceraeneldirectoriolocalseincluyeprimero,despuéslas«herramientas»propias,comorequire.h,luegocabecerasdelibreríasdeterceros,despuéscabecerasdelalibreríaestándarC++,ynalmentecabecerasdelalibreríaC.LajusticaciónparaestovienedeJohnLakosenLarge-ScaleC++SoftwareDesign(Addison-Wesley,1996):FIXMELoserroresdeusolatentessepuedeevitarasegurandoqueel-chero.hdeuncomponenteescoherenteensimismo-sindeclaracionesodenicionesexternas.Incluirelchero.hcomoprimeralíneadelchero.caseguraquenofaltaningunapiezadeinformacióndelainterfazfísicadelcomponenteenelchero.h(o,silahay,apareceráencuantointentecompilarelchero.c.Sielordendeinclusiónfuese«desdeelmásespecícoalmásgeneral»,enton-cesesmásprobablequesisucherodecabeceranoescoherenteporsimismo,lodescubriráantesyprevendrádisgustosenelfuturo.A.7.GuardasdeinclusiónencherosdecabeceraLosguardasdeinclusiónseusansiempreenloscherosdecabeceraparapre-venirinclusionesmúltiplesdurantelacompilacióndeunúnicochero.cpp.Losguardasdeinclusiónseimplementanusado#defineycomprobandosielnombrenohasidodenidopreviamente.Elnombrequeseusaparaelguardaestábasa-doenelnombredelcherodecabecera,perocontodaslasletrasenmayúsculayreemplazandoelpuntoporunguiónbajo.Porejemplo:
//IncludeGuard.h
#ifndefINCLUDEGUARD_H
#defineINCLUDEGUARD_H
//Bodyofheaderfilehere...
#endif//INCLUDEGUARD_H
Elidenticadordelaúltimalíneaseincluyeúnicamenteporclaridad.Algunospreprocesadoresignorancualquiercarácterqueaparezcadespuésdeun#endif,peronoeselcomportamientoestándaryporesoelidenticadoraparececomentado.A.8.UsodelosespaciosdenombresEnloscherosdecabecera,sedebeevitardeformaescrupulosacualquiercon-taminacióndelespaciodenombres.Esdecir,sisecambiaelespaciodenombresfueradeunafunciónoclase,provocaráqueelcambioocurratambiénencualquiercheroqueincluyaesecherodecabecera,loqueresultaentodotipodeproblemas.
529
i
i
“Volumen1”—2012/1/12—13:52—page531—#569i
i
i
i
i
i
B:DirectricesdeProgramaciónEsteapéndiceesunacoleccióndesugerenciasparaprogramaciónconC++.Sehanreunidoalolargodemiexperienciaencomodo-centeyprogramadorytambiéndelasaportacionesdeamigosincluyendoaDanSaks(co-autorjuntoaTomPlumdeC++ProgrammingGuidelines,PlumHall,1991),ScottMeyers(autordeEffectiveC++,2ªedición,Addison-Wesley,1998),andRobMurray(autordeC++Strategies&Tactics,Addison-Wesley,1993).También,muchosdelosconsejosestánresumidosapartirdelcontenidodeThinkinginC++.1.Primerohagaquefuncione,despuéshágalorápido.Estoesciertoinclusosiseestásegurodequeunatrozodecódigoesrealmenteimportanteysesabequeseráuncuellodebotellaeselsistema.Nolohaga.Primero,consigaqueelsis-tematengaundiseñolomássimpleposible.Entonces,sinoessucientementerápido,optimícelo.Casisiempredescubriráque«su»cuellodebotellanoeselproblema.Guardesutiempoparaloverdaderamenteimportante.2.Laeleganciasiemprevalelapena.Noesunpasatiempofrívolo.Nosóloper-mitequeunprogramaseamásfácildeconstruirydepurar,tambiénesmásfácildecomprenderymantener,yahíesdonderadicasuvaloreconómico.Es-tacuestiónpuederequerirdealgunaexperienciaparacreerselo,porquepuedeparecerquemientrasseestáhaciendountrozodecódigoelegante,noseesproductivo.Laproductividadaparececuandoelcódigoseintegrasinproble-masenelsistema,einclusocuandosemodicaelcódigooelsistema.3.Recuerdeelprincipio«divideyvencerás».Sielproblemaalqueseenfrentaesdesmasiadoconfuso,intenteimaginarlaoperaciónbásicadelprogramasepuedehacer,debidoalaexistenciadeuna«pieza»mágicaquehaceeltrabajodifícil.Esta«pieza»esunobjeto-escribaelcódigoqueusaelobjeto,despuésimplementeeseobjetoencapsulandolaspartesdifícilesenotrosobjetos,etc.4.NoreescribaautomáticamentetodosucódigoCaC++amenosquenecesiteuncambiarsignicativamentesufuncionalidad(esdecir,noloarreglesinoestároto).RecompilarCenC++esunpositivoporquepuederevelarerroresocultos.Simembargo,tomarcódigoCquefuncionabienyreescribirloenC++noeslamejorformadeinvertireltiempo,amenosquelaversiónC++leofrezcamásoportunidaddereutilizarlocomounaclase.5.SitieneungrantrozodecódigoCquenecesitecambios,primeroaislelaspar-tesdelcódigoquenosemodicará,posiblementeenvolviendoesasfuncionesenuna«claseAPI»comométodosestáticos.Despuéspongaaténciónalcódigoquevaacambiar,recolocandolodentrodeclasesparafacilitarlasmodicacio-nesenelprocesodemantenimiento.
531
i
i
“Volumen1”—2012/1/12—13:52—page533—#571i
i
i
i
i
i
13.Recuerdeunareglafundamentaldelaingenieríadelsoftware1:Todoslospro-blemasdeldiseñodesoftwaresepuedesimplicarintroduciendounanivelmásdeindirecciónconceptual.Estaúnicaideaeslapasedelaabstracción,laprincipalcualidaddelaprogramaciónorientadaaobjetos.14.Hagaclasestanatómicascomoseaposible:Esdecir,déacadaclaseunpro-pósitoúnicoyclaro.Sieldiseñodesuclaseodesusistemacrecehastaserdemasiadocomplicado,dividalasclasescomplejasenotrasmássimples.Elindicadormásobvioestamañototal:siunaclaseesgrande,FIXME:chancesareit'sdoingdemasiadoydeberíadividirse.15.Vigilelasdenicionesdemétodoslargos.Unafuncióndemasiadolargaycom-plicadaesdicilycarademantener,yesproblemaqueestéintentadohacerdemasiadotrabajoporellamisma.Siveunafunciónasí,indicaque,almenos,deberíadividirseenmúltiplesfunciones.Tambiénpuedesugerirlacreacióndeunanuevaclase.16.Vigilelaslistasdeargumentoslargas.Lasllamadasafunciónsevuelvendifí-cilesdeescribir,leerymantener.Ensulugar,intentemoverelmétodoaunaclasedondeseamásapropiado,y/opaseleobjetoscomoargumentos.17.Noserepita.Siuntrozodecódigoserepiteenmuchosmétodosdelasclasesderivadas,pongaelcódigoenunmétododelaclasebaseeinvóquelodesdelasclasesderivadas.Nosóloahorrarácódigo,tambiénfacilitalapropagacióndeloscambios.Puedeusarunafuncióninlinesinecesitaeciencia.Aveceseldescubrimientodeestecódigocomúnañadiráfuncionalidadvaliosaasuinterface.18.Vigilelassentenciasswitchocadenasdeif-else.Sonindicadorestípicosdecódigodependientedeltipo,loquesignicaqueestádecidiendoquécódigoejecutarbasándoseenalgunainformacióndetipo(eltipoexactopuedenoserobvioenprincipio).Normalementepuedereemplazarestetipodecódigoporherenciaypolimorsmo;unallamadaaunafunciónpolimórcaefectuarálacomprobacióndetipoporusted,yharáqueelcódigoseamásableysencillodeextender.19.Desdeelpuntodevistadeldiseño,busqueydistingacosasquecambianycosasquenocambian.Esdecir,busqueelementosenunsistemaquepodríancambiarsinforzarunrediseño,despuésencapsuleesoselementosenclases.PuedeaprendermuchomássobreesteconceptoenelcapítuloDessignPatternsdelVolumen2deestelibro,disponibleenwww.BruceEckel.com220.TengacuidadoconlasFIXMEdiscrepancia.Dosobjetossemánticamentedife-rentespuedeteneraccionesidénticas,oresponsabilidades,yhayunatenden-cianaturalaintentarhacerqueunaseasubclasedelaotrasólocomobeneciodelaherencia.Esesellamadiscrepancia,peronohayunajusticaciónrealpa-raforzarunarelaciónsuperclase/subclasedondenoexiste.Unsoluciónmejorescrearunaclasebasegeneralqueproduceunaherenciaparalasdoscomoclasesderivadas-esorequireunpocomásdeespacio,perosiguebenecian-dosedelaherenciayprobablementeharáunimportantedescubrimientosobreeldiseño.
1QuemeexplicóAndrewKoening.2(N.deT.)EstáprevistalatraduccióndelVolumen2porpartedelmismoequipoquehatraducidoestevolumen.VisiteFIXME
533
i
i
“Volumen1”—2012/1/12—13:52—page534—#572i
i
i
i
i
i
ApéndiceB.DirectricesdeProgramación
21.TengacuidadoconlaFIXME:limitacióndelaherencia.Losdiseñosmáslímpiosañadennuevascapacidadesalasheredadas.Undiseñosospechosoeliminacapacidadesdurantelaherenciasinañadirotrasnuevas.Perolasreglasestánhechaspararomperse,ysiestátrabajandoconunalibreríaantigua,puedesermásecienterestringirunaclaseexistenteensussubclasesquerestructurarlajerarquíademodoquelanuevaclaseencajedondedebería,sobrelaclaseantigua.22.Noextiendafuncionalidadfundamentalpormediodesubclases.Siunelemen-todelainterfazesesecialparaunaclasedeberíaestárenlaclasebase,noañadidoenunaclasederivada.Siestáañadiendométodosporherencia,quizádeberíarepensareldiseño.23.Menosesmás.Empiececonunainterfazmínimaaunaclase,tanpequeñaysimplecomonecesitepararesolverelproblemaqueestátratando,peronoin-tenteanticipartodaslasformasenlasquesepodríausarlaclase.Cuandouselaclase,descubriráformasdeusarlaydeberáexpandirlainterface.Sinembar-go,unavezquequelaclaseestésiendousada,nopodráreducirlainterfazsincausarproblemasalcódigocliente.Sinecesitaañadirmásfunciones,estábien;esonomolesta,únicamenteobligaarecompilar.Peroinclusosilosnuevosmé-todosreemplazanlasfuncionalidaddelosantiguos,dejetranquilalainterfazexistente(puedecombinarlafuncionalidaddelaimplementaciónsubyacentesilodesea.Sinecesitaexpandirlainterfazdeunmétodoexistenteañadiendomásargumentos,dejelosargumentosexistentesenelordenactual,ypongava-lorespordefectoatodoslosargumentosnuevos;deestemodonoperturbaráningunadelasllamadasantiguasaesafunción.24.Leasusclasesenvozaltaparaestarseguroquequesuenanlógicas,reriendo-sealasrelaciónentreunaclasebaseyunaclasederivadacom«es-un»yalosobjetosmiembrocomo«tiene-un».25.Cuandotengaquedecidirentreherenciaycomposición,preguntesinecesi-tahacerupcastaltipobase.Silarespuestaesno,elijacomposición(objetosmiembro)enlugardeherencia.Estopuedeeliminarlanecesidaddeherenciamúltiple.Sihereda,losusuariospensaránFIXME:theyaresupposedtoupcast.26.Aveces,senecesitaheredarparaaccederamiembrosprotegidosdeunaclasebase.Estopuedeconduciraunanecesidaddeherenciamúltiple.Sinonecesitahacerupcast,primeroderiveunanuevaclaseparaefectuarelaccesoprotegido.Entonceshagaquelanuevaclaseseaunobjetomiembrodentrodecualquierclasequenecesiteusarla,ellugardeheredar.27.Típicamente,unaclasebaseseusaráprincipalmenteparacrearunainterfacealasclasesqueheredendeella.Deesemodo,cuandocreeunaclasebase,hagaquepordefectolosmétodosseanvirtualespuros.Eldestructorpuedesertambiénvirtualpuro(paraforzarquelosderivadastenganqueanularloexplicitamente),perorecuerdeponeraldestructoruncuerpo,porquetodosdestructoresdelajerarquíaseejecutansiempre.28.Cuandoponeunmétodovirtualpuroenunaclase,hagaquetodoslosmé-todosdelaclaseseantambiénviruales,ypongaunconstructorvirtual.Estapropuestaevitasorpresasenelcomportamientodelainterfaz.Empieceaqui-tarlapalabravirtualsólocuandoestéintentandooptimizarysuperladorhayaapuntadoenestadirección.
534
i
i
“Volumen1”—2012/1/12—13:52—page535—#573i
i
i
i
i
i
29.Useatributosparavariacionesenlosvaloresymétodosvirtualesparavaria-cionesenelcomportamiento.Esdecir,siencuentraunaclasequeusaatribu-tosestáticosconmétodosquecambiandecomportamientobasandoseenesosatributos,probablementedeberiarediseñarlaparaexpresarlasdiferenciasdecomportamientoconsubclasesymétodosvirtualesanulados.30.Ifdebehaceralgonoportable,creeunaabstracciónparaelservicioypóngaloenunaclase.Estenivelextradeindirecciónfacilitalaportabilidadmejorquesisedistribuyeraportodoelprograma.31.Evitelaherenciamúltiple.Estaráasalvodemalassituaciones,especialmentecuandoreparelasinterfacesdeclasesqueestánfueradesucontrol(veaelVo-lumen2).Deberíaserunprogramadorexperimentadoantesdepoderdiseñarconherenciamúltiple.32.Nouseherenciaprivada.Aunque,estáenellenguajeyparecequetieneunafuncionalidadocasional,elloimplicaambigüedadesimportantescuandosecombinaconcomprobacióndinámicadetipo.Creeunobjetomiembroprivadoenlugardeusarherenciaprivada.33.Sidosclasesestánasociadasentresidealgúnmodo(comoloscontenedoresylositeradores).intentehacerqueunadeellasseaunaclaseamigaanidadadelaotro,talcomolaLibreríaEstándarC++haceconlosinteradoresdentrodeloscontenedores(EnlaúltimapartedelCapítulo16semuestranejemplosdeesto).Nosoloponedemaniestolaasociaciónentrelasclases,tambiénpermitequeelnombredelaclasesepuedareutilizaranidándolaenotraclase.LaLibreríaEstándarC++lohacedeniendounclaseiteradoranidadadentrodecadaclasecontenedor,deesemodoloscontenedorestienenunainterfacecomún.Laotrarazónporlaquequerráanidarunaclaseescomopartedelaimplementaciónprivada.Enesecaso,elanidamientoesbeneciosoparaocultarlaimplementaciónmásporlaasociacióndeclasesylaprevencióndelacontaminacióndelespaciodenombrescitadaarriba.34.Lasobrecargadeoperadoresensólo«azucarsintáctico:»unamaneradiferentedehacerunallamadaafunción.Issobrecargaunoperadornoestáhaciendoquelainterfazdelaclaseseamásclaraofácildeusar,nolohaga.Creesólounoperadordeconversiónautomáticadetipo.Engeneral,seguirlasdirectricesyestiloindicadosenelCapítulo12cuandosobrecargueoperadores.35.Noseaunavíctimadelaoptimizaciónprematura.Esecaminollevaalalocura.Inparticular,nosepreocupedeescribir(oevitar)funcionesinline,haceralgu-nasfuncionesnovirtuales,anarelcódigoparahacerlomásecientecuandoestéenlasprimerfasedecontruccióndelsistema.Elobjetivoprincipaldeberíaserprobareldiseño,amenosqueelpropiodiseñorequieraciertaeciencia.36.Normalmente,nodejequeelcompiladorcreelosconstructores,destructoresoeloperator=porusted.Losdiseñadoresdeclasessiempredeberíandecirquédebehacerlaclaseexactamenteymantenerlaenteramentebajosucon-trol.Sinoquierecostructordecopiauoperator=,declareloscomoprivados.Recuerdequesicreaalgúnconstructor,elcompiladorunsintetizaráuncons-tructorpordefecto.37.Sisuclasecontienepunteros,debecrearelconstructordecopia,eloperator=yeldestructordelaclaseparaquefuncioneadecuadamente.38.Cuandoescribaunconstructordecopiaparaunaclasederivada,recuerdella-marexplícitamentealconstructordecopiadelaclasebase(tambiéncuandose
535
i
i
“Volumen1”—2012/1/12—13:52—page536—#574i
i
i
i
i
i
ApéndiceB.DirectricesdeProgramación
usanobjetosmiembro).(VeaelCapítulo14.)Sinolohace,elconstructorpordefectoseráinvocadodesdelacasebase(oelobjetomiembro)yconmuchaprobabilidadnoharáloqueustedespera.Parainvocarelconstructordecopiadelaclasebase,páseleelobjetoderivadodesdeelqueestácopiando:
Derived(constDerived&d):Base(d){//...
39.Cuandoescribaunoperadordeasignaciónparaunaclasederivada,recuerdellamarexplícitamentealoperadordeasignacióndelaclasebase.(VeaelCa-pítulo14.)SInolohace,noocurriránada(lomismoesaplicablealosobjetosmiembro).Parainvocareloperadordeasignacióndelaclasebase,useelnom-bredelaclasebaseyeloperadorderesolucióndeámbito:
Derived&operator=(constDerived&d){
Base::operator=(d);
40.Sinecesitaminimizarlasrecompilacionesduranteeldesarrollodeunproyectolargo,useFIXME:demostradaenelCapítulo5,yeliminelasolosilaecienciaentiempodeejecuciónesunproblema.41.Eviteelpreprocesador.Usesiempreconstparasubstitucióndevaloreseinli-nesparalasmachos.42.Mantengalosámbitostanpequeñoscomoseaposibledemodoquelavisibili-dadyeltiempodevidaddelosobjetossealomáspequeñoposible.Estoreduceelpeligrodeusarunobjetoenelcontextoequivocadoyellosuponeunbugdi-cildeencontrar.Porejemplo,supongaquetieneuncontenedoryuntrozodecódigoqueiterasobreél.Sicopiaelcódigoparausarlootrocontenedor,pue-dequeaccidentalmenteacabeusandoeltamañodelprimercontenedorcomoellímitesuperiordelnuevo.Pero,sielprimercontendorestuviesefueradelámbito,podríadetectarelerrorentiempodecompilación.43.Evitelasvariablesglobales.Esfuerceseenponeslosdatosdentrodeclases.Enmásprobablequeaparezcanfuncionesglobalesdeformanaturalquevariablesglobales,aunquepuedequedespuésdescubraqueunafunciónglobalpuedeencajarcomométodoestáticodeunaclase.44.Sinecesitadeclaraunaclaseofuncióndeunalibrería,hágalosiempreincluyen-dosucherodecabecera.Porejemplo,siquierecrearunafunciónparaescribirenunostream,nodeclarenuncaelostreamporustedmismo,usandounaespecicacióndetipoincompletacomoesta:
classostream;
Esteenfoquehacequesucódigoseavulnerablaacambiosenlarepresentación.(Porejmplo,ostreampodríasserenrealidaduntypedef.)Enlugardeloanterior,usesiempreelcheordecabecera:
#includeiostream�
Cuandocreesuspropiasclases,siunalibreríaesgrande,proporcionesasususuariosunaversiónabreviadadelcherodecabeceraconespecicacionesdetipoincompletas(esdecir,declaracionesdelosnombresdelasclases)para
536
i
i
“Volumen1”—2012/1/12—13:52—page541—#579i
i
i
i
i
i
C:LecturasrecomendadasC.1.SobreCThinkinginC:FoundationsforJava&C++,porChuckAllison(unseminarioenCDROMdeMindView,Inc.,2000,incluidoalnaldeestelibroydisponibletambiénenwww.BruceEckel.com).Setratadeuncursoqueincluyeleccionesytransparen-ciassobrelosconceptosbásicosdellenguajeCparaprepararallectoraaprenderJavaoC++.NoesuncursoexhaustivosobreC;sólocontienelonecesarioparacambiar-seaesosotroslenguajes.UnasseccionesadicionalessobreesoslenguajesconcretosintroducenalaspiranteaprogramadorenC++oenJava,asuscaracterísticas.Requi-sitosprevios:algunaexperienciaconunlenguajedealtonivel,comoPascal,BASIC,Fortran,oLISP(seríaposibleavanzarporelCDsinesebagaje,peroelcursonoestápensadoparaservirdeintroducciónbásicaalaprogramación).C.2.SobreC++engeneralTheC++ProgrammingLanguage,3ªedición,porBjarneStroustrup(Addison-Wesley1997).Hastaciertopunto,elobjetivodelaobraquetieneensusmanosespermitirleusarellibrodeBjarneamododereferencia.Dadoquecontieneladescripcióndellenguajeporsupropioautor,estípicamenteahídondesemirapararesolverdudassobrequésesuponequeC++debeonodebehacer.Cuandoempieceadominarellenguajeyestépreparadoparapasaralascosasserias,lonecesitará.C++Primer,3ªEdición,porStanleyLippmanyJoseeLajoie(Addison-Wesley1998).Hadejadodeserunaintroducción;sehaconvertidoenunvoluminosolibromuydetallista,yesunodelosqueconsultojuntoconeldeStroustrupcuandointentoresolverunacuestión.«PensarEnC++»debeproporcionarunabaseparaentenderC++PrimerasícomoellibrodeStroustrup.C&C++CodeCapsules,porChuckAllison(Prentice-Hall,1998).Eselibropresu-poneunconocimientodeCyC++,ytratacuestionesqueyahayansidoquebraderosdecabeza,oquenologrózanjaradecuadamentealaprimera.Laobrasolucionala-gunastantoenCcomoenC++.TheC++Standard.Eseeseldocumentoenelqueelcomitéhatrabajadotantoduranteaños.Noesgratis,desgraciadamente.PeroporlomenossepuedeadquirirenformatoPDFporsólo$18enwww.cssinfo.com.C.2.1.MipropialistadelibrosAparecenacontinuaciónordenadosporfechadepublicación.Notodosestánalaventaactualmente.
541
i
i
“Volumen1”—2012/1/12—13:52—page544—#582i
i
i
i
i
i
ApéndiceC.Lecturasrecomendadas
queestararmadohastalosdientesconherramientasmentalesqueayudenaseguirenelmododeexperimentación(«Estonofunciona,vamosaprobarotracosa»)ynoeneldenegación(«No,noesproblema.Todovamaravillosamente,nonecesita-moscambiar»).Creoqueloslibrossiguientes,leídosantesdeelegirunmétodo,leproporcionaránesasherramientas.SoftwareCreativity,porRobertGlass(Prentice-Hall,1995).Eseeselmejorlibroqueheleídoquedescribaunavisióndeconjuntosobreeldebatedelasmetodologías.ConstadeunaseriedeensayoscortosyartículosqueGlasshaescritoocomprado(P.J.Plaugeresunodelosquecontribuyenallibro),quereejansusnumerososañosdedicadosapensaryestudiareltema.Sonamenosydelalongitudjustaparadecirlonecesario;nodivaganiaburreallector.Perotampocovendesimplementeaire;haycentenaresdereferenciasaotrosartículosyestudios.Todoslosprogramado-resyjefesdeproyectodeberíanleereselibroantesdecaerenelespejismodelasmetodologías.SoftwareRunaways:MonumentalSoftwareDisasters,porRobertGlass(Prentice-Hall1997).Lorealmentebuenodeeselibroesqueexponealaluzloquenuncacontamos:lacantidaddeproyectosquenosolofracasan,sinoquelohacenespec-tacularmente.Veoquelamayoríadenosotrosaúnpiensa«Esonomevaapasaramí»(o«Esonovolveráapasarme»)ycreoqueesonosdesfavorece.Altenersiempreenmentequelascosaspuedensalirmal,seestáenmejorposiciónparahacerlasirbien.ObjectLessonsporTomLove(SIGSBooks,1993).otrobuenlibroparatener«pers-pectiva».Peopleware,porTomDemarcoyTimothyLister(DorsetHouse,2ªedición1999).Apesardequetieneelementosdedesarrollodesoftware,eselibrotratadeproyectosyequiposdetrabajoengeneral.Peroelénfasisestápuestoenlaspersonasysusnecesidades,ynoenlastecnologías.Sehabladecrearunentornoenelquelagenteestéfelizyproductiva,enlugardedecidirlasreglasquedebenseguirparaconver-tirseperfectosengranajesdeunamáquina.Estaúltimaactitud,piensoyo,esloquemáscontribuyeaquelosprogramadoressonríanydigansíconlacabezacuandounmétodoesadoptadoysigantranquilamentehaciendolomismoquesiempre.Complexity,byM.MitchellWaldrop(Simon&Schuster,1992).Relataelencuen-troentreungrupodecientícosdediferentesdisciplinasenSantaFe,NuevoMéji-co,paradiscutirsobreproblemasrealesquecomoespecialistasnopodíanresolveraisladamente(elmercadobursátileneconomía,laformacióninicialdelavidaenbiología,porquélagentesecomportadeciertamaneraensociología,etc.).Alre-unirlafísica,laeconomía,laquímica,lasmatemáticas,lainformática,lasociología,yotrasciencias,seestádesarrollandounenfoquemultidisciplinaraesosproblemas.Peromásimportanteaun,unanuevaformadepensarenesosproblemasextrema-damentecomplejosestáapareciendo:alejándosedeldeterminismomatemáticoydelailusióndepoderescribirunafórmulaquepredigatodosloscomportamientos,hacialanecesidaddeobservarprimeroybuscarunpatrónparadespuésintentaremularloportodoslosmediosposibles.(Ellibrocuenta,porejemplo,laaparicióndelosalgoritmosgenéticos).Esetipodepensamiento,creoyo,esútilamedidaqueinvestigamosformasdegestionarproyectosdesoftwarecadavezmáscomplejos.
544

Documentos PDF asociados:

Pensar en C++ (Volumen 1) - Grupo ARCO
HISTORIA DEL TIRO CON ARCO - Igartza Arco
Pensar rapido, pensar despacio - Medicina y Arte
O ARCO IRIS EL A URA - oceanodeteosofia.com
Angulos y longitudes de arco - hcornejo.com
17-SOLDADURA DE ARCO ELECTRICO - biblio3.url.edu.gt
LA LEY DEL ARCO DE ANDRÉ MALBY - tallerdearqueria.es
Soldador por arco con electrodo revestido - trabajo.gov.ar
Soy un Guerrero del Arco Iris - archivo-es.greenpeace.org
BRUJERÍA Y HECHICERÍA EN LATINOAMÉRICA ARCO TEÓRICO Y ...
Un arco iris de alimentos Querida familia: Hoy en mi clase ...
antonio skarmeta pdf arco iris Los dias del - WordPress.com
Cada vez más gente elige el tiro con arco Con el espíritu de
001-240 Pensar bien - planetadelibros.com
FERNANDO SAVATER La aventura de pensar
APRENDE A PENSAR POR TI MISMO - infoservi.com
O símbolo dá que pensar - Universidade de Coimbra
SEIS SOMBREROS PARA PENSAR - ues.mx
Pensar el construir, el habitar y la técnica: una ...
Resumen De La Magia De Pensar En Grande Aprenda Los ...
LA MAGIA DE PENSAR EN GRANDE. David Schwartz
Aprende A Pensar Por Ti Mismo Edward De Bono
CUENTOS PARA PENSAR - salvablog01.files.wordpress.com
Aprenda a Pensar Como un Programador - Argentina en Python
RESUMEN LIBRO: LA MAGIA DE PENSAR EN GRANDE Autor: David J ...
Metatemas Libros para pensar la ciencia Colección dirigida ...
Cuentos para soñar y pensar: Charles Perrault para niños
Pensar bien - sentirse bien: manual práctico de terapia ...
Volumen 4 - catedralesgoticas.es
VOLUMEN II - anmat.gov.ar
Volumen 1 - catedralesgoticas.es
Volumen 2 BIOQUÍMICA
Volumen 2 - catedralesgoticas.es
¿Cómo pensar como Sherlock Holmes? www.librosmaravillosos ...
REFERENCIAS BIBLIOGRÁFICAS VOLUMEN I - UB
Momentos de la Creación Volumen 160
masa (m) Volumen (v) - redjbm.com
Metafísica 4 en 1 Volumen 1 - eruizf.com
Solucionario unidad 7 del volumen 2
Volumen 2 BIOQUÍMICA - Biología Celular