paulo1205
(usa Ubuntu)
Enviado em 22/02/2018 - 01:24h
Em C:
void *pv=(void *)0x12345678;
char *pc=(char *)0xFEDCBA9876543210;
Na verdade, como em C um ponteiro para void pode ser automaticamente convertido em qualquer outro tipo de ponteiro, você pode optar por usar sempre
void * para converter o endereço.
int *pi=(void *)0x1111111111111111;
double *pd=(char *)0xCCCCCCCCCCCCCCCC;
Em C++, há mais de uma maneira, e isso implica algumas explicações.
Em princípio, possivelmente o jeito do C indicando o tipo duas vezes (porque em C++ não existe conversão automática partindo de
void * para ponteiros de outros tipos) funcionaria, mas conversões de tipo ao estilo de C são consideradas mau estilo (e em alguns casos podem inferir uma conversão errada). A conversão de valor inteiro para ponteiro precisa ser feita, portanto, com uma conversão
reinterpret_cast .
// Sendo explícito quanto ao tipo na declaração e na atribuição:
int *pi=reinterpret_cast<int *>(0x01234567);
// Sendo explícito quanto ao tipo na declaração e forçando o mesmo tipo na atribuição, mas sem repetir explicitamente:
char *pc=reinterpret_cast<decltype(pc)>(0x01234567);
// Fazendo o compilador inferir o tipo a partir do tipo do(s) dado(s) apontado(s).
auto *dyn_int_array=reinterpret_cast<int *>(0xFEDCBA9876543210); // Note que não há como distinguir entre um ponteiro para
// um único elemento e um que aponte para um array dinâmico.
Ao trabalhar com OO e tipos criados pelo usuário, algumas vezes pode ser interessante chamar os construtores dos objetos dentro da região alocada. Para tanto, a notação de
placement new , que é uma versão sobrecarregada do operador
new para criar objetos em regiões de memória previamente alocadas de alguma outra maneira, informadas através de um ponteiro para
void .
#include <new> // Necessário para ter acesso a versões sobrecarregadas padronizadas do operador new.
// Sendo explícito duas vezes quanto ao tipo.
float *pf=new(reinterpret_cast<void *>(0xBEEF15F00D) float; // Para tipo nativo, sem chamar construtor default.
// Usando dedução de tipo a partir do tipo do dado apontado.
auto *pu=new(reinterpret_cast<void *>(0x1EA7F00D) unsigned(); // Para tipo nativo, chamando "construtor" default (atribui valor zero ao elemento).
auto *pn=new(reinterpret_cast<void *>(0x1A7EBEEF) double(3.14159265); // Para tipo nativo, chamado "construtor" de cópia.
struct tipo_X {
tipo_X(){ /* ... */ }
virtual ~tipo_X(){ /* ... */ }
};
class tipo_Y: public tipo_X {
tipo_Y() { /* ... */ }
tipo_Y(int i, char c){ /* ... */ }
~tipo_Y(){ /* ... */ }
};
// Sendo explícito duas vezes quanto ao tipo:
tipo_X *pX=new(reinterpret_cast<void *>(0xDEADBEEF)) tipo_X; // Chama o construtor default do dado do tipo tipo_X usando a região de
// memória indicada para conter o objeto.
// Fazendo o compilador inferir o tipo a partir do tipo do(s) dado(s) apontado(s).
auto *dyn_Y_array=new(reinterpret_cast<void *>(0xA11FED0FBEEF)) tipo_Y[500]; // Array com 500 elementos do tipo tipo_Y, chamando construtor
// default para cada elemento.
// Posso usar polimorfismo, também (ponteiro para classe base aponta para objeto de classe derivada).
tipo_X *pXd=new(reinterpret_cast<void *>(0xDEAFEE1) tipo_Y(1, 'a'); // Chama construtor arbitrário.
Em tempo: não cabe chamar
delete (ou
delete [] ) em áreas alocadas com
placement new (ao menos não com a versão padrão), pois ela só faz a criação dos objetos, não uma alocação de fato. Se você precisar destruir os objetos por elas alocados, esse será uma daqueles raros casos em que cabe chamar o destrutor explicitamente (e.g. “
pXd->~tipo_X(); ”, que, por ser virtual, e por estar ligado a um objeto derivado no exemplo acima, var chamar realmente o destrutor de
tipo_Y ).