41

Here's a notable video (Stop teaching C) about that paradigm change to take in teaching the c++ language.

And an also notable blog post

I have a dream ...

I'm dreaming of so called C++ courses/classes/curriculae will stop teaching (requiring) their students to use: ...

Since C++11 as established standard we have the Dynamic memory management facilities aka smart pointers.
Even from earlier standards we have the c++ standard Containers library as a good replacement for raw arrays (allocated with new T[]) (notably usage of std::string instead of c-style NUL terminated character arrays).

Question(s) in bold:

Let aside the placement new override, is there any valid use case that can't be achieved using smart pointers or standard containers but only using new and delete directly (besides implementation of such container/smart pointer classes of course)?

It's sometimes rumored (like here or here) that using new and delete handrolled can be "more efficient" for certain cases. Which are these actually? Don't these edge cases need to keep track of the allocations the same way as standard containers or smart pointers need to do?

Almost the same for raw c-style fixed size arrays: There is std::array nowadays, which allows all kinds of assignment, copying, referencing, etc. easily and syntactically consistent as expected by everyone. Are there any use cases to choose a T myArray[N]; c-style array in preference of std::array<T,N> myArray;?


Regarding interaction with 3rd party libraries:

Assumed a 3rd party library returns raw pointers allocated with new like

MyType* LibApi::CreateNewType() {
    return new MyType(someParams);
}

you can always wrap that to a smart pointer to ensure that delete is called:

std::unique_ptr<MyType> foo = LibApi::CreateNewType();

even if the API requires you to call their legacy function to free the resource like

void LibApi::FreeMyType(MyType* foo);

you still can provide a deleter function:

std::unique_ptr<MyType, LibApi::FreeMyType> foo = LibApi::CreateNewType();

I'm especially interested in valid "every day" use cases in contrast to academic/educational purpose requirements and restrictions, which aren't covered by the mentioned standard facilities.
That new and delete may be used in memory management / garbage collector frameworks or standard container implementation is out of question1.


One major motivation ...

... to ask this question is to give an alternative approach vs any (homework) questions, which are restricted to use any of the constructs mentioned in the title, but serious questions about production ready code.

These are often referred to as the basics of memory management, which is IMO blatantly wrong/misunderstood as suitable for beginners lectures and tasks.


1)Add.: Regarding that paragraph, this should be a clear indicator that new and delete isn't for beginner c++ students, but should be left for the more advanced courses.

Community
  • 1
  • 1
