Are There Any Valid Use Cases to Use New and Delete, Raw Pointers or C-Style Arrays With Modern C++

Modern c++ while dealing with legacy code owning raw pointers: unique_ptr VS softer GSL ownerT*

A) all owning raw pointers (clearly detectable by the delete in the destructor but also always cross-checked with the user guide) replaced with unique_ptr.

CONTRA I have to add .get() at every call of the framework methods (plus sometimes some .reset() when initlization cannot happen in-class/in-initializer-list), which at least at the first glance looks weird, but maybe one has just to get used to.

I would definitely recommend this approach. If you are bothered by the get and reset calls, you can define your own wrapper that has implicit conversions. Less safe, still better than the old state. Something like this:

template<class T>
struct owned
{
std::unique_ptr<T> ptr;

/*implicit*/ owned(T* ptr=nullptr) noexcept
: ptr(ptr)
{}
/* implicit */ operator T*() const noexcept
{ return ptr.get(); }
};

C) why should I have pointers at all, and not just embed the object inside??? I should get the exact PRO and CONTRA as option (A) - just & to add instead of .get() -, right???
Of course the approach departs more substantially from what the framework suggests

There is a good chance that you can't do this if your framework uses opaque pointers as compilation firewalls.

Another risk is that if you hand out pointers to these objects and then move the objects around, you create dangling pointers. Note that the objects created by the framework might themselves be self-referential or their own constructor/factory might have borrowed references to the object.

A well-written framework should either deal with those cases in copy constructors or deny use of those (making them private if this is pre C++11). That is something you have to check for yourself.

To delete or to free

I do not have thorough enough knowledge of C++ to say for myself, but based on what user2079303 has written in their answer, it seems that the theoretical answer to this question is that it is impossible. However, in practice there are many ways to approach this issue.


1. Check the documentation

In most cases, if you're developing software that uses a library, it should have documentation that provides this information. If not, you may want to question whether or not the benefits of using this library really outweigh the costs.

2. Check the source code

If you have access to the source code, you should be able to find out how the memory in question is being allocated. This is most likely more difficult than option 1, but it gives you a definite answer, rather than an answer that someone else wrote (which really should be correct, but doesn't have to be).

3. Use valgrind

Valgrind is a debugging utility tool, not a tool for proving correctness (correct me if I'm wrong), but in the absence of direct access to the resources we need to answer this question, it can be very useful. We can experiment with the following program, where we imagine that in our application we may not have access to the body of MysteryFunctionOne and MysteryFunctionTwo:

#include <iostream>

int* MysteryFunctionOne() {
return new int();
}

int* MysteryFunctionTwo() {
return (int *) malloc(sizeof(int));
}

int main() {
int* p1 = MysteryFunctionOne();
int* p2 = MysteryFunctionTwo();

std::cout << std::hex << p1 << std::endl << p2 << std::endl;

// For illustrative purposes, suppose we free both pointers incorrectly
free(p1);
delete p2;

return 0;
}


If we compile and run the above program with valgrind program_name, we receive two important error messages, the first of which is:

Mismatched free() / delete / delete []
at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x400A2E: main (in <path/to/program>)
Address 0x5ab6c80 is 0 bytes inside a block of size 4 alloc'd
at 0x4C2E0EF: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x4009A3: MysteryFunctionOne() (in <path/to/program>)
by 0x4009C8: main (in <path/to/program>)


This tells us that we have incorrectly used free in function main. The lower half of the error message even lets us know that operator new was used in MysteryFunctionOne(). Thus we know we need to delete the pointer returned by MysteryFunctionOne(), so we change

free(p1);

to

delete p1;

The second error message we receive is analogous, and from it we learn that we need to free(p2).

In this case, even without reading the source code or documentation, valgrind was able to guide us towards correct memory management. In general, this may not always be the case, but it is an option to consider in the absence of the source code or salient documentation.

How to correctly deallocate class member?

You're looking for a destructor:

class Field
{
private:
size_t* square = new size_t[5];
public:
~Field() { delete [] square; }
};

But learn the Rule of Zero and defer instead to a resource managing class:

class Field
{
private:
std::unique_ptr<size_t[]> square;
public:
Field() : square(new size_t[5]) { }
};

Which has the benefits of having move semantics already do the right thing, copy semantics already being disabled, and the destructor already managing your resources.

Is this a good programming practice to cleanup memory?

In Qt memory management is organized by parent-child relation.
Basically when you create a new QObject of any kind, and give it a parent argument, parent will be responsible for deleting that object.

So basically, this static vector is obsolete and this is not an opinion.
When parent-child relation is used if you delete root element all children will be destroyed.

This static vector is kind of global variable and this should be avoided in any framework or programming language. So this is another reason not to use it.



Related Topics



Leave a reply



Submit