Pergunta

Qual é a melhor abordagem para encapsular objetos e gerir sua vida?Exemplo:Eu tenho uma classe Um, que contém um objeto do tipo B e é o único responsável por isso.

Solução 1, clone b para garantir que somente Um é capaz de limpá-lo.

class A
{
    B *b;
public:
    A(B &b)
    {
        this->b = b.clone();
    }

    ~A()
    {
        delete b; // safe
    }
};

Solução 2, usar diretamente o objeto passado, corremos o risco de um potencial duplo gratuitamente aqui.

class A
{
    B *b;
public:
    A(B *b)
    {
        this->b = b;
    }

    ~A()
    {
        delete b; // unsafe
    }
};

No meu caso, a solução #2 seria melhor se adequam.No entanto, eu me pergunto se isso é considerado ruim código, pois alguém pode não saber sobre o comportamento de Um, mesmo que seja documentado.Eu posso pensar em um dos seguintes cenários:

B *myB = new B();
A *myA = new A(myB);
delete myB; // myA contains a wild pointer now

Ou,

B *myB = new B();
A *firstA = new A(myB);
A *secondA = new A(myB); // bug! double assignment
delete firstA; // deletes myB, secondA contains a wild pointer now
delete secondA; // deletes myB again, double free

Posso simplesmente ignorar estas questões se eu documentar corretamente o comportamento de Um?É o suficiente para declarar a responsabilidade e deixar isso para os outros leiam o google docs?Como é gerenciada em sua base de código?

Foi útil?

Solução

Você deve definir o seu objeto de modo que a semântica de propriedade são, tanto quanto possível, definida pela interface.Como David Thornley apontou, std::auto_ptr é o ponteiro inteligente de escolha para indicar transferência de propriedade.Definir a classe assim:

class A
{    
    std::auto_ptr<B> b;
public:    
    A(std::auto_ptr<B> b)    
    {
        this->b = b;
    }
    // Don't need to define this for this scenario
    //~A()
    //{ 
    //   delete b; // safe
    //}
};

Desde que o contrato de std::auto_ptr é que a atribuição = transferência de propriedade, o seu construtor agora afirma implicitamente que Um objeto tem a propriedade de o ponteiro para B é passado.Na verdade, se um cliente tenta fazer algo com um std::auto_ptr<B> que eles usaram para construir um a Um, após a construção, a operação falhará, como o ponteiro que espera vai ser inválido.

Outras dicas

Eu nunca excluir nada me menos que eu realmente preciso. Isso leva a erros.

ponteiros

inteligentes são seu amigo. std::auto_ptr<> é seu amigo quando um objeto é dono de outra e é responsável por excluí-lo quando sair do escopo. boost::shared_ptr<> (ou, agora, std::tr1::shared_ptr<>) é seu amigo quando há potencialmente mais de um objeto ligado a um outro objeto, e você quer que o objeto excluído quando não há mais referências a ele.

Então, use sua solução 1 com auto_ptr, ou a sua solução 2 com shared_ptr.

Se você estiver escrevendo código que alguém vai usar mais tarde, estas questões devem ser abordadas. Neste caso, eu iria para a contagem de referência simples (talvez com ponteiros inteligentes). Considere o seguinte exemplo:

Quando uma instância da classe de encapsulamento é atribuído um objeto B, ele chama um método de contador de referência B aumento do objeto. Quando a classe encapsular é destruída, ela não exclui B, mas em vez chama um método fazer contagem de referência diminuição. Quando o contador atingir zero, um objecto B está destruída (ou destrói-se para esse efeito). Desta forma, várias instâncias de encapsular classe pode trabalhar com uma única instância do objeto B.

Mais sobre o assunto:. contagem de referência

Se o seu objetivo é o único responsável pelo objeto passado, em seguida, excluí-lo deve ser seguro. Se não estiver seguro do que a afirmação de que você é o único responsável é falsa. Então, qual é? Se você está interface está documentado que você vai eliminar o objecto de entrada, então é responsabilidade do chamador para ter certeza de receber um objeto que deve ser excluído por você.

Se você está clonagem A, e ambos A1 e A2 reter referências a B, então a vida de B não está sendo controlado inteiramente por A. Ele está sendo compartilhado entre os vários A. Clonagem B garante um um-para-um entre As e Bs, que vai ser fácil para garantir a consistência vida.

Se a clonagem B não é uma opção, então você precisa descartar o conceito de que um é responsável pela vida de B. De qualquer outro objeto precisará gerenciar os vários B, ou que você precisa para implementar um método como contagem de referência.

Para referência, quando eu penso do termo 'Clone', implica uma cópia em profundidade, o que clonar B também. Eu esperaria os dois Como a ser completamente separada uma da outra depois de um clone.

Eu faço as coisas não clone desnecessariamente ou "apenas para ser seguro".

Em vez disso eu sei de quem é a responsabilidade de apagar alguma coisa: ou através de documentação ou por ponteiros inteligentes ... por exemplo, se eu tiver uma função create que instancia algo e retorna um ponteiro para ele e não excluí-lo, de modo que não está claro onde e por whome essa coisa é sempre suposto ser excluído, então em vez de create do retornando um ponteiro nua eu poderia definir o tipo de retorno de create como retornando o ponteiro contido dentro de algum tipo de ponteiro inteligente.

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