user0042
  • 7,917
  • 3
  • 24
  • 39
  • @VTT I explicitly excluded these. It's clear for me how these are valid. – user0042 Oct 28 '17 at 15:21
  • 2
    Factories that dynamically create objects of various types based on some inputs, and return a pointer to a polymorphic base (or a smart variant of such a pointer). Polymorphic objects that provide a clone functionality. – Peter Oct 28 '17 at 15:28
  • 4
    @Peter why couldn't you use a `std::unique_ptr` or `std::shared_ptr` to handle those? – Ben Cottrell Oct 28 '17 at 15:29
  • @Ben Cottrell - those smart pointer types don't support creation of objects of different/derived types. Once the object is created then, yes, its lifetime can be managed. – Peter Oct 28 '17 at 15:39
  • 1
    @Peter It is possible to create `shared_ptr` / `unique_ptr` of a derived type and then assign it to base type smart pointer. For example - http://cpp.sh/5r7rl Am I misunderstanding your comment? – Ben Cottrell Oct 28 '17 at 15:46
  • 3
    @Peter _"those smart pointer types don't support creation of objects of different/derived types"_ They actually do. – user0042 Oct 28 '17 at 15:49
  • 3
    You still need `new` to construct objects in some cases because `std::make_unique` doesn't accept deleters. – Galik Oct 28 '17 at 16:23
  • 1
    @Galik Not exactly what I'm after, but good point. – user0042 Oct 28 '17 at 16:29
  • It is not quite clear to me if you are asking when you still litterally have to call new/delete or if you are asking when you should not manage the lifetime with a smart pointer. Especially in c++11 there are a lot of instances, where you create an object with new, but manage it via delete and some instances, where you technically could create an object via make_... but later call release on it and manage it in some different way. – MikeMB Nov 03 '17 at 06:54
  • _"Regarding that last paragraph, this should be a clear indicator that new and delete isn't for beginner c++ students, but should be left for the more advanced courses."_ that's really **false**. Even though you might not use them _directly_, you cannot be unaware of them. You can't skip them: they're just camouflaged. C++ isn't C# or Java. And if it were _that_ hard C wouldn't have gained that much success. – edmz Nov 05 '17 at 17:14
  • @edmz _Advanced_ stuff in c++ though. I'm arguing about teaching such stuff for beginners, or as being the _regular case_. – user0042 Dec 14 '17 at 23:24
  • Graphics programming when you are creating a pointer to an array of typically unsigned char to load in image data or texture data from a file. With many graphics libraries you still need a basic "raw" pointer to temporarily hold the data until you are able to `populate` one of your engine's internal data structures that suites your needs. The same can be said about various audio libraries, and compression, expansion, and encryption libraries. However I do have to agree for the most general types, yes smart pointers are the way to go. – Francis Cugler Dec 30 '17 at 08:16
  • @FrancisCugler There's no reasoning why the address of a vectors `data()` member can't be handed out to such APIs if you manage the lifetime properly. – user0042 Dec 30 '17 at 08:18
  • @user0042 that's true; however after writing the code to read a texture file parse and process its data for so long; it's more of a natural aesthetic and preference than anything; however with this type of pointers with new & delete typically I'll keep them both within the same function and I won't pass the pointer out... I usually pass in a structure that will hold the data after its been processed. But this is a use case of when to use raw pointers. – Francis Cugler Dec 30 '17 at 08:31
  • @FrancisCugler _"But this is a use case of when to use raw pointers. "_ Not really IMO. What about exception safety? I'd simply use a local `std::vector` resize it once properly, and don't care about anything else. – user0042 Dec 30 '17 at 08:33
  • @user0042, sometimes you need the heap cause the stack won't suffice. – Francis Cugler Dec 30 '17 at 09:37
  • @FrancisCugler _"sometimes you need the heap"_ I have no clue what you're talking about?? The whole purpose of `std::vector` is to manage _"heap"_ allocation properly. – user0042 Dec 30 '17 at 09:39

19 Answers19

27

When ownership should not be local.

As an example, a pointer container may not want ownership over the pointers in it to reside in the pointers themselves. If you try to write a linked list with forward unique ptrs, at destruction time you can easily blow the stack.

A vector-like container of owning pointers may be better suited to storing delete operation at the container or subcontainer level, and not at the element level.

In those and similar cases, you wrap ownership like a smart pointer does, but you do it at a higher level. Many data structures (graphs, etc) may have similar issues, where ownership properly resides at a higher point than where the pointers are, and they may not map directly to an existing container concept.

In some cases it may be easy to factor out the container-ownership from the rest of the data structure. In others it may not.

Sometimes you have insanely complex non-local non-reference counted lifetimes. There is no sane spot to put the ownership pointer in those cases.

Determining correctness here is hard, but not impossible. Programs that are correct and have such complex ownership semantics exist.


All of these are corner cases, and few programmers should run into them more than a handful of times in a career.

edmz
  • 8,220
  • 2
  • 26
  • 45
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
15

I'm going to be contrarian, and go on record as saying "no" (at least to the question I'm pretty sure you really intended to ask, for most of the cases that have been cited).

What seem like obvious use-cases for using new and delete (e.g., raw memory for a GC heap, storage for a container) really aren't. For these cases, you want "raw" storage, not an object (or array of objects, which is what new and new[] provide respectively).

