Existe um uso legítimo de void*
em C ++? Ou foi introduzido porque C o tinha?
Só para recapitular meus pensamentos:
Entrada : Se quisermos permitir vários tipos de entrada, podemos sobrecarregar funções e métodos, alternativamente, podemos definir uma classe base comum, ou modelo (obrigado por mencionar isso nas respostas). Em ambos os casos, o código é mais descritivo e menos sujeito a erros (desde que a classe base seja implementada de maneira sã).
Saída : Não consigo pensar em nenhuma situação em que preferiria receber void*
em vez de algo derivado de uma classe base conhecida.
Apenas para deixar claro o que quero dizer: não estou perguntando especificamente se há um caso de uso para void*
, mas se há um caso em que void*
é a melhor ou a única escolha disponível. O que foi perfeitamente respondido por várias pessoas abaixo.
variant
,any
, marcado união. Qualquer coisa que possa indicar o tipo real de conteúdo e ser mais seguro de usar.Respostas:
void*
é pelo menos necessário como resultado de::operator new
(também todooperator new
...) e demalloc
e como o argumento donew
operador de colocação .void*
pode ser considerado o supertipo comum de todo tipo de ponteiro. Portanto, não significa exatamente um ponteiro paravoid
, mas um ponteiro para qualquer coisa.BTW, se você quiser manter alguns dados para várias variáveis globais não relacionadas, você pode usar alguns
std::map<void*,int> score;
então, depois de ter declarado globalint x;
edouble y;
estd::string s;
fazerscore[&x]=1;
escore[&y]=2;
escore[&z]=3;
memset
quer umvoid*
endereço (os mais genéricos)Além disso, os sistemas POSIX têm dlsym e seu tipo de retorno evidentemente deve ser
void*
fonte
char *
. Eles são muito próximos nesse aspecto, embora o significado seja um pouco diferente.C++
está escrito,C++
isso é motivo suficiente para tervoid*
. Tenho que amar este site. Faça uma pergunta e aprenda muito.void*
tipo. Todas as funções de biblioteca padrão usadaschar*
Existem vários motivos para usar
void*
, sendo os 3 mais comuns:void*
em sua interfaceNa ordem inversa, denotar memória não digitada com
void*
(3) em vez dechar*
(ou variantes) ajuda a evitar aritmética acidental de ponteiro; existem muito poucas operações disponíveis no,void*
por isso geralmente requer conversão antes de ser útil. E, claro, assim como com,char*
não há problema com aliasing.O apagamento de tipo (2) ainda é usado em C ++, em conjunto com modelos ou não:
std::function
E obviamente, quando a interface com a qual você lida usa
void*
(1), você tem pouca escolha.fonte
Ai sim. Mesmo em C ++, às vezes optamos por, em
void *
vez detemplate<class T*>
porque às vezes o código extra da expansão do template pesa muito.Normalmente, eu o usaria como a implementação real do tipo, e o tipo de modelo herdaria dele e envolveria os casts.
Além disso, os alocadores de blocos personalizados (novas implementações do operador) devem ser usados
void *
. Esta é uma das razões pelas quais o g ++ adicionou uma extensão para permitir a aritmática do ponteirovoid *
como se fosse de tamanho 1.fonte
struct wrapper_base {}; template<class T> struct wrapper : public wrapper_base {T val;} typedef wrapper* like_void_ptr;
é um simulador void - * - mínimo usando modelos.Verdadeiro.
Isso é parcialmente verdade: e se você não puder definir uma classe base comum, uma interface ou algo semelhante? Para defini-los, você precisa ter acesso ao código-fonte, o que muitas vezes não é possível.
Você não mencionou modelos. No entanto, os modelos não podem ajudá-lo com polimorfismo: eles funcionam com tipos estáticos, ou seja, conhecidos em tempo de compilação.
void*
pode ser considerado como o menor denominador comum. Em C ++, você normalmente não precisa dele porque (i) você não pode fazer muito com ele inerentemente e (ii) quase sempre há soluções melhores.Além disso, você acabará convertendo-o em outros tipos de concreto. É por isso que
char *
geralmente é melhor, embora possa indicar que você está esperando uma string de estilo C, em vez de um bloco puro de dados. É por isso quevoid*
é melhor do quechar*
isso, porque permite a conversão implícita de outros tipos de ponteiro.Você deve receber alguns dados, trabalhar com eles e produzir uma saída; para conseguir isso, você precisa conhecer os dados com os quais está trabalhando, caso contrário, você terá um problema diferente que não é o que estava resolvendo originalmente. Muitos idiomas não têm
void*
e não têm nenhum problema com isso, por exemplo.Outro uso legítimo
Ao imprimir endereços de ponteiro com funções como
printf
o ponteiro deve tervoid*
tipo e, portanto, você pode precisar de um lançamento paravoid
*fonte
Sim, é tão útil quanto qualquer outra coisa na língua.
Como exemplo, você pode usá-lo para apagar o tipo de classe que você pode converter estaticamente para o tipo certo quando necessário, a fim de ter uma interface mínima e flexível.
Em que a resposta não é um exemplo de uso que deve dar-lhe uma idéia.
Eu copio e colo abaixo por uma questão de clareza:
class Dispatcher { Dispatcher() { } template<class C, void(C::*M)() = C::receive> static void invoke(void *instance) { (static_cast<C*>(instance)->*M)(); } public: template<class C, void(C::*M)() = &C::receive> static Dispatcher create(C *instance) { Dispatcher d; d.fn = &invoke<C, M>; d.instance = instance; return d; } void operator()() { (fn)(instance); } private: using Fn = void(*)(void *); Fn fn; void *instance; };
Obviamente, este é apenas um dos vários usos do
void*
.fonte
Interface com uma função de biblioteca externa que retorna um ponteiro. Aqui está um para um aplicativo Ada.
extern "C" { void* ada_function();} void* m_status_ptr = ada_function();
Isso retorna um ponteiro para tudo o que Ada queria falar para você. Você não precisa fazer nada sofisticado com ele, você pode devolvê-lo a Ada para fazer a próxima coisa. Na verdade, desemaranhar um ponteiro Ada em C ++ não é trivial.
fonte
auto
em vez disso neste caso? Supondo que o tipo seja conhecido em tempo de compilação.Resumindo, C ++ como uma linguagem estrita (sem levar em conta relíquias C como malloc () ) requer void *, uma vez que não tem pai comum de todos os tipos possíveis. Ao contrário do ObjC, por exemplo, que tem objeto .
fonte
malloc
enew
ambos retornamvoid *
, então você precisaria mesmo que houvesse uma classe de objeto em C ++operator new()
retornavoid *
, mas anew
expressão nãoint
2. se for EBC não virtual, em que diferevoid*
?A primeira coisa que me ocorre (que eu suspeito que seja um caso concreto de algumas das respostas acima) é a capacidade de passar uma instância de objeto para um threadproc no Windows.
Eu tenho algumas classes C ++ que precisam fazer isso, elas têm implementações de thread de trabalho e o parâmetro LPVOID na API CreateThread () obtém um endereço de uma implementação de método estático na classe para que a thread de trabalho possa fazer o trabalho com uma instância específica da classe. A conversão estática simples de volta no threadproc produz a instância com a qual trabalhar, permitindo que cada objeto instanciado tenha um thread de trabalho a partir de uma única implementação de método estático.
fonte
Em caso de herança múltipla, se você precisa para obter um ponteiro para o primeiro byte de um pedaço de memória ocupada por um objeto, você pode
dynamic_cast
paravoid*
.fonte