sábado 13 de diciembre de 2008

¡Nos mudamos!

¡NOS MUDAMOS!



Hola a todos. Pues bueno, tras casi dos años de estar bloggeando sobre C++, este es el fin del blog.

Pero calma, que no panda el cúnico. Solo nos mudamos

Sucede que estoy abriendo mi sitio web personal, y entre otras amenidades todo lo que tenía de C++ lo estoy poniendo ahí, además de otras nuevas cosas que espero sirvan en un futuro.

Espero nos sigas visitando, y espero seguir contando con tu presencia en nuestra nueva dirección:

Mi sitio

Programación en C++

El equivalente a este blog

Un tutorial de C++ que estoy preparando.


Saludos,
Fernando.

viernes 14 de noviembre de 2008

Cómo detectar fugas de memoria en Visual C++

Fugas de memoria (memory leaks), cosas horribles del mundo, uno de los más grandes enemigos a los que el programador de C++ se puede enfrentar.

Todos las conocemos, ¿verdad? Ubicas memoria dinámica y olvidas desubicarla:


int main()
{
int* p = new int();

return 0;
// no se elimina p con un delete, así que
// aquí hay una fuga de memoria
}


Obvio el caso anterior difícilmente ocurriría en la vida real. Era un ejemplo simple. En esa vida real cosas mucho más complicadas pueden ocurrir. Y encontrar en dónde se encuentra la fuga es una tarea titánica.

Visual C++ provée algunas herramientas para poder detectar fugas de memoria. Cuando corremos nuestro programa en modo de depuración.

En primer lugar, cuando trabajamos con ATL o MFC, tenemos una macro llamada DEBUG_NEW que sirve para dar información sobre el archivo y línea donde se invoca la creación dinámica. Para ello tendríamos que hacer algo como:


#ifdef _DEBUG
#define new DEBUG_NEW
#endif


e incluirlo en todos los archivos. DEBUG_NEW lleva un registro de todos los objetos creados de forma dinámica, con el nombre del archivo y la línea de donde fueron instanciados. Cuando el programa termina, se llama al Object Dump de todos los objetos (en el caso de aquellos que deriven de CObject, se manda a la versión de CObject::Dump), lo que hace que se muestre información en la ventana de "Output" del IDE de Visual C++. Cuando hay un memory leak, así se presentará.



En la figura anterior les pongo un ejemplo. Corrí mi programa y al cerrarlo me detectó una fuga de memoria. En el output podemos ver los "dumps" o volcado de memoria que hace el IDE (en mi caso, Visual C++ 9). En particular, notamos que el primer volcado me lo redirecciona al archivo lookupoptions.cpp. Esto es gracias a que emplée DEBUG_NEW como indiqué anteriormente. Por ello, al hacer doble click me lleva directamente a donde se ubicó el elemento.

Por supuesto el saber dónde se generó el objeto es una parte del problema. Pero no siempre resulta obvio. Por ejemplo, en la figura anterior se crea un objeto con new y se agrega a una colección. Ésta se usa a lo largo de la existencia de una ventana hija y posteriormente la colección se encarga de eliminar todos los objetos que contenga. Por lo que la eliminación del objeto está en otra parte del código, posiblemente sin estar relacionada. Si el problema no es ubicar el objeto en memoria, sino desubicarlo.

Para poder detectar esto, normalmente tenemos que saber bajo qué condiciones se está creando nuestro objeto. En el ejemplo anterior, el IDE marca la clase que genera el objeto, pero no marca bajo qué condición se crea (i.e. qué otro objeto mandó crear la colección antes mencionada). ¿Cómo podríamos saberlo? Un breakpoint sería buena opción. Pero mejor aún, un breakpoint bajo las condiciones en las que se da la fuga. Pues bien, MFC / Visual C++ provée un mecanismo para hacer esto.

Cada objeto que se crea de forma dinámica tiene un ID, que representa su número de creación. Cuando se hace el volcado de la memoria para detectar la fuga, se imprime este ID entre llaves. En la figura anterior se muestran encerradas en un círculo rojo. En este caso, el objeto fue el 27620° en ser creado. Por otro lado, existe una función, _CrtSetBreakAlloc que toma como parámetro un número que es precísamente el número de objeto creado (en el caso del ejemplo anterior, 27620). Esta función hace que cuando se vaya a crear el objeto cuyo número es el del parámetro (i.e. 27620) se mande llamar a AfxDebugBreak, lo cuál actúa como un breakpoint. En ese momento, podemos revisar el stack y demás variables para saber cuál es el entorno y contexto de la creación de mi objeto, facilitando la caza del bug.

