Diferença entre `const shared_ptr <T>` e `shared_ptr <const T>`?

116

Estou escrevendo um método de acesso para um ponteiro compartilhado em C ++ que funciona mais ou menos assim:

class Foo {
public:
    return_type getBar() const {
        return m_bar;
    }

private:
    boost::shared_ptr<Bar> m_bar;
}

Portanto, para oferecer suporte à constância getBar()do tipo de retorno deve ser um boost::shared_ptrque evite a modificação do que Barele aponta. Meu palpite é que esse shared_ptr<const Bar>é o tipo que eu quero retornar para fazer isso, ao passo const shared_ptr<Bar>que impediria a reatribuição do próprio ponteiro para apontar para um diferente, Barmas permitiria a modificação do para o Barqual ele aponta ... No entanto, não tenho certeza. Eu agradeceria se alguém que sabe com certeza pudesse confirmar isso ou me corrigir se eu errar. Obrigado!

Dave Lillethun
fonte
3
É exatamente o que você disse. Você pode consultar a documentação dos operadores *e ->confirmar isso.
syam
2
Qual é a diferença entre T *conste T const *? O mesmo.
3
@ H2CO3 Nem um pouco. O constnormalmente modifica o que _precede, então T *consté um constponteiro para Te T const*é um ponteiro para const T. E é melhor evitar o uso constsem nada antes disso.
James Kanze
6
@JamesKanze, esse é o ponto de H2CO3: a diferença entre T *conste T const *é a mesma que a diferença entre const shared_ptr<T>eshared_ptr<const T>
Jonathan Wakely
1
@JamesKanze Oh, mas sim. T *consté um ponteiro const para não const T, então é const shared_ptr<T>. Em contraste, T const *é um ponteiro não const para const T, então é shared_ptr<const T>.

Respostas:

176

Você está certo. shared_ptr<const T> p;é semelhante a const T * p;(ou, equivalentemente, T const * p;), isto é, o objeto apontado é constenquanto const shared_ptr<T> p;é semelhante ao T* const p;que significa que pé const. Em suma:

shared_ptr<T> p;             ---> T * p;                                    : nothing is const
const shared_ptr<T> p;       ---> T * const p;                              : p is const
shared_ptr<const T> p;       ---> const T * p;       <=> T const * p;       : *p is const
const shared_ptr<const T> p; ---> const T * const p; <=> T const * const p; : p and *p are const.

O mesmo vale para weak_ptre unique_ptr.

Cassio Neri
fonte
1
Você também respondeu uma pergunta que eu tinha na minha cabeça sobre ponteiros regulares (const T * vs. T * const vs. T const *). :) Não mencionei isso porque não queria que minha pergunta sobre o SO fosse muito ampla, e essa era a pergunta pertinente à minha tarefa atual. De qualquer forma, acho que entendo muito bem agora. Obrigado!
Dave Lillethun
9
Estou feliz que ajudou. Uma última dica que uso para me lembrar sobre const T* p;', 'T const * p;e T * const p. Veja o *como um separador no sentido de que o que é consté o que está do mesmo lado do *.
Cassio Neri
5
Minha regra prática é que constsempre se refere ao que está do lado esquerdo. Se nada está à esquerda, é a coisa à direita.
hochl
hochi - que tal const T * p; equivalente a T const * p ;?
Vlad
Cassio, você pode adicionar que no caso do tipo retornado const shared_ptr <T>, ele não pode ser usado em funções não const enquanto isso não for verdadeiro para ponteiros const.
Vlad
2

boost::shared_ptr<Bar const>impede a modificação do Barobjeto por meio do ponteiro compartilhado. Como um valor de retorno, o const in boost::shared_ptr<Bar> constsignifica que você não pode chamar uma função não const no temporário retornado; se fosse por um ponteiro real (por exemplo Bar* const), seria completamente ignorado.

Em geral, mesmo aqui, as regras usuais se aplicam: constmodifica o que o precede: em boost::shared_ptr<Bar const>, o Bar; in boost::shared_ptr<Bar> const, é a instanciação (a expressão boost::shared_ptr<Bar>que é const.

James Kanze
fonte
1
@gatopeich Então você consegue delete.
Marcin
@Marcin você poderia explicar?
gatopeich de
1
#Check this simple code to understand... copy-paste the below code to check on any c++11 compiler

#include <memory>
using namespace std;

class A {
    public:
        int a = 5;
};

shared_ptr<A> f1() {
    const shared_ptr<A> sA(new A);
    shared_ptr<A> sA2(new A);
    sA = sA2; // compile-error
    return sA;
}

shared_ptr<A> f2() {
    shared_ptr<const A> sA(new A);
    sA->a = 4; // compile-error
    return sA;
}

int main(int argc, char** argv) {
    f1();
    f2();
    return 0;
}
vivek2k6
fonte
Posso sugerir o uso de std::make_shared()(desde C ++ 14).
Joel Bodenmann
0

Gostaria de fazer uma demonstração simples baseada na resposta de @Cassio Neri:

#include <memory>

int main(){
    std::shared_ptr<int> i = std::make_shared<int>(1);
    std::shared_ptr<int const> ci;

    // i = ci; // compile error
    ci = i;
    std::cout << *i << "\t" << *ci << std::endl; // both will be 1

    *i = 2;
    std::cout << *i << "\t" << *ci << std::endl; // both will be 2

    i = std::make_shared<int>(3);
    std::cout << *i << "\t" << *ci << std::endl; // only *i has changed

    // *ci = 20; // compile error
    ci = std::make_shared<int>(5);
    std::cout << *i << "\t" << *ci << std::endl; // only *ci has changed

}
Jónás Balázs
fonte