How to deal with pointers without smart pointers?

I am reading C ++ Primer Plus by Stephen Frata . I read chapter 6, which means that I learned about pointers, but not about objects and classes (although I know about OOP).

I came from the background of ActionScript (Flash) and Java, so I have never dealt with pointers before, but I understand them. I have a bunch of questions about them, though.

As I understand it, you need to connect the new ones and delete, i.e. the object / function that creates the pointer is responsible for freeing it. But imagine a simple factory function, for example:

SomeObject * createSomeObject(){ return new SomeObject; } 

This looks rather problematic. Who is responsible for releasing this pointer now?

What if I create a class that provides public access to the pointer that it created. Following the new / delete rule, this class should be responsible for freeing the pointer in its destructor. But since a pointer can be used by another class, destroying the first class will break the second ...

These two polls are similar. What can I do to manage a pointer that is known to other objects than the one who created it?

Note. I know that a smart pointer can solve this problem, but I wonder how people get along without them.

+4
source share
10 answers

"Who is responsible for its removal?" This is a very good question. Typically, with such a function, you just have to document that the returned pointer should be deleted. The factory user can determine which class or function is responding. However, this is a bit vague, and it really is a problem.

In modern C ++ style, that’s why smart pointers are used. Consider:

 std::unique_ptr<SomeObject> createSomeObject() { return new SomeObject; } 

In this case, the pointer belongs to the returned unique_ptr . Wherever you move it, the stored pointer is deleted in its destructor (when it is deleted or when the object containing it destroys). This makes it clear which part of the code is responsible for its destruction, and the deletion is automatic (therefore, you cannot forget to delete it or make some kind of "destructive" call), therefore it is considered a solution to the above problem.

+3
source

Memory management issues and the “ownership” and lifetime of objects strongly affect C ++ design. Smart pointers and similar methods are generally preferred.

However, if you do not want to use smart pointers, etc. then you just need to be very strict. As a rule, memory management of a certain object should occur through one interface. Therefore, any function that creates an object based on the heap (for example, your createSomeObject() ) must have a corresponding function that deletes the object (for example, deleteSomeObject(SomeObject *) ). Of course, there are always exceptions to this kind of recommendation.

This and good documentation minimizes the likelihood of someone messing up and causing a memory leak.

+2
source

There are different approaches to this:

One of them "does not stand out on the heap":

 SomeObject createSomeObject(){ return SomeObject(); } 

If you do this, no one should free the object, and you have no sign of concern. A potential drawback is that SomeObject needs to be copied, but often this is a good solution and it should be standard. Do not use new / delete in user code; hide them inside constructor / destructor calls. (For example, perhaps SomeObject allocates some data on the heap inside and frees it when the object itself is destroyed).

The second approach is related, but uses a smart pointer:

 std::shared_ptr<SomeObject> createSomeObject(){ return std::make_shared(new SomeObject()); } 

it is like you are not returning a pointer, you are returning an object that is responsible for deleting everything that needs to be deleted. A smart pointer has claimed responsibility for your SomeObject instance and will delete it when necessary.

Depending on the circumstances, std::auto_ptr or std::unique_ptr .

In both cases, you rely on RAII , a very powerful idiom that every C ++ programmer should know. Resources should always be wrapped in local objects (not containing heaps) that are copied and moved as needed, and are responsible for cleaning up their internal resources.

+2
source

This looks rather problematic. Who is responsible for releasing this pointer now?

I offer paired functions.

 Xyz* CreateXyz(); void DestroyXyz(Xyz *xyz); Abc* NewAbc(); void DeleteAbc(Abc *abc); 

Or you can simply pass the answeribilty Xyz/Abc removal to clients, that is, those who call this function should also delete on the returned object after use.

No matter what you choose, make clear in your documentation how the created object should be destroyed.

I would prefer pair functions, especially if there are a lot of things before deleting!

EDIT: I suggest this approach to function pairs when creating a DLL or some dynamic library. In fact, this ensures that the object will be destroyed from the same memory pool from which it was created!

+1
source

There is no one who is responsible for creating/destroying.

You define the logic.

If you have allocated a pointer to 512 bytes to store a long 128-char string, successfully changed the string and saved it to a file, you can destroy the pointer at any time after that.

0
source

In C ++, it is not always true that "the object / function that creates the pointer is responsible for freeing," as you say. In the factory class, usually the code that uses the object removes it if you can make sure there are no additional pointer users. Otherwise, a smart pointer or some reference count is required.

0
source

Normally, the factory function is not responsible for deleting created objects. If you lay out a guide for a new / delete all in one class, it would be almost impossible to return pointers at all - especially in multi-threaded environments.

In most cases, the documentation / link explicitly indicates who is responsible for deleting the object, if this is not obvious. Possible solutions without manually handling this would fall into the garbage collection area, I think (reference counters are a popular approach).

0
source

There is no simple answer to what will work in any situation. There are always exceptions to the rules. You just need to consider the different architectures for your software and decide which one seems the easiest to understand for anyone reading the code.

0
source

In principle, it is impossible to write a safe exception code in C ++ if there are bare new ones and deletes.

So you have basically two options:

  • smart pointers for transferring allocated resources.
  • avoid using naked new ones and delete by creating your own classes around the idioms of a class-reference counter. Actually, this is really just a different name for smart pointers, made the hard way.

The main idea is that you create in your structure a set of classes that are cheap for copying that complete another class that contains a reference counter and a resource that will be expensive to copy.

By providing assignment and copy operators on a cheap copy class, you get a data structure that can be passed with the same semantics as the original type, for example, int.

This is a little more than reinventing smart pointers, but I like it because the syntax is less cluttered than using smart pointers not related to typedef in the code, and somewhat satisfactory enough to know about C ++ constructors and overloading the statement to write wrappers.

0
source

Jalf already mentioned an important bit, i.e. that you don’t necessarily allocate a bunch.

Something else is playing this, and the book probably won't tell you the following:

C ++ is not particularly good for OOP. Although it was originally designed as C with classes (which, at least, implies its goals as OOP), OOP is actually very cumbersome in C ++, and not the focus of most modern C ++ idioms.

If you are associated with an OPP base, you have two options:

  • Smart pointers or
  • The garbage collector (I mean this in the broadest sense), a special allocator (for example, a pool) that magically manages its resources can also be counted.

Usually you try to do without explicit pointers and freestore controls in C ++. That is, you push your business objects onto the stack and encapsulate all dynamic memory management in them through RAII.

Of course, this makes subtype polymorphism much more complex. But to mitigate this, C ++ offers a very powerful template engine that allows you to write extremely abstract, high-level code (possibly more than OOP). This is sometimes called oriented algorithms to compensate for it from the orientation of the object. A good example of this is the C ++ <algorithms> header and standard libraries.

0
source

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


All Articles