правильность с умными указателями - PullRequest
2 голосов
/ 09 января 2012

Я пытаюсь лучше понять, как работает 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. Итак, как ссылка на 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 member и только определена const-версия operator[] (не non-const one), то есть нет сообщений об ошибках компилятора. Почему это? например после установки m_vect в качестве участника публичного класса: float tmp = info.m_vect[i].doSomething();

Q5. Как я могу правильно определить классы ShapeInfo и ShapeContainerInfo, чтобы мне нужно было определить только const-version из operator[] и при этом иметь возможность вызывать функцию float doSomething()?

Для тех, кто интересуется всем примером кода, вы можете найти его здесь .
Разъяснения, предложения всегда приветствуются :-) Merci!

1 Ответ

3 голосов
/ 09 января 2012

Q1: shared_ptr - это const, это не значит, что указанный объект является const.Для этого вы хотели бы shared_ptr<const Shape>.

Q2: Так как вы ShapeContainer не были константными, неконстантная функция была более подходящей, поэтому ее вызывали вместо константной версии.

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;
};    
...