Luego entonces, basta mandar llamar a _CrtSetBreakAlloc al mero inicio del programa (main si estamos en consola, WinMain si es una aplicación para Windows, y CWinApp::InitInstance si trabajamos con MFC.

Me pareció interesante, ya que recientemente tuve este problema y pude resolverlo de forma fácil así. MSDN tiene éste artículo que viene con técnicas más avanzadas para detectar fugas de memoria, y éste artículo que habla sobre lo que les acabo de comentar.

Espero esto ayude a eliminar esas cosas horribles llamadas Fugas de Memoria.

jueves 31 de julio de 2008

Herencia virtual

OK, todos sabemos lo poderosa que es la herencia. Hasta aquí, nada nuevo. Sabemos también que C++ provée herencia múltiple. Algunos lenguajes autollamados "modernos" excluyen la herencia múltiple por considerar que su empleo nos puede llevar a muchos errores. Aunque esto sea cierto, el chiste está en capacitar mejor a los programadores en lugar de tratarlos como niños y quitarles expresividad a los programas.

En fin, qué se le va a hacer. Uno de los argumentos más empleados es el siguiente. Consideremos la clase A. Ahora supongamos que tenemos una clase B que deriva de A, y una clase C que deriva también de A. Luego, supongamos que tenemos una clase D que deriva tanto de A como de B. ¿Ven cuál es el llamado problema? ¿No es claro? Veámoslo en código.


class A
{
public:
virtual string Foo() { return "A::Foo"; }
};

class B : public A
{
public:
virtual string Foo() { return "B::Foo"; }
};

class C : public A
{
public:
virtual string Foo() { return "C::Foo"; }
};

class D : public B, public C
{
};

int main()
{
D d;
d.Foo();

return EXIT_SUCCESS;
}


En el ejemplo anterior, desde la perspectiva de D, existen dos versiones de Foo, la de la clase B y la de la clase C. En la función main, ¿a qué versión deberá llamar D, a la versión de B o la versión de C? Un problema comunmente llamado "del diamante".

Por supuesto que este problema es soluble, y C++ proporciona un método para resolverlo llamado Herencia Virtual. Sin embargo, el lector versado podría decir: "en cualquier caso, el tener una clase como la D anterior implica que hay un mal diseño". Y el mismo lector tendría la boca llena de razón.

Veamos pués un ejemplo más común. Supongamos que yo quiero que una clase defina un comportamiento en particular. No me interesa lo que haga o cómo lo haga, solo quiero que tenga el comportamiento que quiero. En otras palabras, quiero que una clase tenga ciertos métodos con una firma determinada, pero no quiero hacer una implementación. Este tipo de diseño es común cuando queremos forzar a un objeto a que se ciña a un "contrato" en particular, y es lo que en lenguajes modernos llamamos interfaz. Una interfaz, pués, no es otra cosa que una clase que no tiene variables miembro y que todos los métodos que tiene son virtualmente puros. Algo como:


class ISumable
{
public:
virtual ~ISumable() { }

virtual int Sumar(int a, int b) = 0;
virtual int Restar(int a, int b) = 0;
};

class OperacionesSimples : public ISumable
{
public:
virtual int Sumar(int a, int b) { return a + b; }
virtual int Restar(int a, int b) { return Sumar(a, -b); }
};


Declaramos una interfaz ISumable que nos garantiza que la clase que la implemente va a poder realizar operaciones aritméticas triviales como la suma y la resta. De esta forma, yo podría crear una función genérica que opere sobre ésta y haga alguna transformación, digamos:


void SumarDiezDígitos(ISumable* pSumable)
{
int valor;

assert(pSumable != NULL);
valor = 0;

for (int i = 0; i < 10; i++)
{
valor = pSumable->(i, valor);
}

cout << "El valor final es: " << valor << endl;
}


No me importa cómo haga la suma, mientras me diga que va a sumar. Así, tenemos una primera clase, OperacionesSimples que va a implementar esta interfaz. Ahora supongamos que queremos tener otra interfaz que nos permita realizar sumas, restas, multiplicaciones y divisiones. Se vería como:


class IAritmetica
{
public:
virtual ~IAritmetica();

virtual int Suma(int a, int b) = 0;
virtual int Resta(int a, int b) = 0;
virtual int Multiplicacion(int a, int b) = 0;
virtual int Division(int a, int b) = 0;
};


Y queremos crear una clase que implemente dicha interfaz, pero para reutilizar código, derivamos de nuestra clase OperacionesSimples. Entonces tendríamos:


class OperacionesAritmeticas : public OperacionesSimples, public IAritmetica
{
public:
virtual int Multiplicar(int a, int b) { return a * b; }
virtual int Dividir(int a, int b) { return b != 0 ? a / b : 0; }
};


Lo anterior no compilará porque tanto IAritmetica como OperacionesSimples definen dos métidos iguales: Sumar y Restar. ¿Qué podemos hacer ante esto? Evidentemente, la solución a ambos problemas reside en la herencia virtual.

La herencia virtual en esencia es un mecanismo a través del cuál le decimos al compilador que herede de una clase pero que haga los métodos heredados virtualmente puros. En consecuencia, tendremos que implementarlos aun si la clase base los implementa. Para hacer esto, empleamos la palabra reservada virtual al heredar de nuestra clase. La resolución, pues, del primer problema planteado quedaría como sigue.


class A
{
public:
virtual string Foo() { return "A::Foo"; }
};

class B : public A
{
public:
virtual string Foo() { return "B::Foo"; }
};

class C : public A
{
public:
virtual string Foo() { return "C::Foo"; }
};

class D : public virtual B, public virtual C
{
public:
virtual string Foo() { return B::Foo(); }
};

int main()
{
D d;
d.Foo();

return EXIT_SUCCESS;
}


Ahora sí no habrá problema de compilación, ya que heredamos D virtualmente tanto de B como de C. Por supuesto, tuvimos que implementar el método para que explícitamente decida cuál versión llamar, o bien escriba una versión propia del método.

La resolución del segundo problema quedaría como sigue.


class OperacionesSimples : public virtual ISumable
{
public:
virtual int Sumar(int a, int b) { return a + b; }
virtual int Restar(int a, int b) { return Sumar(a, -b); }
};

class OperacionesAritmeticas : public OperacionesSimples, public virtual IAritmetica
{
public:
virtual int Sumar(int a, int b) { return OperacionesSipmles::Sumar(a, b); }
virtual int Restar(int a, int b) { return OperacionesSimples::Restar(a, b); }
virtual int Multiplicar(int a, int b) { return a * b; }
virtual int Dividir(int a, int b) { return b != 0 ? a / b : 0; }
};


Y voilà, se acabó el problema. La herencia virtual es bastante útil sobre todo cuando diseñamos e implementamos interfases. De hecho es recomendable que todas nuestas interfases las heredemos de forma virtual, para evitarnos problemas relacionados con la herencia múltiple, la cuál como se ha demostrado, solo genera problemas cuando no se conoce el lenguaje.

martes 15 de julio de 2008

CMFCListView: CListView mejorado

Con la liberación del Feature Pack para Visual C++, se añadieron muchos nuevos controles (como el Ribbon o los Docking Windows), y también se mejoraron algunos controles (como CMFCButton que mejora a CButton, y CMFCListCtrl que mejora a CListCtrl). Éste último control de verdad que mejora bastante con respecto a los anteriores. Así pués, decidí emplearlo en una aplicación que estoy desarrollando, bajo la arquitectura documento / vista de MFC.

Primero, pensé que podría subclasear al CListCtrl de CListView, pero no se puede porque en esencia, CListView *es* CListCtrl. Es decir, la vista no tiene un control directamente asociado. Después pensé en derivar del padre de CListView, CCtrlView, pero también ví que era imposible debido a que el mecanismo es crear un control dado el nombre de la clase (y después de emplear el Spy++ descubrí que el nombre de CMFCListCtrl es SysListView32, el mismo que para CListCtrl.

Así las cosas, decidí entonces crear mi propia vista, derivando de CView. No es muy difícil, solo hay que crear el control CMFCListCtrl y hacerlo del tamaño de la ventana capturando el mensaje WM_SIZE. Sin más preámbulos, aquí está el código. Este es el archivo de encabezado (que yo llamé mfclistview.h).


#pragma once

class CMFCListView : public CView
{
DECLARE_DYNCREATE(CMFCListView);
DECLARE_MESSAGE_MAP();

public:
CMFCListView();
virtual ~CMFCListView();

CListCtrl& GetListCtrl();

protected:
virtual int OnCreate(LPCREATESTRUCT lpcs);
virtual void OnSize(UINT nType, int cx, int cy);
virtual void OnDraw(CDC* pDC);

virtual void InitList();

private:
CMFCListCtrl m_wndListCtrl;
};


En mi caso concreto, necesitaba incluir el "stdafx.h", archivo precompilado. De aquí, lo importante es notar los siguientes encabezados:


#include <afxwin.h>
#include <afxext.h>
#include <afxcmn.h>
#include <afxcview.h>


Y finalmente, el archivo de implementación (en mi caso, mfclistview.cpp).


#include "stdafx.h"
#include "mfclistview.h"

IMPLEMENT_DYNCREATE(CMFCListView, CView);

BEGIN_MESSAGE_MAP(CMFCListView, CView)
ON_WM_CREATE()
ON_WM_SIZE()
END_MESSAGE_MAP();

CMFCListView::CMFCListView()
: CView()
{
}

CMFCListView::~CMFCListView()
{
}

int CMFCListView::OnCreate(LPCREATESTRUCT lpcs)
{
DWORD dwStyle;
BOOL bResult;

bResult = CView::OnCreate(lpcs) == -1;
if (bResult == -1) return bResult;

// aquí puedes modificar los estilos normales de CListCtrl y los
// extendidos de CMFCListCtrl.
dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | LVS_REPORT;
// simplemente creamos nuestro control asociado a la vista
bResult = m_wndListCtrl.Create(dwStyle, CRect(0, 0, 0, 0), this, 1);

// la idea de llamar a InitList es que puedas inicializar el control,
// digamos, añadir las columnas a mostrar, y las imágenes de las filas, etc.
InitList();

return bResult ? 0 : -1;
}

void CMFCListView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);

// esta es la otra parte importante del código. cuando la vista cambie de
// tamaño, hacemos que el control ocupe todo el ancho de la misma.
if (::IsWindow(m_wndListCtrl))
m_wndListCtrl.MoveWindow(0, 0, cx, cy, TRUE);
}

