简体   繁体   中英

Get notified when object goes out of scope/gets invalid

I have an Vector class that can return slices of itself. Those slices will reference to the actual Vector so both the slice and the origin will get modified. A problem would be the following 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
}

The way I want to solve this is simply by throwing exceptions when the user calls methods on the slice object. How can I determine whether or not the original object got out of scope?

There are a couple of ways of solving this.

if you have access to c++11 or boost one way is to use shared and weak pointers. This will force you to instantiate your Vectors as shared pointers but you don't have to implement your own referencing scheme. This also implies that the VectorRef does not own the 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
}

Harald's answer is probably the direction you want to go down but I would like to ask a few more questions about the internal memory management of Vector and VectorBase . It looks like you pass a VectorBase into the VectorRef constructor by value and then expect the reference it creates to still be valid afterwards, do you really want to do this? This implies that the copy constructor of VectorBase shares ownership of its resource and unless you are doing some sophisticated copy-on-write stuff (or even if you are) this is probably a bad thing. Also why is it even taking a VectorBase and not a Vector ? Is VectorBase meant to be an interface? In which case you should definitely be passing in a reference to that constructor instead.

What you seem to want is probably something along the lines of:

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

And then check the std::weak_ptr on each usage as Harald suggests. However I would also consider having VectorRef also have a std::shared_ptr unless that completely defeats the business logic, a slice could conceptually extend the lifetime of the array and would certainly result in more graceful failure occurring than throwing exceptions.

Another thing I would consider is that you mention that this is meant to be part of a maths API in which case I would suggest throwing this entire thing out the window. I expect a maths library to be focused on performance and the overhead of reference counted smart pointers is simply going to be unacceptable. You should concentrate on making it clear in the interface documentation what is and isn't safe usage of your API with respect to taking references to array slices not being valid beyond the lifetime of the array and move the onus of correct memory management onto the user. This is entirely acceptable and is what the standard library does with respect to iterators etc.

As already commented, the problem arises, because the object the reference refers to gets destroyed. That is also why you should only return local variables as copies and not as pointers/references. If you really want to know if an object get out of scope, you could use a noisy destructor.

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

But this doesn't solve the issue, because the object will still be out of scope. I assume you want to work with it later on. So you have some options to fix your problem.

  1. You could return a copy of the object.
  2. You could allocate the object dynamically and return a reference.

Now back to the original question. If you generally want to know if a pointer is invalid, make sure to set him to zero if he is not used anymore, so then you can always check if(Pointer == NULL)//if out of scope . Other than that I don't know any valid method to determine if a reference is invalid. Apart from that it is your duty as a programmer to make sure, that the program doesn't access memory it is not allowed to access.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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