Pergunta

Em muitas APIs C++ (as baseadas em COM vêm à mente) que fazer algo para você, o ponteiro para o objeto que é construído geralmente é necessário como um ** ponteiro (e a função irá construí-lo e iniciá-lo para você)

Você geralmente vê assinaturas como:

HRESULT createAnObject( int howbig, Object **objectYouWantMeToInitialize ) ;

- mas você raramente vê o novo objeto sendo passado como valor de retorno.

Além das pessoas quererem ver os códigos de erro, qual o motivo disso?É melhor usar o ** padrão em vez de um ponteiro retornado para operações mais simples, como:

wchar_t* getUnicode( const char* src ) ;

Ou seria melhor escrever isso como:

void getUnicode( const char* src, wchar_t** dst ) ;

A coisa mais importante que consigo pensar é lembrar de liberá-lo, e o ** alguma forma, por algum motivo, tende a me lembrar que também preciso desalocá-lo.

Foi útil?

Solução

"Além de querer códigos de erro"?

O que faz você pensar aí é a além disso. Os códigos de erro são praticamente a única razão. A função precisa de alguma maneira de indicar falha. C não tem exceções, por isso precisa fazer isso através de um parâmetro de ponteiro ou pelo valor de retorno, e o valor de retorno é idiomático e mais fácil de verificar ao chamar a função.

(A propósito, não há regra universal que ** significa que você precisa libertar o objeto. Nem sempre é esse o caso, e provavelmente é uma má idéia usar algo que arbitrário para lembrá -lo de quais objetos limparem.)

Outras dicas

Duas razões vêm à minha mente.

Primeiro são códigos de erro, na verdade. Além de C ++, C não tem exceções e com é um C-API. Além disso, muitos projetos baseados em C ++ preferem não usar exceções por vários motivos. Pode haver casos, em que um valor de retorno não pode sinalizar erros, por exemplo, se sua função retornar um número inteiro, pode não haver valor inteiro, que pode representar um código de erro. Embora os erros de sinalização com ponteiros sejam fáceis (nulo == erro), alguns designers da API preferem sinalizar erros de maneira consistente sobre todas as funções.

Segundo, as funções podem ter apenas um valor de retorno, mas chamá -las pode criar vários objetos. Algumas funções da API Win32 levam vários ponteiros a ponteiros que podem ser preenchidos opcionalmente, se você chamar essas funções com indicadores não nulos. Você não pode retornar dois ponteiros, ou melhor, isso seria estranho de usar, se o valor de retorno for alguma estrutura por valor contendo mais de um ponteiro. Aqui também uma API consistente é um objetivo sensato de alcançar.

Novos objetos em argumentos de função passados ​​por ** é melhor.Isso me conforta para o uso futuro da mudança void para bool por exemplo, para retornar o sucesso de uma função ou outras informações que forneçam funções funcionais.

Responda em uma linha: Isso é muito melhor para códigos de erro resultantes.

Além das pessoas querendo ver códigos de erro, qual é a razão para isso?

Existem algumas razões para isso. Um deles está escrevendo uma interface utilizável em C (você vê isso no Winapi e no Windows com).

A compatibilidade com versões anteriores é outro motivo (ou seja, a interface foi escrita assim e quebrá -la agora quebraria o código existente).

Eu iria com a compatibilidade C para um princípio de design ao usar código como este. Se você escrevesse em C ++, escreveria

retval Myfunction(Result *& output);

ao invés de

retval Myfunction(Result ** output);

ou (ainda melhor):

Result *Myfunction();

e faça com que a função jogue uma exceção no erro.

Não tenho certeza se concordo que é a melhor maneira de fazê -lo ... isso pode ser melhor:

Object * createAnObject(int howbig, HRESULT * optPlaceResultCodeHereIfNotNull = NULL);

Dessa forma, não há como mexer com a indiragem dupla (o que pode ser um pouco complicado para pessoas que não estão acostumadas), e as pessoas que não se importam com os códigos de resultado não precisam se preocupar com o segundo argumento ... Eles podem verificar se o valor de retorno é nulo ou não.

Na verdade, como é C ++, você pode facilitar as coisas, usando a sobrecarga de funções:

Object * createAnObject(int howbig);
Object * createAnObject(int howbig, HRESULT & returnResultCode);

Qualquer método chama em uma chamada COM tem que ser Hresult. Os códigos de retorno são alavancados em toda a estrutura e passar um ponteiro duplo é uma maneira bem conhecida de obter o objeto criado.

Não respondendo à sua pergunta, mas um comentário como sua pergunta apresentou alguns pensamentos que tenho sobre a programação COM/DCOM usando o C ++.

Todo esse "ponteiro" e "ponteiro para ponteiro", gerenciamento de memória e contagem de referência são as razões pelas quais eu evito fazer programação com o C ++. Mesmo com o ATL no lugar, eu não gosto disso pela simples razão de que não parece natural o suficiente. Dito isto, fiz alguns projetos usando ATL.

Naquela época, a alternativa é usar vb. O código VB parece mais natural para a programação COM ou DCOM.

Hoje, eu usaria C#.

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