smart - weak_ptr example c++
Usando ponteiros inteligentes para os alunos (2)
Um
unique_ptr
não funcionaria por causa degetDevice()
, 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.