Existem muitos ponteiros em C ++, mas para ser sincero em 5 anos ou mais na programação C ++ (especificamente com o Qt Framework), eu só uso o ponteiro bruto antigo:
SomeKindOfObject *someKindOfObject = new SomeKindOfObject();
Eu sei que existem muitos outros ponteiros "inteligentes":
// shared pointer:
shared_ptr<SomeKindofObject> Object;
// unique pointer:
unique_ptr<SomeKindofObject> Object;
// weak pointer:
weak_ptr<SomeKindofObject> Object;
Mas não tenho a menor idéia do que fazer com eles e o que eles podem me oferecer em comparação com indicadores brutos.
Por exemplo, eu tenho esse cabeçalho de classe:
#ifndef LIBRARY
#define LIBRARY
class LIBRARY
{
public:
// Permanent list that will be updated from time to time where
// each items can be modified everywhere in the code:
QList<ItemThatWillBeUsedEveryWhere*> listOfUselessThings;
private:
// Temporary reader that will read something to put in the list
// and be quickly deleted:
QSettings *_reader;
// A dialog that will show something (just for the sake of example):
QDialog *_dialog;
};
#endif
Claramente, isso não é exaustivo, mas para cada um desses três indicadores é correto deixá-los "em bruto" ou devo usar algo mais apropriado?
E, na segunda vez, se um empregador ler o código, ele será rigoroso quanto ao tipo de indicação que eu uso ou não?
c++
programming-practices
pointers
qt
smart-pointer
CheshireChild
fonte
fonte
Respostas:
Um ponteiro "bruto" não é gerenciado. Ou seja, a seguinte linha:
... vazará memória se um acompanhamento
delete
não for executado no momento adequado.auto_ptr
A fim de minimizar esses casos,
std::auto_ptr<>
foi introduzido. Devido às limitações do C ++ anteriores ao padrão de 2011, no entanto, ainda é muito fácilauto_ptr
vazar memória. É suficiente para casos limitados, como este, no entanto:Um de seus casos de uso mais fracos é em contêineres. Isso ocorre porque, se uma cópia de uma
auto_ptr<>
é feita e a cópia antiga não é redefinida com cuidado, o contêiner pode excluir o ponteiro e perder dados.unique_ptr
Como substituição, o C ++ 11 introduziu
std::unique_ptr<>
:Tal
unique_ptr<>
será limpo corretamente, mesmo quando for passado entre as funções. Ele faz isso representando semanticamente a "propriedade" do ponteiro - o "proprietário" a limpa. Isso o torna ideal para uso em contêineres:Ao contrário
auto_ptr<>
,unique_ptr<>
é bem-comportado aqui e, quando ovector
redimensionamento, nenhum dos objetos será excluído acidentalmente enquanto avector
cópia é armazenada em backup.shared_ptr
eweak_ptr
unique_ptr<>
é útil, com certeza, mas há casos em que você deseja que duas partes da sua base de código possam se referir ao mesmo objeto e copiar o ponteiro, mantendo a limpeza adequada garantida. Por exemplo, uma árvore pode ficar assim, ao usarstd::shared_ptr<>
:Nesse caso, podemos até manter várias cópias de um nó raiz, e a árvore será limpa adequadamente quando todas as cópias do nó raiz forem destruídas.
Isso funciona porque cada um deles
shared_ptr<>
mantém não apenas o ponteiro para o objeto, mas também uma contagem de referência de todos osshared_ptr<>
objetos que se referem ao mesmo ponteiro. Quando um novo é criado, a contagem aumenta. Quando um é destruído, a contagem diminui. Quando a contagem chega a zero, o ponteiro édelete
d.Portanto, isso apresenta um problema: Estruturas com ligações duplas terminam em referências circulares. Digamos que queremos adicionar um
parent
ponteiro à nossa árvoreNode
:Agora, se removermos um
Node
, há uma referência cíclica a ele. Nunca serádelete
d porque sua contagem de referência nunca será zero.Para resolver esse problema, você usa um
std::weak_ptr<>
:Agora, tudo funcionará corretamente e a remoção de um nó não deixará referências presas ao nó pai. No entanto, torna a caminhada na árvore um pouco mais complicada:
Dessa forma, você pode bloquear uma referência ao nó e ter uma garantia razoável de que ele não desaparecerá enquanto você estiver trabalhando nele, já que está segurando
shared_ptr<>
nele.make_shared
emake_unique
Agora, existem alguns problemas menores
shared_ptr<>
eunique_ptr<>
que devem ser resolvidos. As duas linhas a seguir têm um problema:Se
thrower()
lançar uma exceção, as duas linhas vazarão memória. E mais do que isso,shared_ptr<>
mantém a contagem de referência longe do objeto para o qual aponta e isso pode significar uma segunda alocação). Isso geralmente não é desejável.O C ++ 11 fornece
std::make_shared<>()
e o C ++ 14 fornecestd::make_unique<>()
para resolver esse problema:Agora, nos dois casos, mesmo se
thrower()
lançar uma exceção, não haverá vazamento de memória. Como bônus,make_shared<>()
tem a oportunidade de criar sua contagem de referência no mesmo espaço de memória que seu objeto gerenciado, que pode ser mais rápido e economizar alguns bytes de memória, oferecendo uma exceção de garantia de segurança!Notas sobre Qt
Deve-se notar, no entanto, que o Qt, que deve suportar compiladores anteriores ao C ++ 11, possui seu próprio modelo de coleta de lixo: muitos
QObject
s possuem um mecanismo no qual eles serão destruídos corretamente sem a necessidade do usuário paradelete
eles.Não sei como
QObject
se comportará quando gerenciado por ponteiros gerenciados pelo C ++ 11, portanto não posso dizer queshared_ptr<QDialog>
é uma boa idéia. Não tenho experiência suficiente com o Qt para ter certeza, mas acredito que o Qt5 foi ajustado para este caso de uso.fonte
shared_ptr
objeto é um objeto separado - uma alocação separada - donew
objeto ed. Eles existem em diferentes locais.make_shared
tem a capacidade de reuni-los no mesmo local, o que melhora a localidade do cache, entre outras coisas.shared_ptr
é um objeto. E para gerenciar um objeto, ele deve alocar um objeto (contagens de referência (fraco + forte) + destruidor).make_shared
permite alocar isso e o objeto gerenciado como uma peça.unique_ptr
não os usa, portanto, não há vantagem correspondente, além de garantir que o objeto seja sempre de propriedade do ponteiro inteligente. Como um aparte, pode-se ter umshared_ptr
que possua um objeto subjacente e represente umnullptr
, ou que não possua e represente um ponteiro não nulo.shared_ptr
faz: 1. Compartilha a propriedade de algum objeto (representado por um objeto interno alocado dinamicamente, com uma contagem de referência fraca e forte, além de um deleter) . 2. Ele contém um ponteiro. Essas duas partes são independentes.make_unique
emake_shared
ambos garantem que o objeto alocado seja colocado com segurança em um ponteiro inteligente. Além disso,make_shared
permite alocar o objeto de propriedade e o ponteiro gerenciado juntos.