void CMFCListView::OnDraw(CDC* pDC)
{
// este método es virtualmente puro en CView, por lo que hay que
// implementarlo.
}

void CMFCListView::InitList()
{
// puedes poner aquí la inicialización del control, o mejor aún, derivar tu
// propia vista de CMFCListView y sobreescribir este método.
}

CListCtrl& CMFCListView::GetListCtrl()
{
// al estilo de CListView, podemos acceder directamente al control.
// el mecanismo es diferente, pero el resultado es el mismo. esto,
// con la finalidad de que se pueda reemplazar CListView por
// CMFCListView con la menor cantidad de problemas. Nota que regresamos
// una referenca a CListCtrl (padre de CMFCListCtrl) en lugar de la misma
// CMFCListCtrl. esto, para que no tengas que cambiar tu código existente.
// si deseas emplear métodos de CMFCListCtrl, pues haz un dynamic_cast
// a CMFClistCtrl& y listo. O cambia el código que muestro aquí.
return m_wndListCtrl;
}


Y eso es todo. Así se ve la vista (sin nada más que el código que les muestro) en mi proyecto.



Comentarios y mejoras, son bienvenidos.

lunes 9 de junio de 2008

Constructores y métodos virtuales

Hace algunas lunas hice una entrada sobre constructores y destructores. Ahora voy a describir un comportamiento que fue inesperado para mí, pero que tiene toda la lógica del mundo. Hélo aquí.

