简体   繁体   English

原始指针获取程序的C ++ 11 const正确性

[英]C++11 const correctness for raw pointer getter

I've come across a small issue with const correctness in C++11 which I was hoping I could get clarified--I don't think it has already been asked! 我遇到了一个C ++ 11中具有const正确性的小问题,我希望我能得到澄清-我认为这还没有被问到!

Assume we have a class A, which contains an instance of class B which we want to expose. 假设我们有一个类A,其中包含我们要公开的类B的实例。 If we exposed it as a reference we would provide both const and non-const versions of the getter: 如果我们将其公开作为参考,我们将同时提供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;
};

However if we just had a pointer to B we would provide a getter which was const but returned a copy of the pointer to a non-const instance of B. This is because A doesn't OWN B, it only owns the the pointer, and therefore external modification of B doesn't change the state of A. (Note: I've come to this conclusion from personal experience; I've never found anything explicitly stating this is how this should work) 但是,如果我们只有指向B的指针,我们将提供一个为const的getter,但返回指向B的非const实例的指针的副本。这是因为A并不拥有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;
};

This all makes sense so far, but what do we do if A owns a unique pointer (or shared pointer for that matter) to B? 到目前为止,这一切都说得通,但是如果A拥有指向B的唯一指针(或与此相关的共享指针),我们该怎么办? A now logically owns B--even if not literally. A现在逻辑上拥有B-即使不是字面意义上的。 Up until now I've been following the second example above when exposing a raw pointer to a shared pointer, but since A logically owns B should I be doing something more similar to the first example? 到目前为止,在将原始指针公开给共享指针时,我一直遵循上面的第二个示例,但是由于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;
};

In this case, if your design was correct when A held a B by value, then I'd use the same const/non-const accessors for the case when A holds a B by std::unique_ptr . 在这种情况下,如果当A按值持有B时您的设计是正确的,那么当A通过std::unique_ptr持有B时,我将使用相同的const / non-const访问器。

Why? 为什么? Barring some additional means of ownership transfer away from A (such as a move assignment operator), in both cases A owns a B that will live and die with it. 在两种情况下,除非有一些其他的所有权转移方式(例如移动分配运算符)都离开AA拥有一个BB将与之一起生活和死亡。 That in the latter case the B instance happened to have been allocated separately on the heap and provided to the constructor is immaterial; 在后一种情况下, B实例恰巧是在堆上单独分配并提供给构造函数的,这无关紧要; its lifetime is the same. 它的寿命是相同的。

There's yet another variation of your example that may help settle your vacillation, assuming that we don't need to deal with the possibility of A::m_b being null: 假设我们不需要处理A::m_b为null的可能性,那么您的示例还有另一种变体可以帮助您解决波动问题:

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

Here, A clearly owns its B , and offers no way to transfer ownership. 在此, A显然拥有其B ,并且没有办法转让所有权。 I don't know why we'd allocate B on the heap here, but we can just to make a point. 我不知道为什么要在这里在堆上分配B ,但是我们只能提出一点。 (Note that now A::GetB() returns by reference rather than pointer.) With this example, would you still have a hard time deciding how to write A::GetB() and whether to overload it on const? (请注意,现在A::GetB()通过引用而不是指针返回。)在此示例中,您仍然很难决定如何编写A::GetB()以及是否在const上重载它吗?

While I do not have much authority to answer design questions, I would use something akin to has been proposed in @seh's answer , with the addition of a way to check for the null value of m_b : 尽管我没有足够的权限回答设计问题,但我会使用类似于@seh的答案中提出的内容,并增加了一种检查m_b的空值的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;
};

If you really can't do without pointers (legacy API), then it is more of a grey line. 如果您真的不能没有指针(旧版API),那么它更像是一条灰线。 I would declare both const B* GetB() const and B* GetB() , since a common convention in C++11 is that raw pointers can be used to represent non-owning pointers . 我会声明const B* GetB() constB* GetB() ,因为C ++ 11中的一个通用约定是原始指针可用于表示非所有者指针 Ultimately this would have to been indicated in documentation, though. 最终,这必须在文档中指出。

For an ownership situation it would IMHO be unnatural if a member function could allow modification of a part of a logically const object, but that's a design issue. 对于所有权情况,如果成员函数可以允许修改逻辑const对象的一部分,恕我直言是不自然的,但这是设计问题。

However, do note that a raw pointer can be used to implement ownership. 但是,请注意, 可以使用原始指针来实现所有权。

For example, consider an internal array implemented with direct new -ing and raw pointer. 例如,考虑使用直接newnew指针实现的内部数组。 You would not want an interface that would break when/if that was replaced with a std::vector for handling the storage. 您不希望当/如果用std::vector替换该接口来处理存储时会中断的接口。 And so the rule for whether to provide a const /non- const pair of accessor functions must be rooted in the design level, not in what the language permits for a given implementation of the design. 因此,是否提供访问函数的const /非const对的规则必须植根于设计级别,而不是语言在给定的设计实现中所允许的语言。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM