Se eu declarar um objeto envolvido em um ponteiro compartilhado:
std::shared_ptr<myClass> myClassObject(new myClass());
então eu queria passá-lo como um argumento para um método:
DoSomething(myClassObject);
//the called method
void DoSomething(std::shared_ptr<myClass> arg1)
{
arg1->someField = 4;
}
O acima simplesmente incrementa a contagem de referência do shared_pt e está tudo bem? Ou deixa um ponteiro pendurado?
Você ainda deveria fazer isso ?:
DoSomething(myClassObject.Get());
void DoSomething(std::shared_ptr<myClass>* arg1)
{
(*arg1)->someField = 4;
}
Acho que a 2ª forma pode ser mais eficiente porque só precisa copiar 1 endereço (em oposição ao ponteiro inteligente inteiro), mas a 1ª forma parece mais legível e não prevejo ultrapassar os limites de desempenho. Só quero ter certeza de que não há nada de perigoso nisso.
Obrigado.
c++
c++11
shared-ptr
c++-faq
Steve H
fonte
fonte
const std::shared_ptr<myClass>& arg1
DoSomething
realmente precisa compartilhar a propriedade? Parece que deveria ter apenas uma referência em vez disso ...void DoSomething(myClass& arg1)
.shared_ptr<>
Especificamente, você deve passar por valor para realmente compartilhar a propriedade.std::make_shared
não é apenas mais eficiente, mas também mais seguro que ostd::shared_ptr
construtor.Respostas:
Claro, posso ajudar com isso. Suponho que você tenha algum conhecimento da semântica de propriedade em C ++. Isso é verdade?
Boa.
Ok, só consigo pensar em duas razões para
shared_ptr
argumentar:shared_ptr
s.Em qual você está interessado?
Exemplos de tais funções incluem
std::static_pointer_cast
comparadores customizados ou predicados. Por exemplo, se você precisa encontrar todos os shared_ptr únicos de um vetor, você precisa de tal predicado.Exatamente.
Sim. E se não mudar o ponteiro, você deseja passar pela referência const. Não há necessidade de copiar, pois você não precisa compartilhar a propriedade. Esse é o outro cenário.
Aquele onde você compartilha a propriedade? Está bem. Como você compartilha a propriedade com
shared_ptr
?Então a função precisará fazer uma cópia de um
shared_ptr
, correto?Não, isso é uma pessimização. Se for passado por referência, a função não terá escolha a não ser fazer a cópia manualmente. Se for passado por valor, o compilador escolherá a melhor escolha entre uma cópia e uma movimentação e executará automaticamente. Então, passe por valor.
A função pode simplesmente mover o
shared_ptr
argumento para seu armazenamento. Mover ashared_ptr
é barato porque não altera nenhuma contagem de referência.Nesse caso,
shared_ptr
é completamente irrelevante para a função. Se você deseja manipular a ponta, pegue uma ponta e deixe que os chamadores escolham a semântica de propriedade que desejam.As regras usuais se aplicam. Os indicadores inteligentes não mudam nada.
Direito.
Ah, um caso interessante. Não espero que isso aconteça com frequência. Mas quando isso acontecer, você pode passar por valor e ignorar a cópia, se não precisar, ou passar por referência e fazer a cópia, se precisar.
Se estiver em uma situação em que isso realmente importa, você pode fornecer duas sobrecargas, uma tomando uma referência de valor constante e outra tomando uma referência de valor r. Um copia, o outro se move. Um modelo de função de encaminhamento perfeito é outra opção.
fonte
shared_ptr<T> ptr;
(ou sejavoid f(T&)
, chamado comf(*ptr)
) e o objeto não sobrevive à chamada. Alternativamente, escreva um em que você passe por valorvoid f(T)
e por problemas.void f(T&)
e envolve threads. Acho que meu problema é que o tom da sua resposta desencoraja a passagem da propriedade de shared_ptr's, que é a maneira mais segura, intuitiva e menos complicada de usá-los. Eu seria extremamente contra aconselhar um iniciante a extrair uma referência aos dados pertencentes a um shared_ptr <> as possibilidades de uso indevido são grandes e o benefício é mínimo.Acho que as pessoas estão desnecessariamente com medo de usar ponteiros brutos como parâmetros de função. Se a função não for armazenar o ponteiro ou afetar seu tempo de vida, um ponteiro bruto funciona da mesma forma e representa o menor denominador comum. Considere, por exemplo, como você passaria a
unique_ptr
em uma função que recebe ashared_ptr
como parâmetro, por valor ou por referência const?void DoSomething(myClass * p); DoSomething(myClass_shared_ptr.get()); DoSomething(myClass_unique_ptr.get());
Um ponteiro bruto como um parâmetro de função não impede que você use ponteiros inteligentes no código de chamada, onde realmente importa.
fonte
DoSomething(*a_smart_ptr)
fun(&x)
comfun(x)
. No último exemplo,x
poderia ser passado por valor ou como uma ref const. Como dito acima, também permitirá que você seja aprovadonullptr
se não estiver interessado na saída ...Sim, toda a ideia sobre um shared_ptr <> é que várias instâncias podem conter o mesmo ponteiro bruto e a memória subjacente só será liberada quando a última instância de shared_ptr <> for destruída.
Eu evitaria um ponteiro para um shared_ptr <>, pois isso anula o propósito, pois agora você está lidando com raw_pointers novamente.
fonte
Passar por valor em seu primeiro exemplo é seguro, mas existe um idioma melhor. Passe por referência const sempre que possível - eu diria que sim, mesmo ao lidar com ponteiros inteligentes. Seu segundo exemplo não está exatamente quebrado, mas está muito
!???
. Tolo, não realizar nada e derrotar parte do ponto de ponteiros inteligentes, e vai te deixar em um mundo cheio de erros de dor quando você tenta desreferenciar e modificar as coisas.fonte
shared_ptr<>
especificamente, que você deve fazer uma cópia para realmente compartilhar a propriedade; Se você tiver uma referência ou ponteiro para um,shared_ptr<>
então você não está compartilhando nada e está sujeito aos mesmos problemas de vida útil de uma referência ou ponteiro normal.shared_ptr
do chamador tem a garantia de durar mais que a chamada de função, portanto, a função é segura ao usar oshared_ptr<> const &
. Se for necessário armazenar o ponteiro para um momento posterior, ele precisará copiar (neste ponto, uma cópia deve ser feita, ao invés de conter uma referência), mas não há necessidade de incorrer no custo de cópia, a menos que você precise fazer isto. Gostaria de passar uma referência constante neste caso ....shared_ptr
da interface e passar umaconst
referência simples ( ) para o objeto apontado.shared_ptr<>
para começar? Agora, sua API é realmente um incômodo, pois força o chamador a alterar inutilmente a semântica do tempo de vida do objeto apenas para usá-lo. Não vejo nada que valha a pena defender nisso, além de dizer "tecnicamente não está prejudicando nada". Não vejo nada polêmico sobre 'se você não precisa de propriedade compartilhada, não useshared_ptr<>
'.em sua função,
DoSomething
você está alterando um membro de dados de uma instância de classe, demyClass
modo que o que está modificando é o objeto gerenciado (ponteiro bruto) e não o objeto (shared_ptr). O que significa que no ponto de retorno desta função todos os ponteiros compartilhados para o ponteiro bruto gerenciado verão seu membro de dados:myClass::someField
alterado para um valor diferente.neste caso, você está passando um objeto para uma função com a garantia de que não o está modificando (falando sobre shared_ptr não o objeto possuído).
O idioma para expressar isso é por meio de: a const ref, assim
void DoSomething(const std::shared_ptr<myClass>& arg)
Da mesma forma, você está garantindo ao usuário de sua função que não está adicionando outro proprietário à lista de proprietários do ponteiro bruto. No entanto, você manteve a possibilidade de modificar o objeto subjacente apontado pelo ponteiro bruto.
CAVEAT: O que significa que, se por algum meio alguém chamar
shared_ptr::reset
antes de você chamar sua função, e naquele momento for o último shared_ptr possuindo o raw_ptr, seu objeto será destruído e sua função manipulará um Ponteiro pendente para o objeto destruído. MUITO PERIGOSO!!!fonte