Yo tenía una clase base con un constructor copia, y la clase derivada también con constructor copia. Ambas clases tenían sobrecargadas el operador de asignación. Entonces tenía algo así:


class Base
{
public:
int _id;
string _created;
string _deleted;

Base(const Base& base) {
_id = base._id;
_created = base._created;
_deleted = base._deleted;
}

virtual ~Base() { }

virtual Base& operator= (const Base& base) {
__id = base._id;
_created = base._created;
_deleted = base._deleted;
return *this;
}

...etc...
};

class Derivada : public Base
{
public:
string _nombre;
string _apellido;
string _direccion;

Derivada(const Derivada& derivada)
: Base(derivada)
{
_nombre = derivada._nombre;
_apellido = derivada._apellido;
_direccion = derivada._direccion;
}

virtual ~Derivada() { }

virtual Derivada& operator=(const Derivada& derivada) {
Base::operator= (derivada);
_nombre = derivada._nombre;
_apellido = derivada._apellido;
_direccion = derivada._direccion;
return *this;
}

...etc...
};


Aquí podemos ver dos problemas. El primero, es que hay que escribir mucho. El segundo, es que la forma en la que mandamos llamar la versión padre del operador de asignación (en negritas) es horrible. Por supuesto, para resolver el primer caso bastaría cambiar el constructor a:


