当对象超出范围/变得无效时收到通知

时间:2014-10-04 15:15:55

标签: c++ reference

我有一个可以返回自身切片的Vector类。那些切片将引用实际的Vector,因此切片和原点都将被修改。问题在于以下codesnippet:

class VectorBase {
    // Offers basic methods
}

class Vector : VectorBase {
    // An actual vector
}

class VectorRef : VectorBase {
    // References parts of another vector
    // Constructor creates a slice of the base vector
    VectorRef(VectorBase base, int start, int end);
}

void foo () {
    Vector* a = new Vector();
    VectorRef b(*a, 1,4);
    delete a;
    b.length(); // <---- Problem because a isn't valid anymore
}

我想解决这个问题的方法就是在用户调用切片对象上的方法时抛出异常。如何确定原始对象是否超出范围?

3 个答案:

答案 0 :(得分:2)

有几种方法可以解决这个问题。

如果您有权访问c++11boost,则一种方法是使用共享和弱指针。这将强制您将Vector实例化为共享指针,但您不必实现自己的引用方案。这也意味着VectorRef不拥有Vector。

class VectorRef : VectorBase {
    // References parts of another vector
    // Constructor creates a slice of the base vector
    VectorRef(std::shared_ptr<VectorBase> base, int start, int end) : 
       m_base(base), m_start(start), m_end(end)
    {
    }

    std::weak_ptr<VectorBase> m_base;

    length() override
    {
      if (auto base = m_base.lock()) { // Has to be copied into a shared_ptr before usage
      {
        return m_end-m_start;
      }
      else
      {
        trow(VectorException("Base has expired"));
      }
    }
}

void foo () {
    std::shared_ptr<Vector> a = std::make_shared<Vector>();
    VectorRef b(a, 1,4);
    a.reset();
    b.length(); // Will throw an exception
}

答案 1 :(得分:1)

Harald的回答可能是您想要关注的方向,但我想再问一些关于VectorVectorBase的内部记忆管理的问题。看起来你按VectorBase将值VectorRef传递给VectorBase构造函数,然后期望它创建的引用在之后仍然有效,你真的想这样做吗?这意味着VectorBase的拷贝构造函数共享其资源的所有权,除非你正在做一些复杂的写时复制(或者即使你是),这可能是一件坏事。另外,为什么它甚至采用Vector而不是VectorBasetemplate <class T> class Vector { std::shared_ptr<std::vector<T>> m_stuff; ... } template < class T> VectorRef { std::weak_ptr<std::vector<T>> m_ref; VectorRef(const Vector<T>& v, size_t begin, size_t end); ... } 是否意味着是一个界面?在这种情况下,您肯定应该传递对该构造函数的引用。

你似乎想要的东西可能是:

std::weak_ptr

然后按照Harald的建议检查每个用法的VectorRef然而我还会考虑让std::shared_ptr也有一个{{1}},除非完全违背业务逻辑,一个切片可以在概念上延长数组的生命周期,肯定会带来更多发生优雅失败而不是抛出异常。

我要考虑的另一件事是你提到这是数学API的一部分,在这种情况下,我建议将整个事情抛到窗外。我希望数学库能够专注于性能,并且引用计数智能指针的开销简直是不可接受的。您应该集中精力在界面文档中明确说明API的安全使用方式,以便在数组生命周期内引用数组切片时无效,并将正确的内存管理责任转移到用户。这是完全可以接受的,也就是标准库对迭代器等的作用。

答案 2 :(得分:0)

正如已经评论过的那样,问题出现了,因为引用引用的对象被破坏了。这也是为什么你只应该将局部变量作为副本而不是作为指针/引用返回的原因。如果你真的想知道对象是否超出范围,你可以使用嘈杂的析构函数。

~Vector() { std::cout << "Noisy Destructor, reports when an object is destroyed."; }

但是这并没有解决问题,因为对象仍然不在范围内。我想你以后想要使用它。所以你有一些选择来解决你的问题。

  1. 您可以返回该对象的副本。
  2. 您可以动态分配对象并返回引用。
  3. 现在回到原来的问题。如果您通常想知道指针是否无效,请确保在不再使用指针时将其设置为零,这样您就可以随时检查if(Pointer == NULL)//if out of scope。除此之外,我不知道任何有效的方法来确定引用是否无效。除此之外,作为程序员,您有责任确保程序不访问内存,不允许访问。