Manejo de memoria en C++

Samsung Research Tijuana
Tipos de memoria en C++
Memoria estática (Stack)
Los objetos en memoria estática son destruidos automáticamente al finalizar el bloque donde fueron declarados.
Memoria dinámica (Heap)
Los objetos en memoria dinámica continúan con vida hasta que sean destruidos explícitamente, esto permite que salgan del bloque en el que fueron declarados.
Ejemplo: memoria estática
#include <iostream>
int c = 1; /* Global */
int main()
{
int a = 1; /* Bloque */
int b = 1;
{
int a = 1; /* Segunda declaración de 'a' oculta la primera declaración. */
std::cout << a++ << std::endl; /* 1 */
b += a;
c = 3;
}
++a;
++c;
std::cout << a << std::endl; /* 2 */
std::cout << b << std::endl; /* 3 */
std::cout << c << std::endl; /* 4 */
}Apuntadores
Declaración
La sintaxis para declarar una variable apuntador es:
T *nombre= valor;
Donde:
T: El tipo del dato al que se apunta.
nombre: El nombre de la variable apuntador.
valor: El valor inicial del apuntador (opcional).
int *intPointer = nullptr;Apuntadores
Operadores
Operador * (Indirección)
Regresa una referencia al contenido del apuntador.
*intPointer = 5;
int number = *intPointer;Operador -> (Acceso a miembro)
Acceso al miembro de una clase o estructura contenida por un apuntador. Exactamente igual a utilizar "(*T).<miembro de T>"
std::string *name = getName();
int nameLenght = name->size();Apuntadores
Operadores
Operador & (Dirección de)
Regresa la dirección de memoria del objeto al cual se le aplique el operador en forma de un apuntador.
std::string name = "Victor";
std::string *namePtr = &name;Apuntadores
Operadores
Operador new
Este operador crea un nuevo objeto en memoria dinámica y regresa un apuntador al objeto creado.
double *doublePointer = new double(10.0);Operador delete
Destruye un objeto en memoria dinámica y libera el espacio de memoria que el objeto ocupaba.
delete doublePointer;Ejemplos
int main()
{
int i = 5; /* Objeto en memoria estática. */
int *ptr1; /* Apuntador no inicializado. */
int *ptr2 = nullptr; /* Apuntador nulo. */
int *ptr3 = &i; /* Apuntador a la dirección de i. */
int *ptr4 = new int(10); /* Apuntador a un espacio de memoria dinámica. */
std::string *name = getName();
*ptr1 = name->size(); /* Error: Acceso de memoria invalido */
*ptr2 = name->size(); /* Error: ptr1 es nulo */
*ptr3 = name->size(); /* Ok, modifica el valor de i */
*ptr4 = name->size(); /* Ok, modifica el valor del objeto en memoria dinámica. */
delete ptr4; /* Objetos en memoria dinámica se borran explícitamente. */
delete name;
return 0;
}Ejemplo: memoria dinámica
#include <string>
class Cellphone
{
public:
Cellphone(std::string brand)
: mBrand(brand)
{}
std::string brand() const
{
return mBrand;
}
private:
std::string mBrand;
};
#include <iostream>
#include "Cellphone.h"
Cellphone* badPhoneFactory(std::string brand)
{
Cellphone p(brand);
return &p;
}
Cellphone* phoneFactory(std::string brand)
{
Cellphone *p = new Cellphone(brand);
return p;
}
int main()
{
Cellphone *samsung = phoneFactory("Samsung");
Cellphone *apple = badPhoneFactory("Apple");
std::cout << samsung->brand() << std::endl;
std::cout << apple->brand() << std::endl; /* Error */
delete samsung;
delete apple;
}Cellphone.h
main.cpp
RAII
RAII (Resource Acquisition Is Initialization)
Un objeto debe reservar sus recursos durante la inicialización (Constructor) y liberarlos durante su destrucción (Destructor).
class XmlParser
{
public:
XmlParser(const std::string &xmlFilePath)
: xmlFile(new File(xmlFilePath)),
contents(new ByteArray())
{
if (xmlFile->open(File::ReadOnly))
{
contents = xmlFile->readAllBytes();
}
}
~XmlParser()
{
if (xmlFile != nullptr && xmlFile->isOpen())
{
xmlFile->close();
}
delete xmlFile;
delete contents;
}
XmlObject parse() { /* Hacer algo con el contenido. */ }
private:
File *xmlFile;
ByteArray *contents;
}RAII
class XmlParser
{
public:
XmlParser(const std::string &xmlFilePath)
: xmlFile(new File(xmlFilePath)),
contents(new ByteArray())
{
if (xmlFile->open(File::ReadOnly))
{
contents = xmlFile->readAllBytes();
}
}
~XmlParser()
{
if (xmlFile->isOpen())
{
xmlFile->close();
}
delete xmlFile;
delete contents;
}
XmlObject parse() { /* Hacer algo con el contenido. Puede causar excepción. */ }
private:
File *xmlFile;
ByteArray *contents;
}Contenedor de apuntador
template <typename T>
class PointerWrapper
{
public:
PointerWrapper(T *pointer)
: pointer(pointer)
{}
~PointerWrapper()
{
delete pointer;
}
T& operator*()
{
return *pointer;
}
T* operator->() { /* Implementacion */ }
T& operator=() { /* Implementacion */ }
private:
T *pointer;
}Uso del contenedor
class XmlParser
{
public:
XmlParser(const std::string &xmlFilePath)
: xmlFile(new File(xmlFilePath)),
contents(new ByteArray())
{
if (xmlFile->open(File::ReadOnly))
{
contents = xmlFile->readAllBytes();
}
}
~XmlParser()
{
if (xmlFile->isOpen())
{
xmlFile->close();
}
}
XmlObject parse() { /* Hacer algo con el contenido. Puede causar excepción. */ }
private:
PointerWrapper<File> xmlFile;
PointerWrapper<ByteArray> contents;
}STL Smart Pointers
Un smart pointer es un contenedor para un apuntador regular que se encarga de eliminar automáticamente el objeto dinámico y liberar la memoria ocupada por el apuntador. Este tipo de apuntadores fueron introducidos en C++11.
std::unique_ptr
Es un apuntador que tiene el control del objeto al que apunta, cuando se destruye la instancia de std::unique_ptr también se destruye el objeto al que apunta. Por lo tanto solo puede haber una sola instancia apuntando al mismo objeto.
void parseXml(const std::string &xmlFilePath)
{
XmlParser *parser = getParser();
try
{
parser->parse();
delete parser;
}
catch (Exception &e)
{
std::cout << e.what();
delete parser;
}
}void parseXml(const std::string &xmlFilePath)
{
std::unique_ptr<XmlParser> parser(getParser());
try
{
parser->parse();
}
catch (Exception &e)
{
std::cout << e.what();
}
}std::shared_ptr
Este apuntador mantiene un conteo de instancias, el objeto en memoria dinámica se destruye cuando el conteo de instancias llega a cero. Por lo tanto es posible tener muchas instancias de std::shared_ptr apuntando al mismo espacio de memoria.
Ejemplo: std::shared_ptr
#include <string>
class Cellphone
{
public:
Cellphone(std::string brand)
: mBrand(brand)
{}
std::string brand() const
{
return mBrand;
}
private:
std::string mBrand;
};
#include <iostream>
#include <memory>
#include "Cellphone.h"
std::shared_ptr<Cellphone> phoneFactory(std::string brand)
{
std::shared_ptr<Cellphone> p =
make_shared<Cellphone>(brand);
return p;
}
int main()
{
auto samsung = phoneFactory("Samsung");
std::cout << samsung->brand() << std::endl;
}Cellphone.h
main.cpp
Manejo de memoria en C++
By Victor Romero
Manejo de memoria en C++
Conceptos de manejo de memoria estática y dinámica en C++
- 3,603