Is this a great std :: auto_ptr <> script?

Suppose I have a function that takes a pointer as a parameter. This function may throw an exception because it uses std::vector<>::push_back() to control the life cycle of this pointer. If I declare it like this:

 void manage(T *ptr); 

and name it as follows:

 manage(new T()); 

if it throws an exception by clicking on the pointer to std::vector<> , I really got a memory leak, right?

Declares a function as follows:

 void manage(std::auto_ptr<T> ptr); 

solve my problem?

I would expect it to first allocate std::auto_ptr on the stack (something that I suppose could never throw an exception) and let it acquire ownership of the pointer. Safe

Then inside the function I would press the raw pointer to std::vector<> , which is also safe: if this fails, the pointer will not be added, but the smart pointer will still own the pointer so that it is destroyed. If the click succeeds, I will remove the ownership of the smart pointer above that pointer and return it: this cannot throw an exception, so it will always be ok.

Are my theories correct?

- Change -

No, I think I can’t do this. To do this, you need to take a non-constant link to rvalue (to take property from the smart pointer). I would have to write

 std::auto_ptr<T> ptr(new T()); manage(ptr); 

for this, something that is completely uncomfortable in my case. I am writing this so that I can implement RAII without polluting the code much. Then it would not help. This will be trick 22.

- Change 2 -

Pulling out what Jason Orendorf said, here, for quick reference to readers, the final solution is as follows:

 void manage(T *ptr) { std::auto_ptr<T> autoPtr(ptr); vector.push_back(ptr); autoPtr.release(); } 

This solves the problem of a useless non-constant rvalue reference.

When I finish this class, I will code, I will post it here if anyone finds it useful.

- Change 3 -

Well, there has been a lot of discussion, and there are key points that I should have clarified before. In general, when I post to stackoverflow, I try to explain the reason for my questions and, in general, is completely useless. So this time I thought that I should go straight to the point. Turns out it doesn't work very well XD

Unfortunately, my brain is now stalled, so I think that I can’t even explain correctly what I first decided to do for my purposes. I am trying to find a good solution for atomic operations and a code-safe code entry that is suitable for many cases, but in fact, I cannot handle it. XD I think this is something that I will only learn with time.

I am truly a new C ++ programmer and I am focused on game development. When an exception is thrown into the game engine, this is the end of the execution. The system will free up all the memory for my process, so it doesn’t matter if one or two pointers flow here and there. Now, when I develop a server application, it is difficult for me to deal with exceptions, because an exception cannot lead to a server crash; he should "collapse the request."

That is: "Well, the client, unfortunately, the developers did not foresee this condition, so you will have to try it later (until now it is basically the same as with the game engine, nothing happens but is not isolated from the context only request, not the whole process.) But do not panic, because everything is left in the correct state (here is one of the differences. so the operating system cannot free resources for you, in addition, you should pay attention to canceling operations so far, so you don’t completely block the user account broadcaster, for example, or even a full server service). ".

I will just write more and write down my problems so that next time I can write a better question. I was not ready to ask it now, I'm sorry.

Thanks so much for your answers, I really like stackoverflow. It is absolutely amazing how quickly my questions are answered and how your answers are explained. Thanks.

+4
source share
4 answers

I think enough discussion has been adopted to guarantee another answer.

First, in order to answer the factual question, yes, it is absolutely necessary (and even necessary!) To pass the argument to an intelligent pointer when the transfer of ownership takes place. Passing with a smart pointer is a common idiom for doing this.

 void manage(std::auto_ptr<T> t) { ... } ... // The reader of this code clearly sees ownership transfer. std::auto_ptr<T> t(new T); manage(t); 

Now there is a very good reason why all smart pointers have explicit constructors. Consider the following function (mentally replace std::auto_ptr with boost::shared_ptr if it tickles your imagination):

 void func(std::auto_ptr<Widget> w, const Gizmo& g) { ... } 

If std::auto_ptr had an implicit constructor, then suddenly this code would compile:

 func(new Widget(), gizmo); 

What about him? Almost immediately from Effective C ++, paragraph 17:

 func(new Widget(), a_function_that_throws()); 

Because in C ++ the order of evaluating arguments is undefined, you can very well evaluate arguments in the following order: constructor new Widget() , a_function_that_throws() , std::auto_ptr . If the function is thrown, you have a leak.

Therefore, all resources to be released must be wrapped when built in RAII classes before being passed to the function. This means that all smart pointers must be constructed before passing them as an argument to a function. Creating smart pointers must be constructive for copying using a const reference or implicitly constructive, which would encourage unsafe code. Explicit build provides more secure code.

Now, why don't you do something like this?

 void manage(T *ptr) { std::auto_ptr<T> autoPtr(ptr); vector.push_back(ptr); autoPtr.release(); } 

As already mentioned, the idioms of the interface tell me that I can pass in the pointer that I have and I can delete it. So, nothing prevents me from doing this:

 T item; manage(&t); // or manage(&t_class_member); 

Which, of course, is disastrous. But you would say: "Of course, I know what the interface means, I would never use it that way." However, you can add an additional function argument later. Or someone (and not you, or you, 3 years later), follow this code, they may not see it like you do.

  • This hypothetical “someone else” can only see the headline without comment and (rightfully) suggest the absence of transfer of ownership.
  • They can see how this function is used in other code and replicates the use without looking at the header.
  • They may use code autocompletion to call a function, rather than reading a comment or function and assume that ownership is not transferred.
  • They can write a function that wraps your manage function, and yet someone else will use the wrapper function and skip the documentation of the original function.
  • They may try to “expand” your code so that all old code compiles (and automatically becomes unsafe):

     void manage(T *t, const std::string tag = some_function_that_throws()); 

As you can see, the explicit construction of a smart pointer makes it very difficult to write unsafe code in the above cases.

Therefore, I would not recommend switching to decades of C ++ experience to make an understandable “more enjoyable” and “fun” API.

My 2c.

+1
source

You could do it this way, but you still have to clean up if an exception is thrown, which seems a bit onerous.

If you use something like boost :: shared_ptr (I believe that something like this is also in the TR1 libraries - see the MS implementation for an example), you can forget about the need for cleaning in case of things that are planned.

To do this, you need your vector to accept an instance of boost::shared_ptr < T > , then you would just let it clear your original instance and leave the instance in the vector alive if everything works out well. In case everything goes wrong, all instances of boost::shared_ptr will be destroyed, and you still won't lose the leak.

With smart pointers, he chooses the one that matches the task, and in this case, joint ownership (or simple transfer of ownership) seems to be the goal, so most platforms have better candidates than std :: auto_ptr.

+4
source

In general, using std::auto_ptr as a type of function argument unambiguously tells the caller that the function will own the object and will be responsible for deleting it. If your function matches this description, then in any case, use auto_ptr there, regardless of any other reason.

+4
source

Yes, that's great. (see edit below.) (jkp may see something that I missed, but I don’t think that you "still have to clear if an exception is thrown" because, as you say, in this case auto_ptr will delete the object for you.)

But I think it would be better to hide authptig shenanigans from the caller:

 void manage(T *t) { std::auto_ptr<T> p(t); // to delete t in case push_back throws vec.push_back(t); p.release(); } 

EDIT: I originally wrote “Yes, that's fine,” referring to the original manage(auto_ptr<T>) plan manage(auto_ptr<T>) , but I tried it and found that it was not working. The constructor auto_ptr<T>::auto_ptr(T *) is explicit . The compiler will not allow you to write manage(new T) , because it cannot implicitly convert this pointer to auto_ptr . manage(T *) is a friendly interface!

+1
source

All Articles