Since you want raw storage, you really need/want to use operator new and operator delete to manage the raw storage itself. You then use placement new to create objects in that raw storage, and directly invoke the destructor to destroy the objects. Depending on the situation, you might want to use a level of indirection to that though--for example, the containers in the standard library use an Allocator class to handle these tasks. This is passed as a template parameter, which provides a customization point (e.g., a way to optimize allocation based on a particular container's typical usage pattern).

So, for these situations, you end up using the new keyword (in both the placement new and the invocation of operator new), but not something like T *t = new T[N];, which is what I'm pretty sure you intended to ask about.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • I don't think I understand, what you do you mean by operator new? You said "not something like ...", can you show what the valid way is to use new, other than placement new? – Nir Friedman Oct 28 '17 at 15:46
  • _@Jerry_ That's the 1st real sound answer here. THX. – user0042 Oct 28 '17 at 16:01
  • 1
    Hmm, if you use placement new on storage you expose implementation detail, dontcha? A better solution seems to be, you redefine the standard allocator to return locations inside the storage, and then user code invokes this allocation via usual `new`, nya? – bipll Nov 03 '17 at 07:39
  • 3
    @bipll: I don't see how use of placement new exposes implementation details. Do you notice implementation details with existing containers that use this technique (e.g., vector)? – Jerry Coffin Nov 03 '17 at 13:30
11

One valid use case is having to interact with legacy code. Especially if passing raw pointers to functions that take ownership of them.

Not all libraries you use may be using smart pointers and to use them you may need to provide or accept raw pointers and manage their lifetimes manually. This may even be the case within your own codebase if it has a long history.

Another use case is having to interact with C which does not have smart pointers.

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70
  • You can use a deleter function to wrap such legacy stuff into a smart pointer? Same for interaction with c-APIs. – user0042 Oct 28 '17 at 15:22
  • 2
    I don't see this as a valid use case. You can use `get` if you need to have out a raw pointer and you can use a custom deleter to accept a raw pointer and handle it correctly when it goes you if scope. – NathanOliver Oct 28 '17 at 15:26
  • @NathanOliver if whatever you pass the pointer to takes ownership of it, then passing smart pointer `.get()` would be a bug since now two entities will try to delete it - and a custom deleter won't help you either. Better to just pass `new thing`. – Jesper Juhl Oct 28 '17 at 15:31
  • 4
    This is a semi valid use case for `new` (if the legacy code accepts an owning raw pointer and will call delete on it internally), but not for `delete`; I think it's important to make this distinction. Also, even `new`, while it is verbose, one could argue that it is better to write `make_unique(..).release()`. This addresses the issue with your concern about NathanOliver's suggestion. – Nir Friedman Oct 28 '17 at 15:44
  • 6
    This is what `release()` is for, passing ownership. – Galik Oct 28 '17 at 16:26
  • When a C API function performs a `free` on a pointer you passed to it, then it either requires you to use `malloc` or, which is fortunately more common, it will provide its own allocation function anyway. In either case, it is no use case for `new`. – Christian Hackl Oct 28 '17 at 16:34
  • Even if the library is configurable enough to accept a `void(*)(size_t)` for custom allocation and a matching `void(*)(void*)` for deallocation, you still can't use RAII. – MSalters Oct 30 '17 at 15:45
5

Some APIs might expect you to create objects with new but will take over ownership of the object. The Qt library for example has a parent-child model where the parent deletes its children. If you use a smart pointer, you are going to run into double-deletion issues if you're not careful.

Example:

{
    // parentWidget has no parent.
    QWidget parentWidget(nullptr);

    // childWidget is created with parentWidget as parent.
    auto childWidget = new QWidget(&parentWidget);
}
// At this point, parentWidget is destroyed and it deletes childWidget
// automatically.

In this particular example, you can still use a smart pointer and it will be fine:

{
    QWidget parentWidget(nullptr);
    auto childWidget = std::make_unique<QWidget>(&parentWidget);
}

because objects are destroyed in reverse order of declaration. unique_ptr will delete childWidget first, which will make childWidget unregister itself from parentWidget and thus avoid double-deletion. However, most of the time you don't have that neatness. There are many situations where the parent will be destroyed first, and in those cases, the children will get deleted twice.

In the above case, we own the parent in that scope, and thus have full control of the situation. In other cases, the parent might not be hours, but we're handing ownership of our child widget to that parent, which lives somewhere else.

You might be thinking that to solve this, you just have to avoid the parent-child model and create all your widgets on the stack and without a parent:

QWidget childWidget(nullptr);

or with a smart pointer and without a parent:

auto childWidget = std::make_unique<QWidget>(nullptr);

However, this will blow up in your face too, since once you start using the widget, it might get re-parented behind your back. Once another object becomes the parent, you get double-deletion when using unique_ptr, and stack deletion when creating it on the stack.

The easiest way to work with this is to use new. Anything else is either inviting trouble, or more work, or both.

Such APIs can be found in modern, non-deprecated software (like Qt), and have been developed years ago, long before smart pointers were a thing. They cannot be changed easily since that would break people's existing code.

Nikos C.
  • 50,738
  • 9
  • 71
  • 96
  • I strongly agree with you. To be honest I'm always trully amazed by questions as the current one, because despite the high popularity of the Qt library the smart pointers are considered as the only way for automatic memory management. In my opinion the parent-child relationship of Qt is a far better concept. – scopchanov Nov 04 '17 at 20:29
  • 1
    @scopchanov Well, I wouldn't go as far as to say it's a better concept. For me, the better concept is to keep your objects on the stack. No pointers (smart or otherwise) unless you really need them on the heap. Qt's model is not terrible, but not good either. You can use stack objects in Qt too, but it requires you to think more (== work more) because you can end up with parents trying to delete objects that are on the stack and weren't created with `new`. – Nikos C. Nov 04 '17 at 20:46
  • I personally find it logical and convenient to use. Anyway. The bottom line is that I think you gave a good point. – scopchanov Nov 04 '17 at 22:15
4

The OP specificly asks about how/when handrolling will be more efficient in an everyday use case - and I will address that.

Assuming a modern day compiler/stl/platform, there is not an every day use where handrolled use of new and delete will be more efficient. For the shared_ptr case i believe it will be marginal. In an extremely tight loop(s) there could be something to gain by just using raw new to avoid the ref counting (and find some other method of cleaning up - unless somehow imposed on you, you choose to use shared_ptr for a reason), but that is not an everyday or common example. For the unique_ptr there is not actually any difference, so i think it is safe to say that it is more of rumour and folklore and that performance wise it will not actually matter at all (difference will not be measurable in normal cases).

There are cases where it is not desirable or possible to use a smart pointer class as already covered by others.

darune
  • 10,480
  • 2
  • 24
  • 62
3

For simple use cases, smart pointers, standard containers and references should be enough to use no pointers and raw allocation and de-allocation.

Now for the cases I can think about:

  • development of containers or other low-level concepts - after all the standard library itself is written in C++ and it does make use of raw pointers, new and delete
  • low level optimization. It should never be a first class concern, because compilers are smart enough to optimize standard code, and maintainability is normally more important than raw performance. But when profiling shows that a block of code represents more than 80% of the execution time, low level optimization makes sense, and thats one of the reasons why the low level C standard library is still a part of C++ standards
Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
3

You sometimes have to call new when using private constructors.

Say you decide to have a private constructor for a type that is intended to be called by a friend factory or an explicit create method. You can call new inside this factory but make_unique won't work.

Guillaume Gris
  • 2,135
  • 17
  • 34
2

Another possible valid use case is when you code some garbage collector.

Imagine that you are coding some Scheme interpreter in C++11 (or some Ocaml bytecode interpreter). That language requires you to code a GC (so you need to code one in C++). So ownership is not local, as answered by Yakk. And you want to garbage collect Scheme values, not raw memory!

You probably will end up using explicit new and delete.

In other words, C++11 smart pointers favor some reference counting scheme. But that is a poor GC technique (it is not friendly with circular references, which are common in Scheme).

For example, a naive way of implementing a simple mark-and-sweep GC would be to collect in some global container all the pointers of Scheme values, etc...

Read also the GC handbook.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • 2
    "*C++11 smart pointers favor some reference counting scheme*" - that is only true for the `std::shared_ptr` smart pointer. Not the `std::unique_ptr` smart pointer. Even then, `std::weak_ptr` exists to address the circular reference issue with `std::shared_ptr` – Remy Lebeau Oct 28 '17 at 16:14
  • But that won't work in that Scheme case. You can't predict or define what pointers are weak. – Basile Starynkevitch Oct 28 '17 at 19:17
  • 2
    Even if the target language isn't using an explicit GC, you may want to use a memory management scheme that more accurately follows the specfied memory behavior of that language. Quite relevant as C++ is often a good implementation language. – MSalters Oct 30 '17 at 15:48
2

When you have to pass something across the DLL boundary. You (almost) can't do that with smart pointers.

Valentin
  • 1,108
  • 8
  • 18
2

3 common examples where you have to use new instead of make_...:

  • If your object doesn't have a public constructor
  • If you want to use a custom deleter
  • If you are using c++11 and want to create an object that is managed by a unique_ptr (athough I'd recommend writing your own make_unique in that case).

In all those cases however, you'd directly wrap the returned pointer into a smart pointer.

2-3 (probably not so common) examples, where you wouldn't want/can't to use smart pointers:

  • If you have to pass your types through a c-api (you are the one implementing create_my_object or implement a callback that has to take a void*)
  • Cases of conditional ownership: Think of a string, that doesn't allocate memory when it is created from a string litteral but just points to that data. Nowerdays you probably could use a std::variant<T*, unique_ptr<T>> instead, but only if you are ok with the the information about the ownership being stored in the variant and is you accept the overhead of checking which member is active for each access. Of course this is only relevant if you can't/don't want to afford the overhead of having two pointers (one owning and one non-owning)
    • If you want to base your ownership on anything more complex than a pointer. E.g. you want to use a gsl::owner so you can easily query it's size and have all the other goodies (iteration, rangecheck...). Admittedly, you'd most likely wrap that in your own class,so this might fall into the category of implementing a container.
MikeMB
  • 20,029
  • 9
  • 57
  • 102
2

Adding to other answers, there are some cases where new/delete make sense -

  1. Integrating with a 3rd party library which returns the raw pointer and expect you return the pointer to the library once you are done (The library has its own memory management functionality).
  2. Working on resource constrained embedded device where memory (RAM/ROM) is a luxury (even a few kilobytes). Are you sure you want to add more runtime (RAM) and compiled (ROM/Overlay) memory requirement to your application or you want to program carefully with new/delete?
  3. From purist point of view, in some cases smart pointers won't work intuitively (due to their nature). For example, for builder pattern you should to use reinterpret_pointer_cast, if you are using smart pointers. Another case is where you need to cast from a base type to a derived type. You put yourself danger if you get the raw pointer from smart pointer, cast it and put it in another smart pointer and ended up freeing the pointer multiple times.
fadedreamz
  • 1,156
  • 1
  • 10
  • 19
2

One of the problem I deal with is mining big data structures for hardware design and language analysis with few hundred million elements. Memory usage and performance is a consideration.

Containers are a good convenient way to quickly assemble data and work with it, but the implementation uses extra memory and extra dereferences which affect both, the memory and performance. My recent experiment with replacing smart pointers with a different custom implementation provided about 20% performance gain in a verilog preprocessor. Few years ago I did compare custom lists and custom trees vs vectors/maps and also saw gains. The custom implementations rely on regular new/delete.

So, new/delete are useful in high-efficiency applications for custom designed data structs.

Serge
  • 11,616
  • 3
  • 18
  • 28
1

You can still use new and delete if we want to create our own lightweight memory allocation mechanism. For example

1.Using In-Place new : Generally used for allocating from preallocated memory;

char arr[4];

int * intVar = new (&arr) int; // assuming int of size 4 bytes

2.Using Class Specific Allocators : If we want a custom allocator for our own classes.

class AwithCustom {

public:
    void * operator new(size_t size) {
         return malloc(size);
    }

    void operator delete(void * ptr) {
          free(ptr);
    }
};
DeepakKg
  • 349
  • 2
  • 4
1

The primary use case where I still use raw pointers is when implementing a hierarchy that uses covariant return types.

For example:

#include <iostream>
#include <memory>

class Base
{
public:
    virtual ~Base() {}

    virtual Base* clone() const = 0;
};

class Foo : public Base
{
public:
    ~Foo() override {}

    // Case A in main wouldn't work if this returned `Base*`
    Foo* clone() const override { return new Foo(); }
};

class Bar : public Base
{
public:
    ~Bar() override {}

    // Case A in main wouldn't work if this returned `Base*`
    Bar* clone() const override { return new Bar(); }
};

int main()
{
    Foo defaultFoo;
    Bar defaultBar;

    // Case A: Can maintain the same type when cloning
    std::unique_ptr<Foo> fooCopy(defaultFoo.clone());
    std::unique_ptr<Bar> barCopy(defaultBar.clone());

    // Case B: Of course cloning to a base type still works
    std::unique_ptr<Base> base1(fooCopy->clone());
    std::unique_ptr<Base> base2(barCopy->clone());

    return 0;
}
James Adkison
  • 9,412
  • 2
  • 29
  • 43
1

There is still a chance to use malloc/free in C++, as you can use new/delete, and anything higher level wrapping the STL memory templates provided.

I think in order to really learn C++ and especially understand the C++11 memory templates you should create simple structures with new and delete. Just to better understand how they work. All the smart pointer classes rely on those mechanisms. So if you understand what new and delete does, you are going to appreciate the template more and really find smart ways to use them.

Today I personally try to avoid them as much as possible, but one main reason is the performance, which you should care if it is critical.

These are my rules of thumb I always have in mind:

std::shared_ptr: Automatic management of pointers but due to the reference counting it uses for tracing the accessed pointers, you have a worse performance every time you access these objects. Compared simple pointers I would say 6 times slower. Keep in mind, you can use get() and extract the primitive pointer, and continue accessing it. Of you must be careful with that one. I like to as that as a reference with *get(), so the worse performance is not really a deal.

std::unique_ptr The pointer access may only happen at one point in the code. Because this template forbids copy, thanks to the r-references && feature, it is much faster than an std::shared_ptr. Because there is still some ownership overhead in this class I would say, they are about twice as slow as a primitive pointer. You access the object than the primitive pointer within that template. I also like to use reference trick here, for less required accesses to the object.

About performance, it might be true, that those templates are slower, but keep in mind that if you want to optimize software, you should profile first and see what really takes many instructions. It is very unlikely that smart-pointers are the problem, but sure it depends on your implementation.

In C++ no one should care about malloc and free, but they exist for legacy code. They differ basically in the fact, that they know nothing about c++ classes, which with new and delete operator case is different.

I use std::unique_ptr and std::shared_ptr in my project Commander Genius everywhere and I'm really happy that they exist. I have not to deal with memory leaks and segfaults since then. Before that, we had our own smart-pointer template. So for productive software, I cannot recommend them enough.

Farzad Karimi
  • 770
  • 1
  • 12
  • 31
Gerhard Stein
  • 1,543
  • 13
  • 25
  • 1
    Speking about `std::unique_ptr` you say "Because there is still some ownership overhead in this class ..." - that's wrong. The overhead of `unique_ptr` is designed to (and *does*) compile away. Sure, in a unoptimized debug build there may be some overhead, but in a optimized build a `std::unique_ptr` performs *exactly* the same as a raw pointer. – Jesper Juhl Nov 05 '17 at 19:15
  • Well I'm not sure when you access the object with get() and pass the raw pointer. Unoptimzed you surely have some overhead compared to raw pointers. – Gerhard Stein Nov 05 '17 at 19:20
0

Another use case may be 3rd party library returning raw pointer which is internally covered by own intrusive reference counting (or own memory management - which is not covered by any API/user interface).

Good example is OpenSceneGraph and their implementation of osg::ref_ptr container and osg::Referenced base class.

Although it may be possible to use shared_ptr, the intrusive reference counting is way better for scene graph like use cases.

Personally I do see anything "smart" on the unique_ptr. It is just scope locked new & delete. Although shared_ptr looks way better, it requires overhead which is in many practical cases unacceptable.

So in general my use case is:

When dealing with non-STL raw pointer wrappers.

Venca B Spam
  • 189
  • 4
  • But how's that related to `new` and `delete`? – bipll Nov 03 '17 at 07:42
  • In intrusive referenced pointers (e.g. Boost or OSG) you use new to create standard pointers. The only rule you have to obey is to put the pointer at least to one "ref_ptr container". As long as at least one ref_ptr container keeps it, you can do whatever you want with such pointer (of course except deleting it). When you communicate with such 3rd party library, you just create pointer and send it to the library API. As long as you keep the contract (with the API) you are ok. So ok, with ref_ptr you never use 'delete', but 'new' almost always (except factory based pointers). – Venca B Spam Nov 04 '17 at 10:32
0

another example that has not already been mentioned is when you need to pass an object through a legacy (possibly asynchronous) C-callback. Usually, these things take a function pointer and a void* (or an opaque handle) to pass some payload upon. As long as the callback gives some guarantee on when/how/how many times it will be invoked, resorting to a plain new->cast->callback->cast->delete is the most straightforward solution (ok, the delete will be probably managed by a unique_ptr on callback site, but the bare new is still there). Of course, alternative solutions exist, but always requires the implementation of some sort of explicit/implicit 'object lifetime manager' in that case.

Massimiliano Janes
  • 5,524
  • 1
  • 10
  • 22
  • This case can be encapsulated in a single object that stores the callback and the context pointer, the latter can be smart. Moreover, the context pointer is often simply `this` in such bindings, not subject to any management at all, neither automatic nor manual. – bipll Nov 03 '17 at 07:46
  • @bipll I don't think so, you cannot incapsulate it into an object unless you manage the lifetime of the object itself relative to the api call; the same can be said about passing *this* ... if you don't *need* to manage the object lifetime then you're talking about a totally different use case. – Massimiliano Janes Nov 03 '17 at 08:07
  • Why not exactly? If you only need an object to substitute a context for callback, this object's existence only makes sense as long as you need this callback which is why the callback pair can own the object. – bipll Nov 03 '17 at 09:51
  • @bipll sorry, I'm not sure I follow you now; let's take a step back, consider an api call `foo(callback,(void*))` where you have *no* control on when the callback is called, the only thing you know is that it will be called exactly once. How do you create and pass the object ? – Massimiliano Janes Nov 03 '17 at 10:08
  • Very easy: the pointer-to-object is owned by an instance of Callback class. When Callback instance is dtored, it a) deregisters the callback; b) deletes the object (can be automatic, with unique_ptr). – bipll Nov 03 '17 at 12:16
  • @bipll it’s not that easy at all, because you have to make sure that the instance owning the pointer outlives the callback call. This may or may not be possible, and it adds compexity to simply new’ing at foo call. – Massimiliano Janes Nov 03 '17 at 12:47
  • Please read my previous comment carefully. The callback object deregisters the callback when destroyed. Of course it can be done only when the framework allows to do it. – bipll Nov 03 '17 at 13:09
