C ++ 11 const原始指针getter的正确性

时间:2014-10-23 12:05:07

标签: c++ pointers c++11 const

我在C ++ 11中遇到了一个关于const正确性的小问题,我希望我能澄清一下 - 我认为它还没有被问过!

假设我们有一个A类,它包含我们想要公开的B类实例。如果我们将它作为参考公开,我们将提供getter的const和非const版本:

class B;

class A final
{
public:
    B& GetB() 
    {
        return m_b;
    }
    const B& GetB() const 
    {
        return m_b;
    }
private:
    B m_b;
};

但是如果我们只有一个指向B的指针,我们将提供一个const的getter,但是返回指向B的非const实例的指针的副本。这是因为A不是OWN B,它只拥有指针,因此B的外部修改不会改变A的状态。(注意:我从个人经验中得出这个结论;我从未发现任何明确说明这应该如何工作的事情)

class B;

class A final
{
public:
    A(B* b) 
    {
        m_b = b;
    }
    A* GetB() const
    {
        return m_b;
    }
private:
    B* m_b;
};

到目前为止,这一切都有意义,但如果A拥有B的唯一指针(或共享指针),我们该怎么办? A现在逻辑上拥有B - 即使不是字面上的。到目前为止,当我将一个原始指针暴露给共享指针时,我一直在关注上面的第二个例子,但是由于A逻辑上拥有B,我应该做一些与第一个例子更相似的事情吗?

class B;

class A final
{
public:
    A(std::unique_ptr<B> b)
    {
        m_b = std::move(b);
    }
    B* GetB()
    {
        return m_b.get();
    }
    const B* GetB() const
    {
        return m_b.get();
    }
private:
    std::unique_ptr<B> m_b;
};

3 个答案:

答案 0 :(得分:3)

在这种情况下,如果您的设计在A按值B时是正确的,那么我会在A时使用相同的const /非const访问器按B保留std::unique_ptr

为什么呢?除了A之外的一些其他所有权转移方式(例如移动分配运算符),A拥有一个B生存并死亡的B。在后一种情况下,A::m_b实例碰巧已在堆上单独分配并提供给构造函数并不重要;它的寿命是一样的。

假设我们不需要处理class B { // ... }; class A final { public: A() : m_b(std::make_unique<B>()) {} B& GetB() { return *m_b; } const B& GetB() const { return *m_b; } private: std::unique_ptr<B> m_b; }; 为空的可能性,那么你的例子的另一个变体可能有助于解决你的动摇:

A

此处,B明显拥有其B,并且无法转让所有权。我不知道为什么我们会在堆上分配A::GetB(),但我们可以说明一点。 (注意,现在A::GetB()通过引用而不是指针返回。)在这个例子中,你是否仍然很难决定如何写{{1}}以及是否在const上重载它?

答案 1 :(得分:2)

虽然我没有太多权限回答设计问题,但我会使用类似于@ seh&#39; answer中提出的内容,并添加了一种检查空值的方法。 m_b

class B
{
  // ...
};

class A final
{
public:
  A(std::ptr<B> b) : m_b(std::move(b))
  {}

  bool HasB()
  {
    return m_b != nullptr;
  }

  B& GetB()
  {
    return *m_b;
  }

  const B& GetB() const
  {
    return *m_b;
  }

 private:
   std::unique_ptr<B> m_b;
};

如果你真的不能没有指针(遗留API),那么它更像是一条灰线。我会声明const B* GetB() constB* GetB(),因为C ++ 11中的常见约定是raw pointers can be used to represent non-owning pointers。不过,最终必须在文档中说明这一点。

答案 2 :(得分:1)

对于所有权情况,如果成员函数可以允许修改逻辑const对象的一部分,那么IMHO会不自然,但这是一个设计问题。

但是,请注意原始指针可以用于实现所有权。

例如,考虑使用直接new和原始指针实现的内部数组。如果用std::vector替换它来处理存储,你不会想要一个会破坏的接口。因此,是否提供const /非const对访问函数的规则必须植根于设计级别,而不是根据给定的语言允许实施设计。