Refactoring Code for Using Common Boost Pointers

I wrote a project using regular pointers, and now I'm tired of manual memory management.

What are the problems you might expect during refactoring?

So far, I have already spent an hour replacing X* with shared_ptr<X> for the types that I want to automatically manage memory. Then I changed dynamic_cast to dynamic_pointer_cast . I still see many more errors (compared to NULL by passing this function).

I know that the question is a little vague and subjective, but I think I can take advantage of the experience of someone who has already done this.

Are there any pitfalls?

+4
source share
5 answers

There is a tool that tries to automatically convert to smart pointers. I have never tried. Here is a quote from an abstract of the following work: http://www.cs.rutgers.edu/~santosh.nagarakatte/papers/ironclad-oopsla2013.pdf

To ensure the security of properties that are difficult to check statically, Ironclad C ++ applies dynamic checks using template “smart” pointers. Using a semi-automatic refactoring tool, we ported almost 50 thousand lines of code for Ironclad C ++

0
source

Although it’s easy to just use boost::shared_pointer everywhere, you should use the right smart pointer according to the semantics of ownership.

In most cases, you will need to use std::unique_ptr by default if ownership is not shared between multiple instances of objects.

If you run into problems with cyclic ownership, you can break the loops into boost::weak_ptr .

Also keep in mind that when passing shared_ptr you should always pass them by const link for performance reasons (avoid atomic increment) if you really don't want to transfer ownership of another object.

+4
source

Are there any pitfalls?

Yes, according to the law of murphy, if you blindly replace each pointer with shared_ptr, it turns out that this is not what you wanted, and you will spend the next 6 months of errors that you entered.

What are the problems you might expect during refactoring?

Ineffective memory management, unused resources are stored for longer than necessary, memory leaks (circular links), invalid reference counting (the same pointer assigned to several other shared_pointers).

DO NOT blindly replace everything with shared_ptr. Examine the structure of the program carefully and make sure that shread_ptr is NECESSARY and it represents EXACTLY what you want.

Also, make sure you use version control that supports easy forking (git or mercurial), so when you break something, you can revert to the previous state or run something similar to “git bisect” to find a problem.

obviously you need to replace X * with shared_ptr

Wrong. It depends on the context. If you have a pointer that points to the middle of some array (say, for manipulating pixel data), then you cannot replace it with shared_ptr (and you won’t need it). You should use shared_ptr only when you need to ensure that the object is automatically freed. Automatically freeing an object is not always what you want.

+3
source

If you want to use boost, you should consider whether you want boost :: shared_ptr or boost :: scoped_ptr. Shared_ptr is a resource that should be shared between classes, while scoped_ptr is more like what you might need (at least in some places). Scoped_ptr will automatically delete memory when it goes out of bounds.

Be careful when passing shared_ptr to a function. The general rule with shared_ptr is to pass by value, so a copy is created. If you pass it by reference, the reference count of the pointer will not be increased. In this case, you can delete the part of the memory that you wanted to save.

However, there is a case where you may need to pass shared_ptr by reference. That is, if you want the memory to be allocated inside another function. In this case, just make sure that the caller still contains a pointer to the lifetime of the called function.

 void allocPtr( boost::shared_ptr< int >& ptrByRef ) { ptrByRef.reset( new int ); *ptrByRef = 3; } int main() { boost::shared_ptr< int >& myPointer; // I want a function to alloc the memory for this pointer. allocPtr( myPointer ); // I must be careful that I still hold the pointer // when the function terminates std::cout << *ptrByRef << std::endl; } 
+2
source

I list the steps / problems. They worked for me, but I cannot guarantee that they are 100% correct.

0) check if there can be cyclic common pointers. If so, could this lead to a memory leak? In my case, fortunately, loops should not be broken, because if I had a loop, the objects in the loop would be useful and should not be destroyed. use weak pointers to break loops

1) you need to replace "most" X* with shared_ptr<X> . Shared_ptr (only?) Created immediately after each dynamic distribution of X. In any case, this is a copy built or built with a null pointer (for a NULL signal). To be safe (but a little inefficient), pass these shared_ptrs by reference only. In any case, you probably never passed your pointers by reference to start with => no additional changes are required

2), you could use dynamic_cast<X*>(y) in some places. replace this with dynamic_pointer_cast<X>(y)

3) where you passed NULL (for example, to signal a calculation failure), pass an empty common pointer.

4) delete all removal instructions for the corresponding types

5) make your base class B inherited from enable_shared_from_this<B> . Then, wherever you go, this , go, shared_from_this() . You may need to perform static casting if the function expects a derived type. keep in mind that when you call shared_from_this() , some shared_ptr should already have this . In particular, do not call shared_from_this() in the class constructor

I am sure that you can semi-automate this process to get semantically equivalent, but not necessarily very efficient code. The programmer probably only needs to talk about a circular reference (if any).

In many of these steps, I have repeatedly used regular expressions. It took about 3-4 hours. The code has compiled and executed correctly so far.

+2
source

Source: https://habr.com/ru/post/1411403/


All Articles