Pergunta

Suponha que eu tenho o seguinte código:

void* my_alloc (size_t size)
{
   return new char [size];
}

void my_free (void* ptr)
{
   delete [] ptr;
}

É seguro? Ou deve ptr ser convertido para char* antes da exclusão?

Foi útil?

Solução

Depende de "seguro". Ele geralmente vai funcionar porque a informação é armazenada junto com o ponteiro sobre a própria alocação, de modo que o desalocador pode devolvê-lo para o lugar certo. Neste sentido, é "seguro", enquanto o seu alocador utiliza tags de fronteira internos. (Muitos fazem.)

No entanto, como mencionado em outras respostas, a exclusão de um ponteiro void não chamará destruidores, que pode ser um problema. Nesse sentido, não é "seguro".

Não há nenhuma boa razão para fazer o que está fazendo da maneira que você está fazendo isso. Se você quer escrever suas próprias funções de desalocação, você pode usar modelos de função para gerar funções com o tipo correto. Uma boa razão para fazer isso é para gerar allocators piscina, que pode ser extremamente eficiente para tipos específicos.

Como mencionado em outras respostas, este é indefinido comportamento em C ++. Em geral, é bom evitar um comportamento indefinido, embora o tema em si é complexo e cheio de opiniões conflitantes.

Outras dicas

Apagar através de um ponteiro nulo é indefinido por padrão C ++ - ver secção 5.3.5 / 3:

Na primeira alternativa (apagar objeto), se o tipo estático do operando é diferente da sua dinâmica tipo, o tipo estático será uma base classe de tipo dinâmico do operando e o tipo estático terá um destrutor virtual ou os é um comportamento Indefinido. Na segunda alternativa (Matriz de exclusão), se o tipo dinâmico o objecto a ser suprimido difere seu tipo estático, o comportamento é indefinida.

E a sua nota de rodapé:

Isto implica que um objeto não pode ser excluídos usando um ponteiro de tipo void * porque não há objetos do tipo vazio

.

Não é uma boa idéia e não algo que você faria em C ++. Você está perdendo a sua informação tipo sem nenhum motivo.

Seu destrutor não será chamado sobre os objetos em sua matriz que você está excluindo quando você chamá-lo para os tipos não primitivos.

Você deve em vez substituir new / delete.

A exclusão do * vazio provavelmente libertar a sua memória corretamente por acaso, mas é errado, porque os resultados são indefinido.

Se por alguma razão desconhecida para mim que você precisa para armazenar o ponteiro em um * vazio, em seguida, liberá-lo, você deve usar malloc e livre.

A exclusão de um ponteiro nulo é perigoso porque destruidores não vai ser chamado no valor que ele realmente aponta para. Isso pode resultar em vazamentos de memória / recursos em seu aplicativo.

A pergunta não faz sentido. Sua confusão podem ser em parte devido ao povo linguagem descuidada usam frequentemente com delete:

Você usa delete para destruir um objeto que foi alocada dinamicamente. Não fazê-lo, você forma um expressão de exclusão com ponteiro para esse objeto . Você nunca "excluir um ponteiro". O que você realmente é "excluir um objeto que é identificado pelo seu endereço".

Agora vemos por que a pergunta não faz sentido: Um ponteiro nulo não é o "endereço de um objeto". É apenas um endereço, sem quaisquer semântica. É pode ter vindo do endereço de um objeto real, mas que a informação é perdida, porque foi codificada no tipo do ponteiro originais. A única maneira de restaurar um ponteiro de objeto é lançar a volta ponteiro nulo para um ponteiro de objeto (que requer o autor para saber o que os meios de ponteiro). void em si é um tipo incompleta e, portanto, nunca o tipo de um objecto, e um ponteiro nulo não pode nunca ser utilizada para identificar um objecto. (Os objetos são identificados conjuntamente pelo seu tipo e seu endereço.)

Por causa de char não tem lógica especial destructor. Isto não vai funcionar.

class foo
{
   ~foo() { printf("huzza"); }
}

main()
{
   foo * myFoo = new foo();
   delete ((void*)foo);
}

O d'ctor não são chamados.

Se você quiser usar void *, por que não usar apenas malloc / free? new / delete é mais de gestão de memória apenas. Basicamente, novas / chamadas excluir um construtor / destruidor e há mais coisas acontecendo. Se você usar apenas built-in tipos (como char *) e excluí-los através de void *, ele iria trabalhar, mas ainda não é recomendado. O resultado final é o uso malloc / free se você quiser usar void *. Caso contrário, você pode usar as funções de modelo para sua conveniência.

