Правильность с умными указателями

Я пытаюсь понять, как лучше 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_vectprivate член 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;
};    
Другие вопросы по тегам