3

I've been out of C++ for years, last time I used it was back in gamedesign before C++11. I see all these new pointer types which seem great. But I'm unsure when and how to use them. In the old days I would create a buffer like this:

uint bufferSize = 1024;
unsigned char *buffer = (unsigned char*)malloc(bufferSize, sizeof(char));

It's not pretty and I'm pretty sure even back then new/delete was already a thing in C++, but I never learned about them in that time. I don't think raw pointers should be used if avoidable, so I'm trying to get used to writing this now:

uint bufferSize = 1024;
std::unique_ptr<unsigned char[]> buffer;
buffer = std::make_unique<unsigned char[]>(bufferSize);

But this raises the question, should I just map the old raw pointers to unique_ptr like this? There's also std::vector, which seems like a much more natural fit for any array. Should I use a vector instead? Are there rules of thumb for when to use one or the other (for arrays)?

To add some more specific information, I'm making a game and I'm setting up a screenbuffer to draw to, I'm starting with ASCII and will likely move on to pixels somewhere in the future. The buffer size will never have to change at runtime, but it would be nice to be able to play with the screen size a bit during development so that's why I'm not simply declaring a fixed-length array (my bufferSize is actually two consts multiplied; screenWidth and screenHeight). This is just a prototype, it doesn't have to be scalable or deployable, but I still want to use this chance to learn to write better C++.

  • Rule of thumb: don’t write C, if you want to write C++. Very special corner cases aside, usually std::vector or any other modern STL container is the way to go. – Simon Oct 17 '18 at 21:31
  • 1
    @Simon Is std::unique_ptr not C++? – Kevin Oct 17 '18 at 21:56
  • unique_pte is, but C array is not. – Simon Oct 18 '18 at 4:26
  • 1
    @Simon A native array most certainly is C++. Using a std::array is necessary if you write generic code (native array of length zero is non-standard), or have to treat the whole array as a first-class-object. Otherwise, don't bother. – Deduplicator Oct 18 '18 at 22:46
4

sizeof(TYPE) has been a bad idea in C and C++ since forever, assuming you could use sizeof *pointer. The problem is that it duplicates the type-Information without adding a check.

In C, casting a void* is also a bad idea, as overriding the language should only be done where and when necessary after careful consideration.

And finally, malloc() only accepts one argument, you want to multiply there instead. Your compiler should warn you though.

Next, this:

uint bufferSize = 1024;
unsigned char *buffer = (unsigned char*)malloc(bufferSize * sizeof(char));

Is not quite equivalent to the following, barring the allocator used and the automatic management of the buffer:

uint bufferSize = 1024;
auto buffer = std::make_unique<unsigned char[]>(bufferSize);

The additional difference is the value-initialization, the latter does it, the former refrains from it.

Is that significant?
We cannot say, it depends on the specific circumstances. Maybe you actually want that initialization, or the compiler can optimize it out.

Or maybe the following matches your needs better:

uint bufferSize = 1024;
std::unique_ptr<unsigned char[]> buffer(new unsigned char[bufferSize]);

Alternative using C++20 std::make_unique_default_init():

uint bufferSize = 1024;
auto buffer = std::make_unique_default_init<unsigned char[]>(bufferSize);

Anyway, unless you use something like directly above, or you have to pass the buffer on to code expecting a newed up buffer, std::vector is likely simpler and is liable to be optimized down to the same code.

  • 2
    As C++ is currently defined, the version using malloc apparently has UB. You have to do a placement new on each int in the memory you allocated to get defined behavior. AFAIK, this has no practical effect though (doesn't change behavior with any known compiler). – Jerry Coffin Oct 17 '18 at 14:13
3

Yes, you should use a std::vector<unsigned char> for a buffer of statically-unknown size. You can also use a std::array<unsigned char, SIZE> for a buffer of statically-known size, but you don't have too.

A vector is fairly easy to use.

std::vector<unsigned char> buffer(ScreenWidth * ScreenHeight);
// use buffer.data() as you would previously use a unsigned char *

Don't use std::unique_ptr<unsigned char[]>, new, new[] or malloc, as using them is more verbose, isn't any more performant than equivalent use of vector, and requires careful inspection to ensure you haven't mistakenly got undefined behaviour. For example, you use of malloc didn't create any unsigned chars, so any use is undefined behaviour.

It may be worth declaring a type alias if you go for array

constexpr uint ScreenWidth = 1920;
constexpr uint ScreenHeight = 1080;

using ScreenBuffer = std::array<unsigned char, ScreenWidth * ScreenHeight>;
  • I upvoted for mentioned std::array. I think the answer could be much stronger if "Don't use std::unique_ptr<unsigned char[]>, new or malloc." was more than a bare assertion. – JETM Oct 17 '18 at 19:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.