-1

I think this is typically a good use case and/or guideline to follow:

  • When the pointer is local to single function's scope.
  • The dynamic memory is handled in the function and you need the heap.
  • You are not passing the pointer around and it isn't leaving the functions scope.

PSEUDO Code:

#include <SomeImageLibrary>

// Texture is a class or struct defined somewhere else.
unsigned funcToOpenAndLoadImageData( const std::string& filenameAndPath, Texture& texture, some optional flags (how to process or handle within function ) {
    // Depending on the above library: file* or iostream...

    // 1. OpenFile

    // 2. Read In Header

    // 3. Process Header

    // 4. setup some local variables.

    // 5. extract basic local variables from the header
    //    A. texture width, height, bits per pixel, orientation flags, compression flags etc.

    // 6. Do some calculations based on the above to find out how much data there is for the actual ImageData...

    // 7. Raw pointer (typically of unsigned char).

    // 8. Create dynamic memory for that pointer or array.

    // 9. Read in the information from the file of that amount into the pointer - array.

    // 10. Verify you have all the information.

    // 11. Close the file handle.

    // 12. Process some more information on the actual pointer or array itself
    // based on its orientation, its bits per pixel, its dimensions, the color type, the compression type, and or if it exists encryption type.

    // 13. Store the modified data from the array into Your Structure (Texture - Class/Struct).

    // 14. Free up dynamic memory...

    // 15. typically return the texture through the parameter list as a reference

    // 16. typically return an unsigned int as the Texture's numerical ID.    
}

This is quite effective; efficient, doesn't need any use of smart pointers; is fast especially if inlining the function. This type of function can either be a stand alone or even a member of a class. If a pattern follows this then it is quite safe to use new & delete or new[] & delete[] if done properly.

EDIT

In the mentioned case(s) above sometimes you want the raw pointers and you want it on the heap. Let's say you have an application that will load say 5,000 texture files, 500 model files, 20 scene files, 500-1000 audio files. You do not want your loading time to be slow, you also want it to be "cache" friendly. Texture loading is very good example of having the pointer on the heap as opposed to the functions stack because the texture could be large in size exceeding your local memory capabilities.

In this context you will be calling these load functions once per object, but you will be calling them several times. After you loaded & created your resources or assets and stored them internally is when and where you would want to use containers instead of arrays and smart pointers instead of raw pointers.

You will load a single asset once, but you may have 100s or 1000s of instances of it. It is with these instances that you would prefer the use of containers and the use of smart pointers to manage their memory within your application over raw pointers and arrays. The initial loading is where you would prefer to be closer to the metal without all the extra unwanted overhead.

If you were working on a A+ class game and you could save your audience 15 to 30s or more of loading time per loading screen then you are in the winners circle. Yes care does need to be taken and yes you can still have unhandled exceptions, but no code is 100% full proof.

This type of design is rarely prone to memory leaks except for those exceptions which can still be handled in many of the cases. Also to safely manage raw pointers, preprocessor macros work well for easy clean up.

Many of these library types also work and deal with raw data, raw memory allocation, etc. and many times smart pointers don't necessarily fit these types of jobs.

Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
  • As already mentioned in my previous comments. Such code would be prone to leak with exceptions happening during read of the file data and processing. That's definitiely not a good and valid use case. – user0042 Dec 30 '17 at 08:51
  • Not if you are checking the code at each step and as soon as you catch an error & before you throw it; you try to release it. There is no 100% full proof code... – Francis Cugler Dec 30 '17 at 08:56
  • So I don't see the advantage. Doing all that would make your code unnecessarily complicated, and doing a lot of things that have nothing to do with the original functionality needed. – user0042 Dec 30 '17 at 08:59
  • @user0042 This is typically how many `graphics`, `model`, and `audio type` libraries handle their `APIs` & `Libraries` especially when you have to write the function to parse the contents, manipulate the data to the needs of your applications uses and its internal structures and mechanics. You are creating the dynamic memory within the function; using it and at any give time if there is an error or exception being thrown before you throw that exception you try to release or delete the pointer, then once all is successful, your release the data and return back to the caller. – Francis Cugler Dec 30 '17 at 09:04
  • @user042 I have written plenty of these functions for loading JPEG, BMP, TGA, TARGA, PNG files, some model & scene files, OGG / Vorbis, etc. Same thing when handling some custom fonts. Yes there are times to use smart pointers, but they are also not for everything... – Francis Cugler Dec 30 '17 at 09:06
-2

When you want to create multidimensional arrays but aren't familiar with C++11 syntax like std::move, or aren't familiar with writing custom deleters for smart pointers.

zhiguang
  • 345
  • 1
  • 7