Base(const Base& base)
{
*this = base;
}


y así, y con eso reduciríamos el código. Pues bien, el caso es que para evitar escribir tanto (eran como veinte clases similares, dentro de este modelo, se me ocurrió crear una función virtual, digamos Copiar, que haga esto. De tal suerte que ya solo habría que sobreescribir esta función y listo, además de que habría una forma aparte del constructor copia y operador de asignación para llevar a cabo esto. Entonces todo quedaba así:



class Base
{
public:
int _id;
string _created;
string _deleted;

Base(const Base& base) {
Copiar(base); // 1
}

virtual ~Base() { }

virtual Base& operator= (const Base& base) {
Copiar(base);
return *this;
}

virtual void Copiar(const Base& base) {
_id = base._id;
_created = base._created;
_deleted = base._deleted;
}

...etc...
};

class Derivada : public Base
{
public:
string _nombre;
string _apellido;
string _direccion;

Derivada(const Derivada& derivada)
: Base(derivada) // 2
{
}

virtual ~Derivada() { }

virtual void Copiar(const Derivada& derivada) {
Base::Copiar(derivada);
_nombre = derivada._nombre;
_direccion = derivada._direccion;
_apellido = derivada._apellido;
}

...etc...
};


¿Cómo se suponía que funciona esto? Bueno, el constructor base (nota 1) manda llamar a Copiar, igual que el operador de asignación, en la clase base. En la clase derivada sobreescribimos al método Copiar. Por ende, en la clase derivada el constructor simplemente deja que la base se haga cargo, ya que ésta mandará llamar a Copiar y, como es virtual, se mandará llamar la versión del hijo. Además, ahora no se necesita sobrecargar el operador de asignación, porque el de la clase padre es suficiente. Sin embargo, algo falló. Éste código no funcionó.


Derivada d1;
d1._id = 1;
d1._created = "06/06/2008";
d1._nombre = "fernando";
d1._apellido = "gomez";

Derivada d2(d1);

cout << d1._id << d1._nombre << " " << d1._apellido << endl;
cout << d2._id << d2._nombre << " " << d2._apellido << endl;


El código anterior muestra el siguiente resultado:


1fernando gomez
2


Es decir, d1 muestra los datos correctos (obvio) pero la copia, d2, no muestra bien más que el id. Tras depurar, me dí cuenta que Derivada no mandaba llamar a la versión Copiar de Derivada (Derivada::Copiar) sino... ¡a la base! Es decir: Base::Copiar. Por eso solo se copiaban los miembros de Base, porque nunca se mandaba llamar a la versión de Derivada.

Pero... Copiar es virtual, ¿no? ¿No al sobreescribirse debería entonces tomar la versión derivada? ¿Es acaso un error esto?

Por supuesto que no. La respuesta es sencilla: Así es como debe funcionar. ¿Por qué? Porque cuando se crea el objeto Base (y por ende se llama al constructor de Base), todavía no se ha creado la parte correspondiente a Derivada, y por lo tanto, no existe la versión Copiar de Derivada. Una vez que sale del constructor, se crea la parte de Derivada y entonces sí, al mandar llamar a Copiar se hará polimórficamente (como en el caso de la sobrecarga del operador de asignación, que sí lo hace bien). Esto tiene todo el sentido del mundo, ¿no? Sí, lo sé, debí haberlo sabido. Bueno, ya lo sabes entonces, al igual que yo.

Así pués, la solución fue que el constructor siempre mande llamar a Copiar en lugar de dejar que la clase padre lo haga. Conclusión: tengan cuidado cuando un constructor manda llamar a métodos virtuales.