Next: OATH -- Object-oriented
Up: Trabalho Relacionado
Previous: SSP Chains --
A ideia de utilizar smart pointers
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: OATH -- Object-oriented
Up: Trabalho Relacionado
Previous: SSP Chains --
David M. M. de Matos
Thu Jun 29 14:58:09 MET DST 1995