template<typename T>
T* my_alloc (size_t size)
{
   return new T [size];
}

template<typename T>
void my_free (T* ptr)
{
   delete [] ptr;
}

int main(void)
{
    char* pChar = my_alloc<char>(10);
    my_free(pChar);
}

Se você realmente deve fazer isso, por que não cortar o homem médio (o new e operadores delete) e chamar o operator new global e operator delete diretamente? (Claro, se você está tentando instrumento operadores new e delete, você realmente deve reimplementar operator new e operator delete.)

void* my_alloc (size_t size)
{
   return ::operator new(size);
}

void my_free (void* ptr)
{
   ::operator delete(ptr);
}

Note que malloc() ao contrário, operator new joga std::bad_alloc em caso de falha (ou chama o new_handler se um está registrado).

Um monte de gente já comentou dizendo que não, não é seguro para excluir um ponteiro nulo. Eu concordo com isso, mas eu também queria acrescentar que, se você estiver trabalhando com ponteiros void, a fim de alocar matrizes contíguos ou algo semelhante, que você pode fazer isso com new para que você vai ser capaz de usar delete com segurança (com , ahem, um pouco de trabalho extra). Isto é feito através da atribuição de um ponteiro nulo para a região de memória (chamado de 'Arena') e, em seguida, fornecendo o ponteiro para a arena para o novo. Veja esta seção no C ++ FAQ . Esta é uma abordagem comum para implementar pools de memória em C ++.

Não há praticamente nenhuma razão para fazer isso.

Em primeiro lugar, se você não sabe o tipo dos dados, e tudo que você sabe é que é void*, então você realmente deve ser apenas tratar esses dados como um typeless blob de dados binários (unsigned char*) e uso malloc / free para lidar com ele. Isso é necessário, por vezes, para coisas como dados de forma de onda e similares, em que você precisa para passar em torno de ponteiros void* a apis C. Isso é bom.

Se você do saber o tipo de dados (ou seja, tem um ctor / dtor), mas por algum motivo você acabou com um ponteiro void* (por qualquer motivo que você tem) então você realmente deve lançá-lo de volta para o tipo que você sabe que ele seja , e chamada delete nele.

Eu tenho usado * vazio, (tipos aka desconhecidos) na minha estrutura para enquanto na reflexão de código e outros feitos de ambiguidade, e até agora, não tive problemas (vazamento de memória, violações de acesso, etc.) de qualquer compiladores . Somente avisos devido à operação de estar fora do padrão.

É perfeitamente faz sentido excluir um desconhecido (void *). Apenas certifique-se o ponteiro segue estas diretrizes, ou pode parar de fazer sentido:

1) O ponteiro desconhecido não deve apontar para um tipo que tem uma deconstructor trivial, e assim, quando escalado como um ponteiro desconhecido que nunca deverão ser removidas. Apenas excluir o ponteiro desconhecido depois de lançar-lo de volta para o tipo original.

2) É a instância que está sendo referenciado como um ponteiro desconhecido na pilha ligado ou memória ligada pilha? Se as referências ponteiro desconhecidos uma instância na pilha, então ele deve nunca ser apagado!

3) Você é 100% positivo o ponteiro do desconhecido é uma região de memória válido? Não, em seguida, ele nunca deve ser DELTED!

Ao todo, há muito pouco trabalho direto que pode ser feito usando um desconhecido tipo de ponteiro (* void). No entanto, indiretamente, a * vazio é um grande trunfo para desenvolvedores C ++ que confiar em quando ambigüidade de dados é necessária.

Se você quer apenas um buffer, o uso malloc / free. Se você deve usar new / delete, considere uma classe wrapper trivial:

template<int size_ > struct size_buffer { 
  char data_[ size_]; 
  operator void*() { return (void*)&data_; }
};

typedef sized_buffer<100> OpaqueBuffer; // logical description of your sized buffer

OpaqueBuffer* ptr = new OpaqueBuffer();

delete ptr;

Para o caso particular de Char.

char é um tipo intrínseco que não tem um destruidor especial. Assim, os argumentos vazamentos é um discutível.

sizeof (char) é geralmente um modo que não há qualquer argumento alinhamento. No caso da plataforma rara onde o sizeof (char) não é um, eles alocar memória suficiente alinhado pelo seu car. Portanto, o argumento de alinhamento é também um discutível.

malloc / free seria mais rápida neste caso. Mas você perde std :: bad_alloc e tem que verificar o resultado de malloc. Chamando as operadoras novas e excluir globais poderia ser melhor, uma vez que desvio o homem médio.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top