Alocação Dinâmica de Memória e Conversões de Tipo em C++

Alocação Dinâmica de Memória

Em programas C++, as necessidades de memória são frequentemente determinadas antes da execução, mas em alguns casos, como quando dependem de entrada do usuário, a alocação precisa ocorrer em tempo de execução. Para isso, C++ fornece os operadores new e delete.

Operadores new e new[]

O operador new aloca memória dinamicamente. Sua sintaxe é:

ponteiro = new tipo
ponteiro = new tipo [quantidade]

A primeira expressão aloca memória para um único elemento, enquanto a segunda aloca um bloco (array). Por exemplo:

int * dados;
dados = new int [10];

Isso aloca espaço para 10 inteiros e atribui o endereço inicial a dados. Os elementos podem ser acessados via dados[0], dados[1], etc.

A principal diferença em relação a arrays estáticos é que o tamanho para new pode ser uma variável, determinada em tempo de execução. A memória é alocada do heap, e como os recursos são limitados, falhas podem ocorrer.

C++ oferece duas formas de verificar alocações: tratamento de exceções (lançando bad_alloc em caso de falha) ou o mecanismo nothrow, que retorna um ponteiro nulo. Exemplo com nothrow:

#include <new>
int * memoria;
memoria = new (nothrow) int [8];
if (memoria == nullptr) {
  // Tratar erro de alocação
}

O mecanismo de exceção é geralmente preferido por eficiência, mas nothrow é mais simples para demonstrações.

Operadores delete e delete[]

Para liberar memória alocada com new, use delete para elementos únicos ou delete[] para arrays. Exemplo:

int * buffer;
buffer = new (nothrow) int [5];
if (buffer != nullptr) {
  // Usar buffer
  delete[] buffer;
}

Passar um ponteiro nulo para delete não tem efeito. Um exemplo completo:

#include <iostream>
#include <new>
using namespace std;

int main() {
  int n, i;
  int * valores;
  cout << "Quantidade de valores: ";
  cin >> n;
  valores = new (nothrow) int[n];
  if (valores == nullptr) {
    cout << "Erro: falha na alocação de memória";
  } else {
    for (i = 0; i < n; i++) {
      cout << "Digite um valor: ";
      cin >> valores[i];
    }
    cout << "Valores digitados: ";
    for (i = 0; i < n; i++) {
      cout << valores[i] << ", ";
    }
    delete[] valores;
  }
  return 0;
}

É importante tratar falhas de alocação, seja com exceções ou verificação de ponteiro nulo.

Alocação Dinâmica em C

C++ também suporta funções C como malloc, calloc, realloc e free (do cabeçalho <cstdlib>). No entanto, memória alocada com new não deve ser misturada com essas funções.

Conversões de Tipo

Conversão Implícita

Conversões implícitas ocorrem automaticamente ao copiar valores para tipos compatíveis. Exemplo:

<short>
short a = 3000;
int b;
b = a; // Promoção de short para int
</short>

Conversões entre tipos numéricos podem causar perda de precisão ou comportamento indefinido, como truncamento de floats para inteiros ou overflow.

Para classes, conversões implícitas podem ser controladas por construtores de um argumento, operadores de atribuição e operadores de conversão de tipo.

Palavra-chave explicit

A palavra-chave explicit previne conversões implícitas em construtores e operadores de conversão. Exemplo:

class Origem {};
class Destino {
public:
  explicit Destino(const Origem& obj) {}
};

void funcao(Destino param) {}

int main() {
  Origem origem;
  Destino destino(origem); // OK
  // funcao(origem); // Erro: conversão explícita necessária
  funcao(destino);   // OK
}

Conversão de Tipo (Casting)

C++ fornece operadores de casting específicos para maior segurança:

  • dynamic_cast: Usado com ponteiros ou referências para classes polimórficas, verifica em tempo de execuição se a conversão é válida.
  • static_cast: Realiza conversões entre tipos relacionados, incluindo upcasts e downcasts, sem verificação em tempo de execução.
  • reinterpret_cast: Converte qualquer tipo de ponteiro para outro, reinterpretando a representação binária, sendo perigoso e não portável.
  • const_cast: Modifica o qualificador const ou volatile de um ponteiro ou referência.

Exemplo com dynamic_cast:

#include <iostream>
using namespace std;

class Base { virtual void metodo() {} };
class Derivada : public Base {};

int main() {
  Base* ptrBase = new Derivada;
  Derivada* ptrDerivada = dynamic_cast<Derivada*>(ptrBase);
  if (ptrDerivada) {
    cout << "Conversão bem-sucedida";
  } else {
    cout << "Falha na conversão";
  }
  delete ptrBase;
}

Exemplo com static_cast:

double valor = 9.81;
int inteiro = static_cast<int>(valor); // Truncamento

Operador typeid

O operador typeid retorna informações sobre o tipo de uma expressão, útil com RTTI para classes polimórficas.

#include <iostream>
#include <typeinfo>
using namespace std;

class ClasseBase { virtual void v() {} };
class ClasseDerivada : public ClasseBase {};

int main() {
  ClasseBase* obj = new ClasseDerivada;
  cout << "Tipo do ponteiro: " << typeid(obj).name() << endl;
  cout << "Tipo do objeto: " << typeid(*obj).name() << endl;
  delete obj;
}

A saída depende da implementação do compilador.

Tags: C++ Alocação Dinâmica operadores new e delete conversões de tipo dynamic_cast

Publicado em 7-5 06:15