Разница между `const shared_ptr <T>` и `shared_ptr <const T>`?

116

Я пишу метод доступа для общего указателя на C ++, который выглядит примерно так:

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

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

Таким образом, для поддержки константности getBar()возвращаемого типа должен быть объект boost::shared_ptr, предотвращающий изменение того, на что Barон указывает. Я предполагаю , что shared_ptr<const Bar>это тот тип, который я хочу вернуть, чтобы сделать это, тогда как const shared_ptr<Bar>это предотвратит переназначение самого указателя, чтобы он указывал на другой, Barно разрешил изменение того, на Barчто он указывает ... Однако я не уверен. Буду признателен, если кто-то, кто знает наверняка, подтвердит это или поправит меня, если я ошибся. Спасибо!

Дэйв Лиллетан
источник
3
Это именно то, что вы сказали. Вы можете посмотреть документацию для операторов *и ->в этом убедиться.
syam
2
В чем разница между T *constи T const *? Такой же.
3
@ H2CO3 Вовсе нет. В constобычно модифицирует , что _precedes, поэтому T *constявляется constуказателем T, и T const*является указателем const T. И лучше избегать использования, если ему constничего не предшествует.
Джеймс Канце
6
@JamesKanze, это точка зрения H2CO3: разница между T *constи T const *такая же, как и разница между const shared_ptr<T>иshared_ptr<const T>
Джонатан Уэйкли,
1
@JamesKanze О, но да. T *constконстантный указатель на неконстантный T, так и есть const shared_ptr<T>. В отличии от этого , T const *не является константным указателем на const T, так shared_ptr<const T>.

Ответы:

176

Ты прав. shared_ptr<const T> p;похож на const T * p;(или, что то же самое, T const * p;), то есть заостренный объект подобен constтогда const shared_ptr<T> p;как подобен, T* const p;что означает, что pесть const. В итоге:

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.

То же самое weak_ptrи для и unique_ptr.

Кассио Нери
источник
1
Вы также ответили на вопрос, который у меня в голове был о регулярных указателях (const T * vs. T * const vs. T const *). :) Я не упомянул об этом, потому что не хотел, чтобы мой вопрос о SO был слишком широким, и это был вопрос, относящийся к моей текущей задаче. Во всяком случае, я думаю, что теперь очень хорошо понимаю. Спасибо!
Dave Lillethun
9
Я рад, что это помогло. Последний совет, который я использую, чтобы помнить о const T* p;', 'T const * p;и T * const p. Рассматривайте *как разделитель в том смысле, что то, что есть, constнаходится на той же стороне *.
Cassio Neri
5
Мое практическое правило заключается в том, что constвсегда относится к предмету слева от него. Если слева ничего нет, значит, это вещь справа.
hochl 03
хоти - как насчет const T * p; эквивалентно T const * p ;?
Влад
Кассио, вы можете добавить, что в случае возвращаемого типа const shared_ptr <T> его нельзя использовать в неконстантных функциях, в то время как это неверно для константных указателей.
Влад
2

boost::shared_ptr<Bar const>предотвращает изменение Barобъекта через общий указатель. В качестве возвращаемого значения константа in boost::shared_ptr<Bar> constозначает, что вы не можете вызвать неконстантную функцию для возвращаемой временной; если бы это был реальный указатель (например Bar* const), он был бы полностью проигнорирован.

В общем, даже здесь действуют обычные правила: constизменяет то, что ему предшествует: in boost::shared_ptr<Bar const>, the Bar; in boost::shared_ptr<Bar> const, это экземпляр (выражение boost::shared_ptr<Bar>const.

Джеймс Канце
источник
1
@gatopeich Так ты можешь delete.
Marcin
@Marcin не могли бы вы рассказать?
gatopeich
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
источник
Могу я предложить использовать std::make_shared()(начиная с C ++ 14).
Joel Bodenmann
0

Я хотел бы провести простую демонстрацию на основе ответа @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

}
Йонас Балаж
источник