next up previous contents index
Next: OATH -- Object-oriented Up: Trabalho Relacionado Previous: SSP Chains --

Smart Pointers

A ideia de utilizar smart pointersgif surgiu como uma aplicação útil da possibilidade de redefinição do operador ->, em C++, para que a sua acção não fosse limitada ao mero desreferenciar um ponteiro, mas também possibilitar a execução de acções antes de efectuar uma invocação [Stroustrup, 1987].

O princípio de funcionamento baseia-se no facto de o operador -> ser considerado, primeiro, como um operador unário, sendo -> posteriormente reaplicado ao resultado da execução de operator->(). Assim, o tipo de retorno do operador deve ser um ponteiro para a classe (para um objecto da classe), para a qual o operador foi definido. O segmento de código C++ abaixo mostra duas classes: Y é uma classe qualquer e X é a classe dos smart pointers para objectos de Y. O funcionamento é semelhante ao de ponteiros C++ normais, com a excepção de que alguma computação que se julgue apropriada pode ser feita em cada acesso.

    struct Y { int m; };                Y *X::operator->()
                                        {
    class X {                               if (p == 0) {
        Y *p;                                   // Initialize p
        //...                                   //...
                                            }
        Y *operator->();                    else {
        //...                                   // Use p
    };                                          //...
                                            }
                                            return p;
                                        }

A utilização de smart pointers, através da redefinição de operator->() é importante para uma determinada classe de problemas. A razão pode ser vista através do conceito chave de indirecção, que pode ser realizado usando smart pointers. Uma outra forma de olhar para a redefinição do operador é como sendo uma forma limitada de delegação.

O conceito tem, no entanto, alguns problemas, citados na literatura [Kennedy, 1991,Edelson, 1992]. Edelson identifica sete propriedades que devem existir para que algumas características dos ponteiros normais sejam providenciadas pelos substitutos. As propriedades estão relacionadas com o facto de se estar a interpôr um objecto entre o programa e o objecto que o programa espera utilizar, sem que, para isso, disponha de suporte de compilador, i.e., o compilador não pode adivinhar que a semântica associada a alguns dos objectos não é a normal para eles. Na lista que se apresenta de seguida é discutida cada uma das propriedades.

dir
Conversão implícita de um ponteiro para um ponteiro para uma base directa. Esta conversão é automática para ponteiros normais da linguagem, mas não é garantido o seu funcionamento para algumas concretizações de smart pointers, e.g. as que realizam as conversões utilizando funções definidas pelo programador. Outras realizações, em particular aquelas em que as classes dos smart pointers pertencem a uma hierarquia replicada, suportam directamente estas conversões.

indir
Conversão implícita de um ponteiro para um ponteiro para uma base indirecta. Ao contrário das anteriores, em que, em princípio, é sempre possível encontrar um modo de funcionamento correcto, seja automático ou não, neste caso as conversões podem nem sempre ser possíveis. Tal é o caso em que o programador redefine apenas as conversões directas entre smart pointers: não há forma de inferir as indirectas a partir do código do programador. Este problema, tal como outros deste modelo, está relacionado com a falta de suporte de compilador aos conceitos em jogo.

prefer
Preferência na conversão, a favor de uma base directa, quando a alternativa é uma base indirecta. Os problemas postos por esta propriedade são da mesma ordem dos que se viram nos dois pontos anteriores. Tal como atrás, este caso apenas é resolvido automaticamente quando se tem uma hierarquia de classes de smart pointers. Ao contrário do que antes se verificava, não é agora definível pelo programador a preferência de conversão, pelo que se obtém um comportamento incorrecto com qualquer realização de smart pointers que não utilize uma hierarquia de classes.

mult
Suporte para herança múltipla. Este suporte está relacionado com as conversões automáticas que são feitas na presença de herança múltipla, com ponteiros normais. Com smart pointers é possível conseguir comportamentos correctos excepto quando se tem uma hierarquia de classes de smart pointers abstracta, em que a classe de topo contém um ponteiro não tipificado, i.e., void*, para o objecto. Em C++, a utilização deste ponteiro é insegura, se não se tiver em conta o nível da hierarquia.

safe
Não há conversão de derived** para base**, porque esta conversão introduziria uma falha de segurança no sistema de tipos da linguagem [Edelson, 1992]. Note-se que as conversões permitidas em C++ normal são as de derived* para base*. Quando se tentam utilizar smart pointers, que são objectos C++, em lugar de ponteiros normais, o erro detectável pelo compilador, no caso de ponteiros normais, apenas ocorre em tempo de execução, provocando a falha do programa. Em apêndice, na secção C.1, é apresentado um pequeno programa que exemplifica o problema.

const
Capacidade de referenciar objectos normais e afectados do qualificador const -- com reforço do compilador do atributo const --, e de conversão de não- const para const. Mais uma vez, o que se pretende é manter a semântica de ponteiros normais ao declarar e utilizar smart pointers. O problema, neste caso, é a incapacidade de representação, pelos smart pointers de todas as relações do atributo const. Com ponteiros normais, as seguintes declarações são possíveis:

Falta aos smart pointers a expressividade que permite as declarações acima, apenas sendo possíveis declarações do tipo:

O que se disse para o atributo const, é também válido para outros atributos (e.g. volatile). A solução passa por definir um nova classe de smart pointers por cada atributo a suportar. É claro que a legibilidade do programa vai sofrer com o uso destas classes.

fast
Eficiência. De um modo geral, todas as organizações da hierarquia de classes dos smart pointers são eficientes, excepto quando o ponteiro C++ para os dados está replicado em cada classe. A ineficiência deve-se à necessidade de executar uma operação por cada classe base, quando se pretende actualizar um smart pointer de uma classe derivada.

Embora, de um modo geral, eficiente, a programação de smart pointers para satisfazer todas as categorias é, na prática, impossível. Algumas das soluções apontadas, tais como (re)definição pelo utilizador de operações que com ponteiros normais são triviais, quando se usam smart pointers não são de fácil concretização e exigem considerável esforço adicional, e.g. na definição de classes adicionais para tratar correctamente o problema dos atributos dos objectos: além de const, há ainda que considerar outros e suas combinações. O resultado final pode ser um elevado número de classes de ponteiros, para resolver um ou mais dos problemas, que vão adicionar complexidade ao programa.



next up previous contents index
Next: OATH -- Object-oriented Up: Trabalho Relacionado Previous: SSP Chains --



David M. M. de Matos
Thu Jun 29 14:58:09 MET DST 1995