智能指针的 const 正确性

Posted

技术标签:

【中文标题】智能指针的 const 正确性【英文标题】:const correctness with smart pointers 【发布时间】:2012-01-09 19:02:45 【问题描述】:

我试图更好地理解const-correctness 是如何工作的,更具体地说,在处理其成员基于containerssmart 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;

;

这是我的问题。

第一季度。为什么我可以通过以下方式调用doSomething() 函数:int index = 0; float tmp = container1[index]-&gt;doSomething();(具有ShapeContainer container1=createBasicShapes();)? 据我了解,在调用const ShapePtr operator[] const 函数后,我们将得到一个const 指向Shape 对象的指针,但是doSomething() 是虚拟的 函数是不是 const。那么,对 const 对象的引用如何调用非 const 函数呢?

第二季度。通过调用 doSomething() 函数,如前所述 (float tmp =container1[index]-&gt;doSomething();)通过添加 operator[]non-const 版本,此 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;
;

第三季度。当我调用float tmp = container2[i].doSomething(); 时,我收到以下编译器错误:error C2662: 'ShapeInfo::doSomething' : cannot convert 'this' pointer from 'const ShapeInfo' to 'ShapeInfo &amp;'。 但是,当我添加重载operator []non-const 版本时,编译器错误消失了。那么,为什么我真的需要 non-const operator[] 来代替 ShapeContainerInfo 而不是 ShapeContainer

第四季度。如果ShapeContainerInfom_vect private 成员现在设置为public 成员并且仅 定义了operator[] 的常量版本(不是non-const 之一),那么没有编译器错误消息。为什么这个?例如将m_vect设置为公共类成员后:float tmp = info.m_vect[i].doSomething();

Q5。如何正确定义ShapeInfoShapeContainerInfo 类,这样我只需要定义operator[]const-version 并且仍然能够调用float doSomething() 函数?

对整个示例代码感兴趣的人,请找到它here。 澄清,建议总是受欢迎的:-) 谢谢!

【问题讨论】:

【参考方案1】:

Q1:shared_ptr 是 const,这并不意味着指向的对象是 const。为此,您需要shared_ptr&lt;const Shape&gt;

Q2:由于你的 ShapeContainer 不是 const,所以 non-const 函数更匹配,所以调用它而不是 const 版本。

Q3:vector 将其 constness 传播到其元素。 shared_ptr 没有。这与数组和原始指针的行为是一致的。 const 数组的元素是 const。 const 指针所指向的东西(必然)不是 const。

Q4:你是说这不会产生错误吗?

ShapeContainerInfo info;
info[0].doSomething();

请澄清,因为那应该是一个错误。

Q4:好的,所以你说这不会产生错误:

ShapeContainerInfo info;
info.m_vect[0].doSomething();

也不应该。向量不是常量。只有在 const 成员函数内部,vector(和所有其他成员)才被视为 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;
;    

【讨论】:

@本杰明,谢谢。关于您对Q3 的回答,我将std::vector 定义为non-const,或者在代码中的确切位置,vector 是否被视为const?你对Q4/Q5有什么建议吗? @Tin:由于您的成员函数是 const,因此该类的所有成员在该函数内都被视为 const。我还在分析 Q5。 @本杰明,不是真的,这是我的时候:float tmp = info.m_vect[i].doSomething();在这种情况下,我将 m_vect 设置为公共类成员而不是私有成员,除此之外,我只考虑了 operator[] 的const version @Benjamin,关于Q3,我在某处读到返回objects by reference 通常不是一个好主意。不幸的是,我没有找到链接。你怎么看这个肯定?此外,关于Q4,这意味着,由于向量是non-const,也是它的元素,那么我们可以调用non-const函数,比如doSomething,对吧?如果你能帮助Q5,那就太好了;-) @Tin: SO cmets 不是讨论的好地方,答案也不是那么简单。如果您想问这个问题,请打开一个新帖子。已添加对 Q5 的回答。

以上是关于智能指针的 const 正确性的主要内容,如果未能解决你的问题,请参考以下文章

C++ const 正确性漏洞或意外使用?

正确地使用智能指针

使用std智能指针的正确方法来确保ptr安全

从类成员返回智能指针的正确方法?

当它的目标应该被删除时,为啥这个智能指针会给出正确的结果?

在具有智能指针的类上正确实现复制构造函数和等于运算符