smart - weak_ptr example c++




Usando ponteiros inteligentes para os alunos (2)

Um unique_ptr não funcionaria por causa de getDevice() , certo?

Não necessariamente não. O que é importante aqui é determinar a política de propriedade apropriada para o objeto Device , isto é, quem será o proprietário do objeto apontado pelo ponteiro (inteligente).

Será a instância do objeto Settings sozinha ? Device objeto Device terá que ser destruído automaticamente quando o objeto Settings for destruído, ou deverá sobreviver a esse objeto?

No primeiro caso, std::unique_ptr é o que você precisa, pois faz com que o Settings o único proprietário (exclusivo) do objeto apontado e o único objeto responsável por sua destruição.

Sob essa hipótese, getDevice() deve retornar um simples ponteiro de observação (os ponteiros de observação são ponteiros que não mantêm o objeto apontado ativo). O tipo mais simples de ponteiro de observação é um ponteiro bruto:

#include <memory>

class Device {
};

class Settings {
    std::unique_ptr<Device> device;
public:
    Settings(std::unique_ptr<Device> d) {
        device = std::move(d);
    }

    Device* getDevice() {
        return device.get();
    }
};

int main() {
    std::unique_ptr<Device> device(new Device());
    Settings settings(std::move(device));
    // ...
    Device *myDevice = settings.getDevice();
    // do something with myDevice...
}

[ NOTA 1: Você pode estar se perguntando por que estou usando ponteiros brutos aqui, quando todo mundo fica dizendo que ponteiros crus são ruins, inseguros e perigosos. Na verdade, esse é um aviso precioso, mas é importante colocá-lo no contexto correto: ponteiros brutos são ruins quando usados ​​para executar o gerenciamento manual de memória , ou seja, alocar e desalocar objetos através de new e delete . Quando usado puramente como um meio para alcançar a semântica de referência e passar por ponteiros não-proprietários, observadores, não há nada intrinsecamente perigoso em ponteiros brutos, exceto talvez pelo fato de que um deve tomar cuidado para não desreferenciar um ponteiro pendente. - NOTA FINAL 1 ]

[ NOTA 2: Como surgiu nos comentários, neste caso particular onde a propriedade é única e o objeto possuído é sempre garantido de estar presente (ou seja, o device membro de dados interno nunca será nullptr ), a função getDevice() poderia (e talvez deva) retornar uma referência em vez de um ponteiro. Embora isso seja verdade, decidi retornar um ponteiro bruto aqui porque quis dizer que essa é uma resposta curta que poderia ser generalizada para o caso em que device poderia ser nullptr e para mostrar que ponteiros brutos são OK, desde que um não use -los para gerenciamento de memória manual. - NOTA FINAL 2 ]

A situação é radicalmente diferente, é claro, se o seu objeto Settings não tiver a propriedade exclusiva do dispositivo. Este poderia ser o caso, por exemplo, se a destruição do objeto Settings não implicasse também a destruição do objeto Device apontado.

Isso é algo que só você, como designer do seu programa, pode dizer; a partir do exemplo que você fornece, é difícil para mim dizer se este é o caso ou não.

Para ajudá-lo a descobrir, você pode se perguntar se há outros objetos além das Settings que têm o direito de manter o objeto Device ativo, desde que eles mantenham um ponteiro para ele, em vez de serem apenas observadores passivos. Se esse é realmente o caso, então você precisa de uma política de propriedade compartilhada , que é o que o std::shared_ptr oferece:

#include <memory>

class Device {
};

class Settings {
    std::shared_ptr<Device> device;
public:
    Settings(std::shared_ptr<Device> const& d) {
        device = d;
    }

    std::shared_ptr<Device> getDevice() {
        return device;
    }
};

int main() {
    std::shared_ptr<Device> device = std::make_shared<Device>();
    Settings settings(device);
    // ...
    std::shared_ptr<Device> myDevice = settings.getDevice();
    // do something with myDevice...
}

Observe que weak_ptr é um ponteiro de observação , não um ponteiro de propriedade - em outras palavras, ele não mantém o objeto apontado ativo se todos os outros ponteiros proprietários do objeto apontado saírem do escopo.

A vantagem de weak_ptr sobre um ponteiro bruto regular é que você pode dizer com segurança se weak_ptr está pendente ou não (isto é, se está apontando para um objeto válido ou se o objeto originalmente apontado foi destruído). Isso pode ser feito chamando a função de membro expired() no objeto weak_ptr .

Estou tendo problemas para entender o uso de ponteiros inteligentes como membros da classe no C ++ 11. Eu li muito sobre ponteiros inteligentes e acho que entendo como unique_ptr e shared_ptr / weak_ptr funcionam em geral. O que não entendo é o uso real. Parece que todo mundo recomenda usar unique_ptr como o caminho a percorrer quase o tempo todo. Mas como eu implementaria algo assim:

class Device {
};

class Settings {
    Device *device;
public:
    Settings(Device *device) {
        this->device = device;
    }

    Device *getDevice() {
        return device;
    }
};    

int main() {
    Device *device = new Device();
    Settings settings(device);
    // ...
    Device *myDevice = settings.getDevice();
    // do something with myDevice...
}

Digamos que eu queira substituir os ponteiros por ponteiros inteligentes. Um unique_ptr não funcionaria por causa de getDevice() , certo? Então é hora de usar shared_ptr e weak_ptr ? Nenhuma maneira de usar unique_ptr ? Parece-me que para a maioria dos casos shared_ptr faz mais sentido a menos que eu esteja usando um ponteiro em um escopo realmente pequeno?

class Device {
};

class Settings {
    std::shared_ptr<Device> device;
public:
    Settings(std::shared_ptr<Device> device) {
        this->device = device;
    }

    std::weak_ptr<Device> getDevice() {
        return device;
    }
};

int main() {
    std::shared_ptr<Device> device(new Device());
    Settings settings(device);
    // ...
    std::weak_ptr<Device> myDevice = settings.getDevice();
    // do something with myDevice...
}

Aquele é o caminho para ir? Muito obrigado!


class Device {
};

class Settings {
    std::shared_ptr<Device> device;
public:
    Settings(const std::shared_ptr<Device>& device) : device(device) {

    }

    const std::shared_ptr<Device>& getDevice() {
        return device;
    }
};

int main()
{
    std::shared_ptr<Device> device(new Device());
    Settings settings(device);
    // ...
    std::shared_ptr<Device> myDevice(settings.getDevice());
    // do something with myDevice...
    return 0;
}

week_ptr é usado apenas para loops de referência. O gráfico de dependência deve ser um gráfico com direção acíclica. Em ponteiros compartilhados, há 2 contagens de referência: 1 para shared_ptr s e 1 para todos os ponteiros ( shared_ptr e weak_ptr ). Quando todos os shared_ptr s são removidos, o ponteiro é excluído. Quando o ponteiro é necessário a partir de weak_ptr , o lock deve ser usado para obter o ponteiro, se existir.







unique-ptr