Вопрос по c++, const, shared-ptr – Смарт-указатель C ++

41

У меня есть несколько контейнеров в классе, например, vector или map, которые содержат shared_ptr для объектов, находящихся в куче.

Например

template <typename T>
class MyExample
{
public:

private:
 vector<tr1::shared_ptr<T> > vec;
 map<tr1::shared_ptr<T> , int> h;
};

Я хочу иметь открытый интерфейс этого класса, который иногда возвращает shared_ptrs для const объектов (черезshared_ptr<const T>) и иногдаshared_ptr<T> где я позволяю звонящему мутировать объекты. Мне нужна логическая корректность const, поэтому, если я помечаю метод как const, он не может изменить объекты в куче.

Вопросов:

1) меня смущает взаимозаменяемостьtr1::shared_ptr<const T> а такжеtr1::shared_ptr<T>, Когда кто-то проходитshared_ptr<const T> shared_ptr в классе, хранить ли его какshared_ptr<T> или жеshared_ptr<const T> внутри вектора и карты или я могу изменить карту, векторные типы (например, insert_elemeent (shared_ptr<const T> объект)?

2) Лучше ли создавать экземпляры классов следующим образом:MyExample<const int> ? Это кажется чрезмерно ограничительным, потому что я никогда не смогу вернутьshared_ptr<int> ?

Ваш Ответ

4   ответа
11

Я бы предложил следующую методологию:

template <typename T>
class MyExample
{
  private:
    vector<shared_ptr<T> > data;

  public:
    shared_ptr<const T> get(int idx) const
    {
      return data[idx];
    }
    shared_ptr<T> get(int idx)
    {
      return data[idx];
    }
    void add(shared_ptr<T> value)
    {
      data.push_back(value);
    }
};

Это обеспечивает постоянство правильности. Как вы видите, метод add () использует не <const T>, а <T>, потому что вы намерены хранить класс Ts, а не const Ts. Но когда вы обращаетесь к нему const, вы возвращаете <const T>, что не проблема, так как shared_ptr <T> может быть легко преобразован в shared_ptr <const T>. И так как оба метода get () возвращают копии shared_ptr во внутреннем хранилище, вызывающий не может случайно изменить объект, на который указывают ваши внутренние указатели. Это все сравнимо с не-умным вариантом указателя:

template <typename T>
class MyExamplePtr
{
  private:
    vector<T *> data;

  public:
    const T *get(int idx) const
    {
      return data[idx];
    }
    T *get(int idx)
    {
      return data[idx];
    }
    void add(T *value)
    {
      data.push_back(value);
    }
};
Разве это не должно бытьshared_ptr<T> может быть легко преобразовано в shared_ptr <const T> `, а не наоборот? user231536
При возврате просто простого члена перегрузка кажется не такой уж большой проблемой. Но что, если вы вернетеshared_ptr изvector - где вы должны рассчитать, какой из них правильный. Как избежать дублирования кода? thomthom
36

shared_ptr<T> а такжеshared_ptr<const T> находятсяне взаимозаменяемы. Это идет в одну сторону -shared_ptr<T> конвертируется вshared_ptr<const T> но не наоборот.

Заметим:

// f.cpp

#include <memory>

int main()
{
    using namespace std;

    shared_ptr<int> pint(new int(4)); // normal shared_ptr
    shared_ptr<const int> pcint = pint; // shared_ptr<const T> from shared_ptr<T>
    shared_ptr<int> pint2 = pcint; // error! comment out to compile
}

скомпилировать через

cl / EHsc f.cpp

Вы также можете перегрузить функцию на основе константности. Вы можете объединить эти два факта, чтобы сделать то, что вы хотите.

Что касается вашего второго вопроса,MyExample<int> вероятно, имеет больше смысла, чемMyExample<const int>.

5

shared_ptr<const T> вы никогда не сможете изменитьT, Конечно, технически возможно разыгратьconst T простоT, но это нарушает намерение сделатьT const, Поэтому, если вы хотите, чтобы люди могли добавлять объекты в ваш класс, они должны давать вамshared_ptr<T> и нетshared_ptr<const T>, Когда вы возвращаете вещи из вашего класса, вы не хотите изменять, то есть когда вы используетеshared_ptr<const T>.

shared_ptr<T> может быть автоматически преобразован (без явного приведения) вshared_ptr<const T> но не наоборот. Это может помочь вам (и вы должны это сделать в любом случае) свободно использоватьconst методы. Когда вы определяете метод классаconstкомпилятор не позволит вам изменять какие-либо элементы данных или возвращать что-либо кромеconst T, Таким образом, использование этих методов поможет вам убедиться, что вы что-то не забыли, и поможет пользователям вашего класса понять, какова цель метода. (Пример:virtual shared_ptr<const T> myGetSharedPtr(int index) const;)

Вы правы в своем втором утверждении, вы, вероятно, не хотите создавать экземпляр класса как<const T>, так как вы никогда не сможете изменить любой из вашихTs.

3

tr1::shared_ptr<const T> имитирует функциональностьT const * а именно то, на что он указывает, является const, а сам указатель - нет.

Таким образом, вы можете назначить новое значение для вашего общего указателя, но я ожидаю, что вы не сможете использовать разыменованныйshared_ptr как значение l

«Л-значение». Значение l не обязательно должно присваиваться! curiousguy

Похожие вопросы