smart - weak_ptr example c++




Devemos passar um shared_ptr por referência ou por valor? (6)

Aqui está o take de Herb Sutter

Diretriz: não passe um ponteiro inteligente como um parâmetro de função, a menos que você queira usar ou manipular o próprio ponteiro inteligente, como compartilhar ou transferir a propriedade.

Diretriz: Expresse que uma função armazenará e compartilhará a propriedade de um objeto heap usando um parâmetro shared_ptr por valor.

Diretriz: Use um parâmetro non-const shared_ptr & apenas para modificar o shared_ptr. Use um const shared_ptr & como um parâmetro somente se você não tiver certeza se vai ou não copiar e compartilhar a propriedade; caso contrário, use o widget * (ou, se não for anulável, um widget &).

Quando uma função recebe um shared_ptr (do boost ou do C ++ 11 STL), você está passando:

  • por referência de const: void foo(const shared_ptr<T>& p)

  • ou por valor: void foo(shared_ptr<T> p) ?

Eu preferiria o primeiro método porque suspeito que seria mais rápido. Mas isso realmente vale a pena ou há algum problema adicional?

Você poderia, por favor, dar as razões de sua escolha ou se o caso, por que você acha que isso não importa.


Desde o C ++ 11, você deve levá-lo por valor sobre const e mais frequentemente do que você imagina.

Se você está tomando o std :: shared_ptr (ao invés do tipo subjacente T), então você está fazendo isso porque você quer fazer algo com ele.

Se você gostaria de copiá-lo em algum lugar, faz mais sentido copiá-lo e std :: movê-lo internamente, em vez de copiá-lo por const e depois copiá-lo. Isto é porque você permite ao chamador a opção por sua vez std :: move o shared_ptr ao chamar sua função, economizando assim um conjunto de operações de incremento e decremento. Ou não. Ou seja, o chamador da função pode decidir se precisa ou não do std :: shared_ptr após chamar a função, e dependendo de se mover ou não. Isso não é possível se você passar por const &, e, portanto, é preferível tomá-lo por valor.

É claro, se o chamador precisar de seu shared_ptr por mais tempo (portanto não pode std :: move-lo) e você não quer criar uma cópia simples na função (digamos que você quer um ponteiro fraco, ou você só quer para copiá-lo, dependendo de alguma condição), então um const & ainda pode ser preferível.

Por exemplo, você deve fazer

void enqueue(std::shared<T> t) m_internal_queue.enqueue(std::move(t));

sobre

void enqueue(std::shared<T> const& t) m_internal_queue.enqueue(t);

Porque neste caso você sempre cria uma cópia internamente


Eu corri o código abaixo, uma vez com foo tomando o shared_ptr por const& e novamente com foo tomando o shared_ptr por valor.

void foo(const std::shared_ptr<int>& p)
{
    static int x = 0;
    *p = ++x;
}

int main()
{
    auto p = std::make_shared<int>();
    auto start = clock();
    for (int i = 0; i < 10000000; ++i)
    {
        foo(p);
    }    
    std::cout << "Took " << clock() - start << " ms" << std::endl;
}

Usando VS2015, x86 release build, no meu processador Intel Core 2 quad (2.4GHz)

const shared_ptr&     - 10ms  
shared_ptr            - 281ms 

A cópia por versão de valor foi uma ordem de grandeza mais lenta.
Se você estiver chamando uma função de forma síncrona do thread atual, prefira a const& version.


Não sabendo o custo do tempo da operação de cópia shared_copy onde o incremento atômico e o decremento estão, eu sofri de um problema de uso da CPU muito maior. Eu nunca esperei incremento atômico e decremento pode custar muito.

Após o resultado do meu teste, o acréscimo e decréscimo atómico int32 demora 2 ou 40 vezes do que o incremento e decréscimo não atómico. Eu tenho isso em 3GHz Core i7 com o Windows 8.1. O primeiro resultado surge quando não ocorre contenção, o último quando ocorre alta possibilidade de contenção. Tenho em mente que as operações atômicas são, por fim, baseadas em hardware. Bloqueio é bloqueio. Ruim para o desempenho quando ocorre a contenção.

Experimentando isso, eu sempre uso byref (const shared_ptr &) do que byval (shared_ptr).


Pessoalmente eu usaria uma referência const . Não há necessidade de incrementar a contagem de referência apenas para diminuí-la novamente em nome de uma chamada de função.


shared_ptr não é grande o suficiente, nem seu construtor \ destructor faz o suficiente para ter um overhead suficiente da cópia para se preocupar em passar por referência versus passar pelo desempenho da cópia.







shared-ptr