|
Adolfo Di Mare |
Resumen |
Abstract |
|
|
Versión CISCI-2009 --- BASE-CISCI-2009.pdf --- BASE-CISCI-2009.ppt
Introducción |
Introduction |
Mediante el uso de clases abstractas es posible lograr que el diseñador C++ construya clases que sirven como plantilla para las clases derivadas que usarán los programadores clientes de sus módulos. Las clases abstractas son un mecanismo del lenguaje que le permite al compilador verificar que la reimplementación de métodos se ha hecho cumpliendo con su especificación. En este escrito se muestra que la potencia expresiva de C++ permite lograr lo mismo usando plantillas y evitando así pagar el costo de funciones virtuales, lo que puede ser útil en muchas aplicaciones en las que la memoria o el tiempo de ejecución tienen gran prioridad. | By using abstract classes a C++ designer can build classes that serve as templates of derived classes for client programmers of his or her modules. Abstract classes are a language mechanism that allows the compiler to verify that methods have been reimplemented complying with their specification. In this paper it is shown that the expressive power of C++ allows to achieve the same thing by using templates and thus avoiding the cost of virtual functions, which may be useful in many applications where memory and execution time have high priority. |
Las plantillas C++ como sustitución del polimorfismo |
C++ templates as a substitution for polymorphism |
1 2 3 4 5 |
class Base { public: virtual void doIt() = 0; }; // ... Base boom; // Error |
|5| error: cannot declare variable `boom' to be of type `Base' |5| error: because the following virtual functions are abstract: |2| error: virtual void Base::doIt() |
Si "Base " es una clase abstracta es porque contiene
algún método "Base::doIt() " virtual o
polimórfico que no está implementado. No es posible
crear objetos de tipo "Base "; si alguno fuera creado,
al ejecutarle su método "doIt() " se
produciría un error en tiempo de ejecución porque
ese método no ha sido definido (pues en la
VMT, la tabla de
métodos virtuales de la clase "Base ", el
puntero para el método "doIt() " invoca
una rutina de error). En la
Figura 1 se muestra que el compilador
detecta este tipo de error si
cualquier programador cliente trata de declarar un objeto de
tipo "Base ".
|
If "Base " is an abstract class is because it contains
a virtual or polymorphic method "Base::doIt() "which
is not implemented. It will not be possible to create objects of
type "Base" and if any were created, execution of its method
"doIt() " would cause a runtime error because this
method has not been defined (because in the VMT, the virtual
methods table for class "Base", the pointer to the method
"doIt() " invokes a routine error.) In
Figura 1 it is shown that the compiler
detects this type of error if any client programmer tries to
declare an object of type "Base ".
|
La diferencia entre "definir" y "declarar" un
objeto en C++ es que las declaraciones se ponen en los archivos de
encabezados <*.h> y <*.hpp> ,
mientras que las definiciones están en los archivos de
implementación <*.c> y
<*.cpp> .
#define que sirve para implementar macros en C++:
#define max(a,b) ( (a)>(b) ? (a) : (b) )
|
The difference between "defining" and
"declaring" an object in C++ is that declarations belong
to header files <*.h> and
<*.hpp> , while the definitions belong to
implementation files <*.c> and
<*.cpp> .
#define
directive used to implement C++ macros:
#define max(a,b) ( (a)>(b) ? (a) : (b) )
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
template <class X> void funBase( X & obj ) { obj.setVal( 5 ); } class TheBase { protected: void setVal( int arg ); }; class Derived : public TheBase { public: void setVal( int arg ) { /* ... */ } }; void call_ok() { Derived val; // ok val.setVal( 12 ); funBase( val ); } |
En la Figura 2 se define la
función de biblioteca "funBase<>() " como
una plantilla, aún antes de declarar las clases
"TheBase " y "Derived ". Será
cuando esa función sea invocada que el compilador
podrá aplicar la plantilla e
instanciar la función para determinar si hay errores.
Luego está declarada la clase "TheBase " que
contiene al método "setVal() ", pero
está declarado como protegido (y además no ha sido
implementado en ningún sitio). Como "setVal() "
es un método protegido, si cualquier programador cliente
declara un objeto de tipo "TheBase ", al invocar el
método "setVal() " o al usar la función
de biblioteca "funBase<>() " se
produciría un error de compilación. El compilador
acepta sin problemas todo este bloque de código, pero si se
declara la variable "val " con el tipo
"TheBase " el compilador detecta varios errores y
emite los mensajes correspondientes.
|
In Figura 2 the library function
"funBase<>() " is defined as a template even
even before classes "TheBase " and
"Derived " get declared. It will be after this
function is invoked that the compiler will be able to apply the
template and instantiate the function to determine if there are
errors. Then class "TheBase " is declared containing
the method "setVal() ", but it is declared protected
(and it is not implemented anywhere). As "setVal() "
is a protected method, if any client programmer declares an object
of type "TheBase ", when invoking method
"setVal() " or when using library function
"funBase<>() " the compiler will emit an error.
The compiler accepts without problems this whole block of code,
but when declaring variable "val " of type
"TheBase " the compiler detects several errors and
emits the corresponding messages.
|
14 15 16 17 18 |
void call_error() { TheBase val; // Error !!! val.setVal( 12 ); funBase( val ); } |
| |In function `void call_error()': | 7| error: `void TheBase::setVal(int)' is protected |16| error: within this context | |In function `void funBase(X&) [with X = TheBase]': |17| instantiated from here | 7| error: `void TheBase::setVal(int)' is protected | 3| error: within this context |
En la Figura 3 se muestra el mensaje de
error que el compilador emite no dice que la clase
"TheBase " ha sido usada para declarar una variable,
como sí está explícitamente dicho cuando se
trata de instanciar la clase abstracta "Base " en la
Figura 1. Sí se hace referencia a
que es impropio utilizar un
método protegido, que es visible únicamente
para clases derivadas: como la función
"call_error() " no es un método de una clase
derivada, apropiadamente el compilador emite el error. La
única diferencia de la implementación de esta
función y "call_ok() " es el uso incorrecto de
la clase "TheBase " en lugar de la clase
"Derived " para la que el método
"setVal() " sí está definido. En otras
palabras, usando el truco de declarar como protegido en la clase
base el método que se desea reimplementar para toda clase
derivada, pero no definiéndolo, se obtiene el apoyo del
compilador para que éste verifique que en la clase derivada
se ha usado la correcta
especificación e
implementación. "TheBase " es una clase
abstracta implementada sin usar polimorfismo. (Aún si se
incluye la implementación para esta operación se
obtendría un error de compilación).
|
In Figura 3 it is shown that the error
message the compiler emits does not say that class
"TheBase " has been used to declare a variable, as it
is explicitly said when trying to instantiate the abstract class
"Base " in
Figura 1. It is mentioned that there is
an improper use of a protected method, which is visible only to
derived classes: as function "call_error() " is not a
method in a derived class, appropriately the compiler emits the
error. The only difference in the implementation of this function
and "call_ok() " is the incorrect usage of class
"TheBase " instead of class "Derived " for
which method "setVal() " is defined. In other words,
using the trick of declaring protected in the base class the
method you want to be reimplemented in every derived class, you
get support for the compiler to verify that in every derived class
the correct specification and implementation has been used.
"TheBase " is an abstract class implemented without
using polymorphism. (Even if the implementation for this operation
is included the compiler would issue an error).
|
Usos de clases abstractas no polimórficas |
Use of non polymorphic abstract clases |
La primera aplicación de las clases abstractas no polimórficas es usarlas en el aula, para mostrarle a los estudiantes las ventajas de separar la especificación de la implementación de un módulo. Para mostrar varias implementaciones diferentes del mismo objeto basta declararlo como una clase abstracta base, incluyéndole únicamente sus operaciones, para luego incorporar en cada implementación la definición de los métodos y funciones apropiados. | The first application of non polymorphic abstract classes is using them in the classroom, to show students the advantages of separating the specification from the implementation of a module. To show several different implementations of the same object it is enough to declare it as an abstract base class, including only its operations, to later incorporate the apropiate definitions of methods and functions for each implementation. |
+---------------+ | Matrix_BASE<> | +---------------+ / \ +----------------+ +-----------------+ | Matrix_Dense<> | | Matrix_Sparse<> | +----------------+ +-----------------+ |
template <class E> class Matrix_BASE { public: typedef E value_type; E & operator() (unsigned, unsigned); friend Matrix_BASE operator+ ( const Matrix_BASE& A, const Matrix_BASE& B ); }; |
En la Figura 4
se muestra la clase
"Matrix_BASE<> " que contiene las operaciones
más importantes para matrices. En las clases derivadas, que
en este caso son
"Matrix_Dense<> " y
"Matrix_Sparse<> ", están reimplementados
los métodos de la clase base. Como complemento a este
ejemplo se puede usar una biblioteca de funciones, en las que
posiblemente se encuentren las funciones
"isScalar<>() " o
"isDiagonal<>() " que verifican alguna propiedad
de la matriz utilizando únicamente los métodos
públicos de la implementación.
|
Class
"Matrix_BASE<> " shown in
Figura 4 contains the most important
operations for matrices. In derived classes, which in this case
are
"Matrix_Dense<> " and
"Matrix_Sparse<> ", the methods of the base class
get reimplemented. To complement this example library of functions
can be used, amongst which functions
"isScalar<>() " or
"isDiagonal<>() " would possibly be found to
verify some matrix property using only public methods in the
implementation.
|
template <class Mat> bool isDiagonal( const Mat& M ) { if ( M.rows() != M.cols() ) { return false; } typename Mat::value_type ZERO = 0; for (unsigned i=1; i < M.rows(); i++) { for (unsigned j=0; j < i; j++) { if ( M(i,j) != ZERO ) { return false; } else if (M(j,i) != ZERO) { return false; } } } return true; } |
En la Figura 5 está la
implementación de la función de biblioteca
"isDiagonal<>() " que utiliza los métodos
públicos de la clase para determinar si la matriz es o no
una matriz diagonal. Al definir la constante "ZERO "
es necesario usar la palabra reservada C++
"typename " para que informarle al compilador que el
identificador "value_type " es el nombre de un tipo o
de una clase, pues es la plantilla
"isDiagonal<>() " debe ser compilada antes
compilar cualquier uso de la versión de las clases a la que
esta función será aplicada y, por ende, el
compilador no tiene forma de adivinar que
"value_type " es un tipo. En la práctica
ocurrirá que la matriz "Matrix_BASE<> "
seguramente esté definida en el archivo de encabezado
"Matrix_BASE.h " y la biblioteca de funciones
seguramente estará definida en otro archivo de encabezado,
por ejemplo
"Matrix_Lib.h ", el que no necesariamente será
usado siempre por todos los programadores. Inclusive el orden de
inclusión de estos 2 archivos es irrelevante, pues es
válido incluir uno u otro de primero.
|
Figura 5
is the implementation for library function
"isDiagonal<>() " that uses the public methods
of the class to determine whether or not the matrix is a diagonal
matrix. When defining the constant "ZERO " it is
necessary to use the
"typename " C++ keyword to tell the compiler that
identifier "value_type " is the name of a type or a
class, as template "isDiagonal<>() " must be
compiled before compiling any use of the version of the classes to
which this function will be applied, and thus the compiler has no
way of guessing that
"value_type " is a type. In practice it will happen that
the matrix "Matrix_BASE<> " will be defined in
header file
"Matrix_BASE.h " and the library functions will be
defined in another header file, for example,
"Matrix_Lib.h ", which will not necessarily be always
used by all programmers. Even the order of inclusion of these 2
files is irrelevant, since it is valid to include one or the other
first.
|
Otro ejemplo importante del uso de esta técnica es
amalgamar el acceso a archivos. Para esto hay que definir la clase
base de acceso "FILE_Base<> " que contiene los
verbos de acceso más importantes, como
"open<>() " y "close<>() ".
Luego, en las clases derivadas se incluye la implementación
específica para cada tipo de acceso.
|
Another important example of the use of this technique is to
amalgamate access to files. For this we must define the base
access class "FILE_Base<> " that contains the
more important access verbs, such as "open<>() "
and "close<>() ". Then, the specific
implementation for each type of access is included in the derived
classes.
|
|
||
template <class X> class FILE_bt: public FILE_Base<X> { void *Btrieve_FBLOCK; public: void open( const char* fName ) {} void close() {} void find( const char * key ) {} bool hasNext() { return false; } void write( const X& val) {} }; void use_Btrieve() { FILE_bt< Person > F; F.open( "data_file.bt" ); { using namespace std; F.find( "Phoebe" ); while ( F.hasNext() ) { cout << F->name << F->age; } } F.close(); } |
template <class X> class FILE_dbf: public FILE_Base<X> { void *DBase_BUFF; public: void open( const char* fName ) {} void close() {} void find( const char * key ) {} bool hasNext() { return false; } void write( const X& val) {} }; void use_DBase() { FILE_dbf< Person > F; F.open( "data_file.dbf" ); { using namespace std; F.find( "Phoebe" ); while ( F.hasNext() ) { cout << F->name << F->age; } } F.close(); } |
En la Figura 6
se muestra cómo es posible amalgamar el acceso a archivos
Btrieve y DBase usando la interfaz común definida en
"FILE_Base<> ". Este enfoque se compara muy
favorablemente con otros como el expuesto en
[DiM-1994], y se puede usar para concretar
una clase genérica para acceso a tablas o bases de datos.
|
In Figura 6
it is showwn how we can amalgamate access to Btrieve and dBase
files using the common interface defined in
"FILE_Base<> ". This approach compares very
favorably with others as described in
[DiM-1994], and can be used to get a
generic class to access tables or databases.
|
Detalles de implementación |
Implementation details |
Trasladar a la práctica la idea básica expresada en
la
Figura 2 requiere de un poco de ingenio
pues es inevitable encontrar problemas al usar el lenguaje para
algo más allá de lo que fue esperado por su
diseñador
[Str-1998]. La organización de las
funciones amigas y los métodos en la clase permite ver
cuáles son los métodos abstractos cuya
implementación está definida en la clase base
"Matrix_BASE<> ". En el código fuente
están agrupadas de manera que sea sencillo reutilizarlas,
pues en un solo bloque de código están todas las
implementaciones juntas. Además, el constructor por
omisión de la clase sí tiene que estar definido
porque el compilador lo necesita para inicializar la base de todo
objeto de tipo "Matrix_BASE<> "; lo mismo sucede
con el destructor. Lo demás métodos no están
implementados pues son métodos abstractos, cuya
implementación debe definirse en las clases derivadas.
|
Taking into practice the basic idea expressed in
Figura 2 requires a little ingenuity as
it is inevitable to find problems when using the language for
anything beyond what was anticipated by its designer
[Str-1998].
The organization of friends functions and methods in the class
helps to see which are the abstract methods whose implementation
is defined in the base class
"Matrix_BASE<> ".
In the source code they are grouped in a way that makes it easy to
reuse, as all implementations are together a single block of code.
Furthermore, the default constructor for the class itself must be
defined because the compiler needs to initialize the base of any
object of type "Matrix_BASE<> "; the same
happens with the destructor. The other methods are not implemented
because they are abstract methods, whose implementation must be
defined in derived classes.
|
Otra dificultad surge con la especificación del operador de
suma para matrices, cuya declaración natural es
ésta:
template <class MAT> MAT operator+( const MAT& A, const MAT& B );Desafortunadamente, cuando se usa la matriz junto con otras clases, como por ejemplo " std::string<> " u otra similar, esta
definición produce un conflicto de ambigüedad, pues
esta misma definición también calza como operador
para la clase "string<> ". La forma de evitar
este choque es declarar "A ", el primer
parámetro, de un tipo marcado por la matriz. Con esta
modificación, ya la plantilla de matrices no calza con
parámetros de otro tipo y así queda eliminado el
conflicto con la clase "string<> ". Esta es la
declaración del primer parámetro "A ":
const Matrix_BASE<typename MAT::value_type>& A;Hay que usar punteros para transformar esta referencia a la clase base en una referencia a la clase derivada; por eso la implementación incluye este renglón: MAT Res = *( (MAT*)(&A) );que deja en la variable " Res " una copia del primer
parámetro "A ".
|
Another difficulty arises with the specification for the addition
operator for matrices, whose natural declaration is:
template <class MAT> MAT operator+( const MAT& A, const MAT& B );Unfortunately, when the matrix is used with other classes, such as " std::string<> " or any similar, this definition
produces a conflict of ambiguity, because this definition also
fits as an operator for class "string<> ". The
way to avoid this clash is to declare "A ", the first
parameter, of a type tagged by the matrix. With this modification,
the matrix template will not fit with other parameters of another
type and thus the conflict with the class
"string<> " will be removed. This is the
declaration of the first parameter "A ":
const Matrix_BASE<typename MAT::value_type>& A;Pointers must be used to cast the reference to the base class as a reference to derived class; this is why the implementation includes this line: MAT Res = *( (MAT*)(&A) );which leaves in variable " Res "a copy of the first
parameter "A ".
|
/// A+B template <class MAT> inline MAT operator- ( const Matrix_BASE< typename MAT::value_type >& A, const MAT& B ) { MAT Res = *( (MAT*)(&A) ); add_Matrix(Res,B); return Res; } |
/// Res += M template <class MAT> void add_Matrix( MAT& Res, const MAT& M ) { // verifica que las dos matrices sean del mismo tamaño assert( "Matrix_BASE<E>::add()" && (Res.rows()==M.rows()) ); assert( "Matrix_BASE<E>::add()" && (Res.cols()==M.cols()) ); for ( unsigned i=0 ; i<Res.rows() ; ++i ) { for ( unsigned j=0 ; j<Res.cols() ; ++j ) { Res.operator()(i,j) += M(i,j); } } return; } |
En la parte superior de la
Figura 7
está la implementación de la operación de
suma en la que se usa el primer parámetro "A "
de la rutina como una etiqueta que evita que la plantilla sea
instanciada para tipos que no están derivados de la clase
"Matrix_BASE<> "; el segundo parámetro
"B " tiene el tipo correcto para ambos
parámetros. Debido a que el tipo del parámetro
"A " es "Matrix_BASE<> ", esta
versión de "operator+<>() " solo se puede
aplicar a objetos derivados de la clase
"Matrix_BASE<> " y, por eso, no hay posibilidad
de ambigüedad cuando se usan matrices y otros tipos para los
que ha sido sobrecargado "operator+<>() ".
|
The implementation of the addition operation is at the top of
Figura 7
where the first parameter "A " is used as a tag to
that keeps the template from being instantiated for types that are
not derived from class "Matrix_BASE<> "; the
second parameter "B " has the correct type for both
parameters. Because the type of the parameter "A " is
"Matrix_BASE<> ", this version of
"operator+<>() " applies only to objects derived
from class "Matrix_BASE<> " and, therefore,
there is no possibility ambiguity when using matrices and other
types for which "operator+<>() " has been
overloaded.
|
En esta implementación de la suma de matrices se usa la
función auxiliar "add_Matrix<>() " para
hacer el trabajo de sumar (la diferencia entre
"función"
y "método" es que la función nunca tiene un
parámetro
"this ", pues existe independientemente de cualquier
clase, mientras que el método siempre está
relacionado a una clase). Esta función es una
implementación abstracta de la suma de matrices, la que
puede ser
reutilizada en las clases concretas derivadas de
"Matrix_BASE<> ", a menos que sea más
conveniente utilizar una implementación específica
más eficiente. Por ejemplo, como la clase
"Matrix_Dense<> " está implementada usando
un vector unidimensional que contiene todos los valores de la
matriz, la implementación más eficiente consiste en
recorrer los 2 vectores por sumar para obtener el resultado. Esta
implementación contrasta con la usada para
"Matrix_Sparse<> ", que sí reutiliza la
implementación de la clase abstracta.
|
In this implementation of the addition for matrices the auxiliary
function "add_Matrix<>() " is used to do the job
of adding (the difference between a
"function" and a
"method" is that the function never has a parameter
"this ", because it exists independently of any class,
while a method is always related to a class). This function is an
abstract implementation of matrix addition, which can be
reused
in the concrete classes derived from
"Matrix_BASE<> ",
unless it is more convenient to use a more efficient specific
implementation. For example, as class
"Matrix_Dense<> "
is implemented using a vector containing all matrix values, the
most efficient implementation is to traverse the 2 vectors to add
and obtain the result. This contrasts with the implementation used
for
"Matrix_Sparse<> ", where the implementation of
the abstract class is reused.
|
template <class T> void add_Matrix( Matrix_Dense<T>& Res, const Matrix_Dense<T> & M ) { // verifica que las dos matrices sean del mismo tamaño assert( "Matrix_Dense<E>::add()" && (Res.rows()==M.rows()) ); assert( "Matrix_Dense<E>::add()" && (Res.cols()==M.cols()) ); T *pRes = Res.m_val; T *pM = & M.m_val[0]; T *pEND = & Res.m_val[M.m_cols * M.m_rows]; for ( ; pRes != pEND; ++pRes, ++pM ) { *pRes += *pM; } return; } |
Si cualquier programador utiliza matrices de tipo
"Matrix_Sparse<> " para sumarlas, el compilador
usará la plantilla de la suma de la parte superior de la
Figura 7 y, por ende, también
instanciará la plantilla de que está en la parte
inferior de esa misma figura. En contraste, si la matriz es de
tipo "Matrix_Dense<> ", el compilador siempre
usará la misma plantilla para
"operator+<>() " pero usará la
implementación de "add_Matrix<>() " de la
Figura 8 porque es más
específica que la implementación general que aparece
en la parte de abajo de la
Figura 7. De esta manera no es necesario
reimplementar un método abstracto si la
implementación provista en la clase base es adecuada, pero
sí es posible aportar una implementación
específica en los casos en que esa sea el mejor enfoque.
|
If any programmer uses matrices of type
"Matrix_Sparse<> " to add them, the compiler
will use the upper part of
Figura 7 as the template for addition
and, thus, it will also instantiate the template at the bottom of
the same figure. In contrast, if the matrices are of type
"Matrix_Dense<> ", the compiler will still use
the same template for "operator+<>() " but it
will use the implementation for
"add_Matrix<>() " in
Figura 8 because it is more specific
than the general implementation that appears at the bottom of
Figura 7. In this way there is no need
to reimplement an abstract method if the implementation provided
in the base class is appropriate, but it is possible to provide a
specific implementation in cases where this is the better
approach.
|
Un problema que surge al usar "Matrix_BASE<> "
como etiqueta para marcar el parámetro de la suma es que el
compilador no puede hacer la conversión automática
de un escalar en una matriz, por lo que este código no
compila:
V = 2 + A;
Hay que usar una conversión explícita invocando directamente al constructor de la clase: V = Matrix_List<unsigned>(2) +
A;
Esta limitación no es exclusiva del uso de clases abstractas no polimórficas, sino que es compartida por cualquier clase C++ para la que se usen operadores sobrecargados (también es correcto argumentar que para evitar errores cualquier programador debe ser consciente de la conversión de un escalar en una matriz y, por eso, no conviene que esa conversión sea automática). |
A problem arises when using "Matrix_BASE<> " as
a tag for the parameter in the addition is that the compiler can
not do the automatic conversion of a scalar into a matrix, so this
code will not compile:
V = 2 + A;
An explicit conversion directly invoking the constructor of the class must be used: V = Matrix_List<unsigned>(2) +
A;
This limitation is not exclusive to the use of non polymorphic abstract classes, but is shared by any C++ class where overloaded operators are used (it is also valid to argue that, to avoid errors, any programmer should be aware of conversions from a scalar to a matrix, which makes inconvenient for this conversion to be automtatic). |
Es perfectamente válido definir campos en la clase
abstracta. Sin embargo, para el caso específico de esta
matriz, no hace falta incorporar ningún campo común
en "Matrix_BASE<> " por lo que esa clase solo
tiene miembros que son métodos.
|
It is perfectly valid to define fields in the abstract class.
However, for the specific case of this matrix, there is no need to
include any common fields in "Matrix_BASE<> " so
the members for this class are just methods.
|
Es una buena práctica de construcción de programas
especificar y documentar módulos, usando herramientas como
Doxygen
[VH-2005]. Si se usa el enfoque descrito en
[DiM-2008] para definir las
especificaciones, ayuda mucho el uso del comando
"\copydoc " que permite copiar la especificación
de los métodos abstractos de la clase base en la
documentación de la clase derivada. En la
implementación de "Matrix_BASE<> " y
todas sus clases derivadas se ha aprovechado esta facilidad de
Doxygen.
|
It is a good program construction practice to specify and document
modules using tools such as Doxygen
[VH-2005]. Using the approach described in
[DiM-2008] to define the specifications,
it helps a lot to use command
"\copydoc " that allows you to copy the specification
for the abstract methods in the base class into the documentation
for the derived class. In implementing
"Matrix_BASE<> " and all its derived classes
this Doxygen facility has been used.
|
Es interesante expandir este trabajo agregándole restricciones al código similares a las expuestas en [Mey-2008] o aprovechar nuevas constucciones sintácticas del lenguaje como los "conceptos" para las plantillas C++ que han sido propuestos para el nuevo estándar para el lenguaje C++ [STD-2008]. El uso de etiquetas para los operadores aritméticos es un paso en esa dirección. | It is interesting to further this work adding restrictions similar to those explained in [Mey-2008] or taking advantage of new syntactic constructions for the language such as "concepts" for C++ templates that have been proposed for the new C++ standard [STD-2008]. The use of tags for arithmetic operators is a step in that direction. |
Método de uso |
Usage method |
Para obtener una clase abstracta no polimórfica se pueden
seguir estas prácticas de programación:
|
To get a non polymorphic abstract class these programming
practices can be followed:
|
Conclusiones |
Conclusions |
Una de las contribuciones más importantes de la
programación orientada a los objetos
[OOP] es mostrar la
importancia de separar la especificación de la
implementación. Al utilizar clases abstractas no
polimórficas se obtiene el beneficio de las clases
abstractas sin obligar al programador a pagar el costo de uso de
métodos virtuales, lo que son invocados indirectamente a
través de punteros almacenados en cada
instancia de un objeto. El enfoque aquí expuesto tiene
al menos 2 aplicaciones inmediatas:
|
One of the most important contributions of object-oriented
programming [OOP] is to show the importance of separating the
specification from the implementation. By using non polymorphic
abstract classes the benefit of the abstract classes is gotten
without forcing the developer to pay the cost of using virtual
methods, which are invoked indirectly through pointers stored in
each instance of an object. The approach presented here has at
least 2 immediate applications:
|
La primera aplicación es muy relevante en cursos de programación pues le permite a los profesores mostrar varias implementaciones para la misma clase. La segunda le puede facilitar el trabajo a programadores profesionales que necesitan construir módulos o bibliotecas eficientes y portátiles para muchas plataformas. Es importante combinar esta práctica de programación con el uso de herramientas de documentación como Doxygen, para facilitar la especificación y documentación de módulos de programación. | The first application is very important in programming courses because it enables teachers to display multiple implementations for the same class. The second one may facilitate the work of professional programmers who need to build modules or libraries, efficient and portable to many platforms. It is important to combine this programming practice with tools for documentation such as Doxygen, to facilitate the specification and documentation of programming modules. |
Agradecimientos |
Acknowledgements |
David Chaves y Alejandro Di Mare aportaron varias observaciones y sugerencias importantes para mejorar este trabajo. | David Chaves and Alejandro Di Mare made several observations and important suggestions to improve this work. |
Código fuente |
Source code |
BASE.zip
:
[.zip]
http://www.di-mare.com/adolfo/p/BASE/BASE.zip
http://www.di-mare.com/adolfo/p/BASE/es/index.html
http://www.di-mare.com/adolfo/p/BASE/en/index.html
Matrix_BASE<>
:
[.es]
[.en]
[.h]
[.txt]
http://www.di-mare.com/adolfo/p/BASE/es/classMx_1_1Matrix__BASE.html
http://www.di-mare.com/adolfo/p/BASE/en/classMx_1_1Matrix__BASE.html
Matrix_Dense<>
:
[.es]
[.en]
[.h]
[.txt]
http://www.di-mare.com/adolfo/p/BASE/es/classMx_1_1Matrix__Dense.html
http://www.di-mare.com/adolfo/p/BASE/en/classMx_1_1Matrix__Dense.html
Matrix_List<>
:
[.es]
[.en]
[.h]
[.txt]
http://www.di-mare.com/adolfo/p/BASE/es/classMx_1_1Matrix__List.html
http://www.di-mare.com/adolfo/p/BASE/en/classMx_1_1Matrix__List.html
Matrix_Sparse<>
:
[.es]
[.en]
[.h]
[.txt]
http://www.di-mare.com/adolfo/p/BASE/es/classMx_1_1Matrix__Sparse.html
http://www.di-mare.com/adolfo/p/BASE/en/classMx_1_1Matrix__Sparse.html
Matrix_Lib<>
:
[.es]
[.en]
[.h]
[.txt]
http://www.di-mare.com/adolfo/p/BASE/es/Matrix__Lib_8h.html
http://www.di-mare.com/adolfo/p/BASE/en/Matrix__Lib_8h.html
Gaussian_Elimination<>
:
[.es]
[.en]
[.h]
[.txt]
http://www.di-mare.com/adolfo/p/BASE/es/Gaussian__Elimination_8h.html
http://www.di-mare.com/adolfo/p/BASE/en/Gaussian__Elimination_8h.html
test_Matrix.cpp
:
[.es]
[.en]
[.cpp]
[.txt]
http://www.di-mare.com/adolfo/p/BASE/es/test__Matrix_8cpp.html
http://www.di-mare.com/adolfo/p/BASE/en/test__Matrix_8cpp.html
test_Gauss.cpp
:
[.es]
[.en]
[.cpp]
[.txt]
http://www.di-mare.com/adolfo/p/BASE/es/test__Gauss_8cpp.html
http://www.di-mare.com/adolfo/p/BASE/en/test__Gauss_8cpp.html
ftp://ftp.stack.nl/pub/users/dimitri/doxygen-1.5.8-setup.exe
Bibliografía |
Bibliography |
|
|
[DiM-1994] | Di Mare, Adolfo:
"genridx.h " Una interfaz uniforme para el uso
de archivos indizados en C,
Revista
Acta Académica,
Universidad Autónoma de Centro América,
Número 15,
pp [35-58],
ISSN 1017-7507, Noviembre 1994.
http://www.di-mare.com/adolfo/p/genridx.htm
http://www.di-mare.com/adolfo/p/src/genridx.zip
http://www.uaca.ac.cr/actas/1994nov/genridx.htm
|
[DiM-2008] | Di Mare, Adolfo:
Uso de Doxygen para especificar módulos y programas,
I Congreso Internacional de Computación y Matemática,
(CICMA-2008),
celebrado del 21 al 23 de agosto en la Universidad Nacional
(UNA), Costa Rica, I.S.B.N.:
978-9968-9961-1-5, 2008.
http://www.di-mare.com/adolfo/p/Doxygen.htm
|
[Mey-2008] | Meyers, Scott:
Enforcing Code Feature Requirements in C++,
Artima Developers, 2008.
http://www.artima.com/cppsource/codefeatures.html
|
[STD-2008] | (c) ISO/IEC:
Working Draft, Standard for Programming Language C++,
N2798=08-0308, 2008.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2798.pdf
|
[Str-1998] | Stroustrup, Bjarne:
The C++ Programming Language, 3rd edition,
Addison-Wesley; 1998.
http://www.research.att.com/~bs/3rd.html
|
[VH-2005] | van Heesch, Dimitri:
Doxygen,
2005.
http://www.doxygen.org/index.html
|
Indice |
Index |
|
|
Acerca del autor |
About the author |
Adolfo Di Mare: Investigador costarricense en la Escuela de Ciencias de la Computación e Informática [ECCI] de la Universidad de Costa Rica [UCR], en donde ostenta el rango de Profesor Catedrático. Trabaja en las tecnologías de Programación e Internet. Es Maestro Tutor del Stvdivm Generale de la Universidad Autónoma de Centro América [UACA], en donde ostenta el rango de Catedrático. Obtuvo la Licenciatura en la Universidad de Costa Rica, la Maestría en Ciencias en la Universidad de California, Los Angeles [UCLA], y el Doctorado (Ph.D.) en la Universidad Autónoma de Centro América. |
Adolfo Di Mare: Costarrican Researcher at the Escuela de Ciencias de la Computación e Informática [ECCI], Universidad de Costa Rica [UCR], where he is full professor. Works on Internet and programming technologies. He is Tutor at the Stvdivm Generale in the Universidad Autónoma de Centro América [UACA], where he is Cathedraticum. Obtained the Licenciatura at UCR, and the Master of Science in Computer Science from the University of California, Los Angeles [UCLA], and the Ph.D. at the Universidad Autónoma de Centro América. |
Acerca de este documento |
About this document |
Referencia: | Di Mare, Adolfo: Clases abstractas no polimórficas para C++, Artículo No. C088PB de la Octava Conferencia Iberoamericana en Sistemas, Cibernética e Informática, CISCI-2009, realizada en Orlando, Florida, EE.UU., julio 2009. |
Internet: |
http://www.di-mare.com/adolfo/p/BASE.htm
http://www.iiis.org/CDs2008/CD2009CSC/CISCI2009/PapersPdf/C088PB.pdf
http://www.iiis.org/CDs2008/CD2009CSC/CISCI2009/Abstract.asp?myurl=C088PB.pdf
|
Autor: | Adolfo Di Mare
<adolfo@di-mare.com>
|
Contacto: | Apdo 4249-1000, San José Costa Rica Tel: (506) 2511-8000 Fax: (506) 2438-0139 |
Revisión: | ECCI-UCR, Enero 2009 |
Visitantes: |