+ 1

Migrate from raw to smart pointer

Hi I have a code where observer pattern is implemented was in raw pointer format. Code is updated to support smart pointer. Refer code below: https://www.sololearn.com/en/compiler-playground/cLGLnqGQDHVB Issue came in to picture at later moment (It is bad to know at last stage , but this is what it is in current scenario). IObserver* GetUserInfoFromThirdPartyLibrary() is the one which is not in our control. Those third persons are having different priority to update the codebase and still they will return raw pointer instead of smart pointer. To use them into observer, Is below line proper? shared_ptr<IObserver> user5(GetUserInfoFromThirdPartyLibrary()); If not, please suggest why and what should be changed here? Another thing is releasing of heap memory allocated by GetUserInfoFromThirdPartyLibrary. Earlier, I used to call this as below: IObserver* ptr = GetUserInfoFromThirdPartyLibrary(); delete ptr;//at later stage Now, I am not doing any memory release after user5 is constructed. Is this sufficient or will result into memory leak?

4th Dec 2023, 3:18 PM
Ketan Lalcheta
Ketan Lalcheta - avatar
6 ответов
+ 2
what i mean is if the <GetUserInfoFromThirdPartyLibrary> is allocating memory using <malloc>, you should use a custom deleter with <std::shared_ptr> to ensure that <free> is called instead of <delete>. the below code has Undefined Behavior: struct myclass { public: int a; float b; public: ~myclass() { std::cout << "myclass::~myclass\n"; } }; myclass* getMyClassFromThirdPartyLibrary() { if ( auto ptr = reinterpret_cast<myclass*>( std::malloc(sizeof(myclass) ) ) ) { return ::new (ptr) myclass( 15, 63.2f ); } return nullptr; } int main() { std::shared_ptr<myclass> tt( getMyClassFromThirdPartyLibrary() ); return 0; } the raw pointer returned by <getMyClassFromThirdPartyLibrary> is allocated using <malloc> instead of <new>, using <std::shared_ptr> directly would result in undefined behavior. the reason is that <std::shared_ptr> assumes it's dealing with memory allocated by <new> and relies on its custom deleter to correctly call <delete>. so we must provide a custom deleter with <std::shared_ptr> to ensure that <free> is called instead of <delete>. struct myclass { public: int a; float b; public: ~myclass() { std::cout << "myclass::~myclass\n"; } }; myclass* getMyClassFromThirdPartyLibrary() { if ( auto ptr = reinterpret_cast<myclass*>( std::malloc(sizeof(myclass) ) ) ) { return ::new (ptr) myclass( 15, 63.2f ); } return nullptr; } struct custom_deleter { public: void operator()(myclass* ptr) { std::destroy_at(ptr); std::free(ptr); } }; int main() { std::shared_ptr<myclass> tt( getMyClassFromThirdPartyLibrary() ); return 0; }
6th Dec 2023, 10:46 AM
MO ELomari
MO ELomari - avatar
+ 1
this situation is generally safe, just make sure that <GetUserInfoFromThirdPartyLibrary> return a new <User> object each time it's called ( so each <std::shared_ptr> will manage its own instance of <User> ). however if <GetUserInfoFromThirdPartyLibrary> always returns the same raw pointer ( and thus the same <User> object), then you would end up sharing ownership of the same object : IObserver* GetUserInfoFromThirdPartyLibrary() { static auto user = new User("Third_Party"); return user; } std::shared_ptr<IObserver> user6(GetUserInfoFromThirdPartyLibrary()); std::shared_ptr<IObserver> user7(GetUserInfoFromThirdPartyLibrary()); here "user5" and "user6" would end up sharing ownership of the same object, and when both <std::shared_ptr> instances are destructed, leading to double deletion of the underlying <User> object. Note: when using smart pointer make sure you have and idea what type of allocation function was used to create the resource handle that you passed-in ultimately. ( malloc-ing and then calling delete with smart pointer is definitely Undefined Behavior )
5th Dec 2023, 11:46 PM
MO ELomari
MO ELomari - avatar
0
Thanks MO ELomari It is helpful. In case of static pointer , will not convert library to use the smart pointer and that static will get deallocated at the end of program span automatically. If library is using malloc instead of new, destructor of shared pointer will have free. Is these correct solution ? Thanks again for this wonderful usecases.
6th Dec 2023, 8:31 AM
Ketan Lalcheta
Ketan Lalcheta - avatar
0
the dtor of shared pointer destroy the object using delete-expression ( delete for new / delete[] for new[] ).
6th Dec 2023, 9:21 AM
MO ELomari
MO ELomari - avatar
0
That's true, but it is default behavior. AFAIK, we can provide custom deleter if we wish as below: struct myclass { myclass() {cout << "ctor\n";} ~myclass() {cout << "dtor\n";} void mydelete() {cout << "custom class\n";} }; int main() { shared_ptr<myclass> tt(new myclass,[](myclass* p1){cout << "can call free here\n";}); return 0; } Here, last argument here makes sure that destructor is not called and we have lambda getting called here. Is this not something which will help here?
6th Dec 2023, 9:39 AM
Ketan Lalcheta
Ketan Lalcheta - avatar
0
Thanks MO ELomari
6th Dec 2023, 4:56 PM
Ketan Lalcheta
Ketan Lalcheta - avatar