Правильность с умными указателями
Я пытаюсь понять, как лучше const-correctness
работать и, более конкретно, при работе с классами, члены которых основаны на containers
а также smart pointers
, Я думаю, что const-correctness
свойство одинаково независимо от членов класса. Однако, поскольку у меня возникли некоторые трудности с четким пониманием того, что происходит, я решил обратиться к вам за советом.
Итак, вот контекст. У меня есть ShapeContainer
класс, который имеет в качестве частного члена вектор умных указателей. Shape
класс является абстрактным и имеет следующую виртуальную функцию virtual float doSomething();
который затем переопределяется его производными классами. Обратите внимание, что это неконстантная функция класса. Соответствующая часть кода приведена ниже:
class ShapeContainer{
public:
typedef std::shared_ptr<Shape> ShapePtr;
typedef std::vector<ShapePtr> ShapePtrContainer;
// .......
const ShapePtr & operator[]( int ) const { return m_vect[index]; }; // const version
// ShapePtr & operator[]( int ) { return m_vect[index]; }; // non-const version
// .......
private:
ShapePtrContainer m_vect;
};
class Shape{
public:
// ...
virtual float doSomething() = 0;
};
Вот мои вопросы.
Q1. Почему я могу позвонить doSomething()
функционировать следующим образом: int index = 0; float tmp = container1[index]->doSomething();
(с ShapeContainer container1=createBasicShapes();
)?
Из того, что я понимаю, после звонка на const ShapePtr operator[] const
функция, которую мы получим const
указатель на Shape
объект, однако doSomething()
виртуальная функция не постоянна. Итак, как ссылка на const-объект может вызвать неконстантную функцию?
Q2. Позвонив doSomething()
функция как ранее проиллюстрированная (float tmp =container1[index]->doSomething();
) и добавив non-const
версия operator[]
, этот latter
перегруженная версия затем вызывается вместо const-version
один. Почему это так?
Теперь вместо того, чтобы иметь ShapeContainer
класс, теперь у меня есть новый класс с именем ShapeContainerInfo
это все еще имеет vector
но промежуточного ShapeInfo
класс (который имеет умный указатель в качестве члена класса).
class ShapeContainerInfo{
public:
typedef std::vector<ShapeInfo> ShapeContainer;
const ShapeInfo & operator []( int index) const { return m_vect[index]; };
// ShapeInfo & operator []( int index) { return m_vect[index]; }; // non-const version
private:
ShapeContainer m_vect;
};
class ShapeInfo{
public:
typedef std::shared_ptr<Shape> ShapePtr;
// ...
float doSomething(){ return m_ShapePtr->doSomething(); };
private:
ShapePtr m_ShapePtr;
int m_nID;
};
Q3. Когда я звоню float tmp = container2[i].doSomething();
Я получаю следующую ошибку компилятора: error C2662: 'ShapeInfo::doSomething' : cannot convert 'this' pointer from 'const ShapeInfo' to 'ShapeInfo &'
,
Тем не менее, когда я добавляю non-const
перегрузка operator []
ошибка компилятора исчезла. Итак, зачем мне действительно нужно non-const operator[]
за ShapeContainerInfo
и не для ShapeContainer
?
Q4. Если m_vect
private
член ShapeContainerInfo
теперь установлен как public
член и только const-версия operator[]
определяется (не non-const
один), нет сообщений об ошибках компилятора. Почему это? например, после настройки m_vect
быть членом общественного класса: float tmp = info.m_vect[i].doSomething();
Q5. Как я мог правильно определить оба ShapeInfo
а также ShapeContainerInfo
классы такие, что мне нужно только определить const-version
из operator[]
и до сих пор в состоянии позвонить float doSomething()
функционировать?
Для тех из вас, кто интересуется всем примером кода, пожалуйста, найдите его здесь.
Разъяснения, предложения всегда приветствуются:-)
Merci!
1 ответ
Q1: shared_ptr - это const, это не значит, что указанный объект является const. Для этого вы хотели бы shared_ptr<const Shape>
,
Q2: Так как у вас ShapeContainer не был const, неконстантная функция была лучшим соответствием, поэтому она была вызвана вместо константной версии.
Q3: вектор распространяет свою постоянство на свои элементы. shared_ptr нет. Это соответствует поведению массивов и необработанных указателей. Элементы массивов const являются const. То, на что указывает указатель const, не является (обязательно) const.
Q4: Вы говорите, что это не производит ошибку?
ShapeContainerInfo info;
info[0].doSomething();
Пожалуйста, уточните, потому что это должно быть ошибкой.
Q4: Хорошо, вы говорите, что это не выдает ошибку:
ShapeContainerInfo info;
info.m_vect[0].doSomething();
И не должно это. Вектор не является постоянным. Только внутри функции-члена const вектор (и все остальные члены) обрабатываются как const.
Q5: Сделайте m_vect вектором уникальных указателей. Внутри функции const сам вектор будет const, а уникальные указатели будут const. Но объекты, на которые указывают уникальные указатели, будут изменяемыми.
Например, функция set в этом классе недопустима:
struct Foo
{
void set(int index, int value) const
{
v[index] = value;
}
std::vector<int> v;
};
Но этот:
struct Foo
{
void set(int index, int value) const
{
*v[index] = value;
}
std::vector<std::unique_ptr<int>> v;
};