63

I have an application that is performing some processing on some images.

Given that I know the width/height/format etc. (I do), and thinking just about defining a buffer to store the pixel data:

Then, rather than using new and delete [] on an unsigned char* and keeping a separate note of the buffer size, I'm thinking of simplifying things by using a std::vector.

So I would declare my class something like this:

#include <vector>

class MyClass
{
    // ... etc. ...

public:
    virtual void OnImageReceived(unsigned char *pPixels, 
        unsigned int uPixelCount);

private:
    std::vector<unsigned char> m_pImageBuffer;    // buffer for 8-bit pixels

    // ... etc. ...
};

Then, when I received a new image (of some variable size - but don't worry about those details here), I can just resize the vector (if necessary) and copy the pixels:

void MyClass::OnImageReceived(unsigned char *pPixels, unsigned int uPixelCount)
{
    // called when a new image is available
    if (m_pImageBuffer.size() != uPixelCount)
    {
        // resize image buffer
        m_pImageBuffer.reserve(uPixelCount);
        m_pImageBuffer.resize(uPixelCount, 0);
    }

    // copy frame to local buffer
    memcpy_s(&m_pImageBuffer[0], m_pImageBuffer.size(), pPixels, uPixelCount);

    // ... process image etc. ...
}

This seems fine to me, and I like that fact that I don't have to worry about the memory management, but it raises some questions:

  1. Is this a valid application of std::vector or is there a more suitable container?
  2. Am I doing the right thing performance-wise by calling reserve and resize?
  3. Will it always be the case that the underlying memory is consecutive so I can use memcpy_s as shown?

Any additional comment, criticism or advice would be very welcome.

Roger Rowland
  • 25,885
  • 11
  • 72
  • 113
  • 4
    Can I suggest using `std::vector::assign` instead of `memcpy` and (sometimes) resize? That will resize if necessary, and avoid the unnecessary initialization of the buffer. – mfontanini Oct 23 '13 at 13:18
  • @mfontanini so I can `assign` a block of memory to the `vector`? Can you show me the syntax? – Roger Rowland Oct 23 '13 at 13:22
  • The size of the buffer gets adjusted to the correct size before the call to `memcpy_s`, so the size check in `memcpy_s` is redundant. `memcpy` is sufficient here. It doesn't make much difference in this example, but in general, once you've dealt with possible buffer overflows, checking for them again just needlessly complicates the code. – Pete Becker Oct 23 '13 at 13:36
  • 2
    I suggest using m_pImageBuffer.data() to access the raw memory block instead of the address of first element – ratchet freak Oct 23 '13 at 14:56
  • @Roger Rowland You do some unnecessary operations here in my opinion. Reserve and resize can lead to a copy operation, which is useless in your case. Also the init of the vector is not necessary for you. – Martin Schlott Oct 23 '13 at 17:10
  • 1
    @PeteBecker, but what about supporting the code over the long run? Using `memcpy_s` will ensure that you don't change things around and introduce a buffer overrun later on, long after you've forgotten what was going through your head when you originally wrote it. – Katie Kilian Oct 23 '13 at 18:53
  • @CharlieKilian - if you've forgotten why you wrote code a particular way then you also forgot documentation and specification. Writing redundant checks is no substitute for a sound development process. – Pete Becker Oct 23 '13 at 20:21
  • 2
    @CharlieKilian - note also that the code that calls `memcpy_s` does not check the return value; if you forget why you resized the buffer and end up trying to write more data than the buffer will hold this call won't overwrite the buffer; all it will do is quietly produce corrupt data. There's no magic formula for writing robust code. You have to **think**. – Pete Becker Oct 23 '13 at 20:28

8 Answers8

44
  1. Sure, this'll work fine. The one thing you need to worry about is ensuring that the buffer is correctly aligned, if your class relies on a particular alignment; in this case you may want to use a vector of the datatype itself (like float).
  2. No, reserve is not necessary here; resize will automatically grow the capacity as necessary, in exactly the same way.
  3. Before C++03, technically not (but in practice yes). Since C++03, yes.

Incidentally, though, memcpy_s isn't the idiomatic approach here. Use std::copy instead. Keep in mind that a pointer is an iterator.

Starting in C++17, std::byte is the idiomatic unit of opaquely typed storage such as you are using here. char will still work, of course, but allows unsafe usages (as char!) which byte does not.

Sneftel
  • 40,271
  • 12
  • 71
  • 104
  • Thanks - good point about alignment, I may want to align for SSE2 later, although still as unsigned char. Can I specify alignment somehow in the vector? – Roger Rowland Oct 23 '13 at 13:14
  • "resize will automatically grow the capacity as necessary, in exactly the same way." Not in exactly the same way. I'd actually say it's `resize` that is not necesarry here. `reserve` will ensure there is enough `capacity`, and `resize` does that plus sets every value to their defaults and changes the `size` of the vector. – John Dibling Oct 23 '13 at 13:15
  • 2
    Only by providing a particular allocator. If you want to align for __m128, just make a vector of __m128s. – Sneftel Oct 23 '13 at 13:15
  • 1
    @JohnDibling -- data is being copied into the elements by memcpy_s; this will be effectless at best or crashy at worst if the vector is not first resized. – Sneftel Oct 23 '13 at 13:16
  • @RogerRowland `reserve` will make sure the capacity is exactly what is specified. `resize` will *grow* the vector so that the capacity is *at least* enough. If the capacity is 4 and you need 5, `reserve` will expand the capacity to 5, but `resize` will expand the capacity to 8 (x2) and set the size to 5. – SoapBox Oct 23 '13 at 13:19
  • 1
    @Ben: I was just pointing out that `resize` and `reserve` do not do the same thing. The end result here might be the same, but that is not because the functions do the same thing. – John Dibling Oct 23 '13 at 13:30
  • 2
    3. is wrong, this was guaranteed in C++03 as well. You’re thinking of `std::string`. – Konrad Rudolph Oct 23 '13 at 21:53
  • 2
    That's what I said -- guaranteed since C++03, but not before C++03. In contrast, it was not guaranteed in C++98. – Sneftel Oct 23 '13 at 21:57
  • It's a shame I can't accept more than one answer here, there's an awful lot of helpful feedback and discussion from a number of users. Thanks to all! – Roger Rowland Oct 24 '13 at 06:19
26

Besides what other answers mention, I would recommend you to use std::vector::assign rather than std::vector::resize and memcpy:

void MyClass::OnImageReceived(unsigned char *pPixels, unsigned int uPixelCount)
{
    m_pImageBuffer.assign(pPixels, pPixels + uPixelCount);
}

That will resize if necessary, and you would be avoiding the unnecessary 0 initialization of the buffer caused by std::vector::resize.

mfontanini
  • 21,410
  • 4
  • 65
  • 73
  • Thanks - I'm liking this - does anyone else disagree with this suggestion? – Roger Rowland Oct 23 '13 at 13:26
  • I disagree that resize() causes 0 initialization, otherwise it is a good advice. Using m_pImageBuffer = std::vector(pPixels, pPixels + uPixelCount) might be more explicit. – Slava Oct 23 '13 at 13:40
  • 2
    @Slava how does it not cause 0 initialization? If the new size is greater than the old one, the new elements will be value initialized(0 initialized for usnigned chars) or initialized with the second argument in OP's case. – mfontanini Oct 23 '13 at 13:42
  • @Slava: `resize` called with only 1 argument (the count) will value-initialize any elements that need to be added. There's little to disagree with on this point. – John Dibling Oct 23 '13 at 13:43
  • 1
    This is the best solution so far. – Diego Giagio Oct 23 '13 at 13:43
  • 1
    @RogerRowland: I would consider this to be cannonical as well. In fact it's probably better than my `copy` method. – John Dibling Oct 23 '13 at 13:46
  • One thing to bear in mind is that `assign` may result in multiple reallocations whereas `reserve(); clear(); copy()` will reallocate only once. Worrying about this borders between selecting the best algorithm and premature micro-optimization. – John Dibling Oct 23 '13 at 13:50
  • @JohnDibling is that really true? I would be suprised if more than one reallocation was performed when using random access iterators. I don't know what the standard says, however. – mfontanini Oct 23 '13 at 13:52
  • @JohnDibling well, micro-optimisations aside, I also like that fact that this is just a single line of code :-) – Roger Rowland Oct 23 '13 at 13:57
  • The Standard dictates that `assign` has the effect of `clear(); insert(...);` 23.3.6.5 says that `insert()` "Causes reallocation if the new size is greater than the old capacity.". If you're using the version of `insert` which takes iterators (or pointers), then I don't see how multiple reallocations can be avoided aside from the normal technique of chunking. That is, `insert` won't be able to reallocate once with the *right* number of elements, because the right number of elements can't be known. At least that's my interpretation. – John Dibling Oct 23 '13 at 13:58
  • 3
    @JohnDibling but if you're using RandomAccessIterators, then you *can* now how many elements there are in constant time. I mean, it might not be explicitly stated by the standard, but I'd be suprised if compilers didn't take this optimization into account. – mfontanini Oct 23 '13 at 14:04
  • I suppose that's probably true. – John Dibling Oct 23 '13 at 14:16
  • 3
    Of course, you can force the optimization by using `reserve` and then `assign`. – Ben Voigt Oct 31 '13 at 17:13
16

Using a vector in this case is fine. In C++ the storage is guaranteed to be contigious.

I would not both resize and reserve, nor would I memcpy to copy the data in. Instead, all you need to do is reserve to make sure you don't have to reallocate many times, then clear out the vector using clear. If you resize, it will go through and set the values of every element to their defaults -- this is unnecesarry here because you're just going to overwrite it anyway.

When you're ready to copy the data in, don't use memcpy. Use copy in conjunction with back_inserter into an empty vector:

std::copy (pPixels, pPixels + uPixelCount, std::back_inserter(m_pImageBuffer));

I would consider this idiom to be much closer to canonical than the memcpy method you are employing. There might be faster or more efficient methods, but unless you can prove that this is a bottleneck in your code (which it likely won't be; you'll have much bigger fish to fry elsewhere) I would stick with idiomatic methods and leave the premature micro-optimizations to someone else.

John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • Ahhh, good stuff, this is very helpful - thanks! I'm using VS2012 which is about as near to C++11 as MS will get, so I can assume contiguous data (fingers crossed). Is `std::copy` more efficient than `memcpy_s` or just better code (or both)? – Roger Rowland Oct 23 '13 at 13:18
  • 1
    The use of `back_inserter` here is likely to make things less efficient than `memcpy_s`, because it works by repeatedly incrementing the same member, rather than setting the size all at once. In contrast, resizing a large vector, initializing it with all zeros, is generally implemented with special assembly routines and is *very very fast*. The same is true of copying a large amount of POD data from one array to another with a single (non-back-inserter-taking) std::copy. – Sneftel Oct 23 '13 at 13:19
  • @Ben thanks for all of your input, I'm beginning to think that some profiling might be worthwhile here too - I've no idea what differences there may be with VS2012 std implementation. – Roger Rowland Oct 23 '13 at 13:24
  • Honestly, no matter what you do, this is unlikely to be anywhere near the slowest part of your application. This is exactly the type of operation that modern, cache-predicting, out-of-order, multiple-issue CPUs do a really good job on. – Sneftel Oct 23 '13 at 13:27
  • 1
    @RogerRowland: I would say better code because it is idiomatic. As Ben argues, `memcpy` might indeed be faster -- then again, might not -- however unless you have demonstrated through profiling that using `back_inserter` is an actual bottleneck in your code, I would categorize using `memcpy` as a premature micro-optimization. Simply because it diverges from the idiom, and ventures close to platform-specific behavior where there be dragons. – John Dibling Oct 23 '13 at 13:28
  • 2
    Agreed. The brain-time you're spending on this could be better put to use refactoring that O(n^2) code block over there into O(n log n). – Sneftel Oct 23 '13 at 13:31
  • Thanks @JohnDibling - I'd hate to "diverge from the idiom" as I've been guilty of that in the past, to my cost. I agree with both you and Ben about performance too, there's a heck of a lot of bottlenecks elsewhere already so I'll stick to clean, idiomatic code I guess. – Roger Rowland Oct 23 '13 at 13:33
  • I know it's entirely implementation dependent but on VS-2012 Release mode I tested a vector.resize followed by a std::copy into the vector (Not using back_inserter, directly into the allocated space), and the compiler just turned the std::copy into a memmove looking at the assembly language generated. So in this case at least there is nothing much to worry about for efficiency. Obviously back_inserter can't do this because it needs to check and potentially grow the vector for each insert – jcoder Oct 23 '13 at 14:07
  • @jcoder: Yeah, `std::copy` is pretty close to a mem-cpy type operation. `back_inserter` simply calls `vector::push_back`, which in turn will `insert`. You can avoid the reallocation by `reserve`ing the `vector` beforehand. – John Dibling Oct 23 '13 at 14:19
  • 1
    You can avoid the reallocation but not the test to see if it's needed before each insert, which then most likely means the fast memmove turns into a slow character by character copy. – jcoder Oct 23 '13 at 14:21
  • Why not just `m_pImageBuffer.insert(end(m_pImageBuffer), pPixels, pPixels+uPixelCount);`? If you have the container itself, and not just an iterator... (at least in libstdc++, `insert` allocates only once for >= `ForwardIterator`). Btw: there's a secondary backtick before `back_inserter` in your answer. – dyp Oct 23 '13 at 15:45
9

I would avoid std::vector as a container for storing an unstructured buffer, as std::vector is profoundly slow when used as a buffer

Consider this (C++14) example (for C++11, you can used shared instead of unique ptrs, but you'll notice slight performance hit in the array example that you don't get from the vectors when running at -O3 or -O2):

#include <chrono>
#include <ctime>
#include <iostream>
#include <memory>
#include <vector>

namespace {
std::unique_ptr<unsigned char[]> allocateWithPtr() {
  return std::unique_ptr<unsigned char[]>(new unsigned char[4000000]);
}

std::vector<unsigned char> allocateWithVector() {
  return std::vector<unsigned char>(4000000);
}
} // namespace

int main() {
  auto start = std::chrono::system_clock::now();

  for (long i = 0; i < 1000; i++) {
    auto myBuff = allocateWithPtr();
  }
  auto ptr_end = std::chrono::system_clock::now();

  for (long i = 0; i < 1000; i++) {
    auto myBuff = allocateWithVector();
  }
  auto vector_end = std::chrono::system_clock::now();

  std::cout << "std::unique_ptr = " << (ptr_end - start).count() / 1000.0
            << " ms." << std::endl;
  std::cout << "std::vector = " << (vector_end - ptr_end).count() / 1000.0
            << " ms." << std::endl;
}

Output:

bash % clang++ -O3 -std=gnu++14 test.cpp && ./a.out
std::unique_ptr = 0 ms.
std::vector = 0 ms

bash % clang++ -O2 -std=gnu++14 test.cpp && ./a.out
std::unique_ptr = 0 ms.
std::vector = 0 ms.

bash % clang++ -O1 -std=gnu++14 test.cpp && ./a.out
std::unique_ptr = 89.945 ms.
std::vector = 14135.3 ms.

bash % clang++ -O0 -std=gnu++14 test.cpp && ./a.out
std::unique_ptr = 80.945 ms.
std::vector = 67521.1 ms.

(Updated for Windows Visual Studio)
Debug:
std::unique_ptr = 8245 ms
std::vector = 9131.98 ms

Release:
std::unique_ptr = 79.46 ms
std::vector = 6830 ms

Even with no writes or reallocations, std::vector is over 800 times slower than just using a new with a unique_ptr at -O0 and 150 times slower at -O1. What's going on here?

As @MartinSchlott points out, it is not designed for this task. A vector is for holding a set object instances, not an unstructured (from an array standpoint) buffer. Objects have destructors and constructors. When the vector is destroyed, it calls the destructor for each element in it, even vector will call a destructor for each char in your vector, and it initializes the members on construction.

You can see how much time it takes just to "destroy" the unsigned chars in this vector with this example:

#include <chrono>
#include <ctime>
#include <iostream>
#include <memory>
#include <vector>

namespace {
std::vector<unsigned char> allocateWithVector() {
    return std::vector<unsigned char>(4000000); }
}

int main() {
    auto start = std::chrono::system_clock::now();

    for (long i = 0; i < 100; i++) {
        auto leakThis = new std::vector<unsigned char>(allocateWithVector());
    }
    auto leak_end = std::chrono::system_clock::now();

    for (long i = 0; i < 100; i++) {
        auto myBuff = allocateWithVector();
    }
    auto vector_end = std::chrono::system_clock::now();

    std::cout << "leaking vectors: = "
              << (leak_end - start).count() / 1000.0 << " ms." << std::endl;
    std::cout << "destroying vectors = "
              << (vector_end - leak_end).count() / 1000.0 << " ms." << std::endl;
}

Output:

leaking vectors: = 2058.2 ms.
destroying vectors = 3473.72 ms.

real    0m5.579s
user    0m5.427s
sys 0m0.135s

Even when removing the destruction of the vector, it's still taking 2 seconds to just construct 100 of these things.

If you're using the buffer to just hold incoming data and you don't require initialization, it's a waste of time to be initializing the bytes to 0 when you construct the vector. The Windows numbers above are so dramatic in Release mode because the OS is not even allocating memory yet. If you memset the data after the new, you'll see it's the same speed for constructing as the vector, but each additional memset adds 2 ms per buffer. So you're spending about 5 ms on the malloc, and 2 on the init. You can create a custom allocator for your vector to avoid initializing your bytes, but at this point, why are you working so hard to make a square peg fit in a round hole.

If you don't need dynamic resizing, or construction & destruction of the elements making up your buffer, don't use std::vector.

Steve Broberg
  • 4,255
  • 3
  • 28
  • 40
  • 1
    This is a meaningful answer, while the accepted one is not. For basic types using vector as a buffer can a lot of useless overhead for construction and destruction. – Pari Jul 10 '19 at 14:07
  • Going forward, this is definitely the wrong answer. You should never explicitly use the `new` operator. If you do, you are probably misusing modern c++.If you're seeing vector allocation as slow, you're probably doing unnecessary initialization, like calling resize prior to assignment. If you're seeing any deallocation as slow, you probably are doing unnecessary destructor calls or have a debugger attached. https://www.modernescpp.com/index.php/no-new-new – Russell Trahan Jun 14 '20 at 18:16
  • @RussellTrahan: In my example above, I'm not running in the debugger, and Im not using any unnecessary destructors - it's std::vector that's performing the unnecessary destructors. The distinction between using new inside a std::unique_ptr and using make_unique is a semantic one, but I've updated my example to be in line with more modern C++ conventions. As my example shows, compiling at the highest optimization levels eliminates the penalty for the bad choice of vector, but that is not always an option for production code, and greatly harms development work. – Steve Broberg Jun 15 '20 at 16:06
  • Aren't you ignoring the fact that std::vector is initializing all of those unsigned chars to 0, but std::array isn't? – Russell Trahan Jun 16 '20 at 04:40
  • 1
    @RussellTrahan: Is there another way to construct the vector to bypass the initialization? The original question is about using vectors as buffers to store image data, so presumably you don't want the initialization (and destruction) that vectors are giving you in the default/naive cases. The whole point of using an array (or new type[n] in the old days) is that it is NOT initializing memory for you. – Steve Broberg Jun 20 '20 at 19:13
  • @RussellTrahan: I think I may know what you're referring to - the fact that with modern memory mappers, allocation of memory is often a no-op that takes virtually no time at all, due to the fact that the OS is deferring the work to actually allocate the memory until the address is actually written to or read. However, my examples above are taken from real-world situations where we witnessed the slowdown in nontrivial production code. You can observe the invocation of the constructors called by vector using a profiler. – Steve Broberg Jun 20 '20 at 19:18
  • Using std::make_unique> is essentially the same as just calling malloc. Using std::vector(4000000) is essentially the same thing as calling malloc THEN calling std::assign(myarray.begin(), myarray.end(), 0). Vector assigns all the contents to 0, whereas std::array does NOT. You aren't comparing apples to oranges if you neglect the initialization of vector contents to 0. – Russell Trahan Jun 21 '20 at 21:19
  • Alternatively, construct the vector and immediately fill it's contents with your desired values by using the range based constructor. Perhaps compare that to std::array and std::assign to the array to zeros. The STL is only ever slower than raw code when it's misused, and calling std::vector(4000000) then trying to populate the vector is misuse. The initialization to 0 is an unnecessary step. – Russell Trahan Jun 21 '20 at 21:20
  • Today I tried to replace my std::vector with a custom build class that replaces resize(), size(), data() function, this class wrapped a raw allocated buffer. In release build i see no difference with stl vector implementation. Your tests just show me that it is slower in debug. That is true, in debug stl code is quite slow by default. – Richy Feb 10 '22 at 07:58
  • @Richy, my argument mainly applies to cases where you don't plan on resizing a buffer. If you are resizing, the extra machinery in std::vector is probably worth it just to avoid coding errors when you roll your own. The non-resizing case is simply a matter of using new, so there isn't much to screw up. However, resizeable buffers can land you with performance problems that slip past design and testing due to the big expense of realloc that can explode when it gets called frequently. – Steve Broberg Feb 28 '22 at 04:43
3

std::vector was MADE to be used in such cases. So, yes.

  1. Yes, it is.

  2. reserve is unnecessary in your case.

  3. Yes, it will.

Ivan Ishchenko
  • 1,468
  • 9
  • 12
2

It depends. If you access the data only through iterators and the [] operator, than its okay to use a vector.

If you have to give a pointer to functions which expect a buffer of e.g. bytes. It is not in my opinion. In this case You should use something like

unique_ptr<unsigned char[]> buf(new unsigned char[size])

is it as save as a vector, but instead of a vector you have maximum control of the buffer. A vector may reallocate a buffer or during a method/function call you may unintentionally make a copy of your whole vector. A easily made mistake.

The rule (for me) is. If you have a vector, use it like a vector. If you need a memory buffer, use a memory buffer.

As in a comment pointed out, the vector has a data method. This is C++. The freedom of using a vector as a raw buffer does not mend that you should use it as a raw buffer. In my humble opinion, the intention of a vector was to have a type save buffer with type save access system. For compatibility you can use the internal buffer for calls. The intention was not to use the vector as a smart pointer buffer container. For that, I use the pointer templates, signaling other user of my code that I use this buffer in a raw way. If I use vectors, I use them in the way they are intended to, not the possible ways they offer.

AS I got some blame here for my opinion (not recommendation) I want to add some words to the actual problem the op described.

If he expect always the same picture size, he should, in my opinion, use a unique_ptr, because that's what he is doing with it in my opinion. Using

 m_pImageBuffer.resize(uPixelCount, 0);

zeros the buffer first before he copy the pPixel to it, a unnecessary time penalty.

If the pictures he is expecting of different size, he should, in my opinion, not use a vector during following reason. Especially in his code:

// called when a new image is available
if (m_pImageBuffer.size() != uPixelCount)
{
    // resize image buffer
    m_pImageBuffer.reserve(uPixelCount);
    m_pImageBuffer.resize(uPixelCount, 0);
}

he will resize the vector, which is in fact a malloc and copy as long as the images are getting bigger. A realloc in my experience always leads to malloc and copy.

That is the reason I, especially in this situation, recommand the use of a unique_ptr instead of a vector.

Martin Schlott
  • 4,369
  • 3
  • 25
  • 49
  • Yes, I *do* need to pass a pointer to the buffer as bytes - why is this a problem? Are you saying that the memory may not be consecutive or is it just that I need to make sure I don't `resize` while something else is using it? (I can understand that one). – Roger Rowland Oct 23 '13 at 13:12
  • what's the problem? for these cases they've added data() method to vector. – zoska Oct 23 '13 at 13:16
  • The memory will not resize and it will be consecutive. But in your example you only need the raw buffer. a unique_ptr will be better if you work in a team, where teammates expect that a vector is used as vector, not as raw buffer. For the compiler or your program, it makes no difference. – Martin Schlott Oct 23 '13 at 13:26
  • Standard guarantees that memory allocated by std::vector is continuous, so your opinion is wrong. And about resizing, if you would work with vector as with normal buffer (get enough size in advance and use it) there would not be any issue. If you need to increase your buffer size vector will do that for you and at least with effectiveness not worse as creating another buffer and copy all data there manually. – Slava Oct 23 '13 at 13:30
  • @Slava , you are right, but I didn't say that a vector is not to be guaranteed to be continuous. If you got a growing list of elements, which you do not want to store in a list for a reason, I use a vector too. I myself stopped using vectors for the case the op showed for his picture buffer, as it is a kind of misuse in my opinion. It has no benefit in opposite to a unique_ptr. Also, using a cat to polish your shoes works fine, it also has a fur wich seems to be made for it. But it it is not okay. – Martin Schlott Oct 23 '13 at 13:44
  • @MartinSchlott the thing is that std::vector was made for that. And it has benefit opposite to a unique_ptr - for example buffer size can grow, and I bet it will grow pretty efficient way. – Slava Oct 23 '13 at 13:51
  • @Slava, I use unique_ptr in situations where a grown is not necessary, like the example shown by the op. As you implicit said, we have no control over the vector grow algorithm in the usual situation. So I do not know if it is always the best for every situation I use vectors for, I do not even bet ;-). I am with you if you say, a vector is a replacement for a raw buffer, as it is type save. We have different opinions (which is totally okay) when you say it is okay to use the only the pointer aspect of the vector. – Martin Schlott Oct 23 '13 at 14:00
  • 1
    @MartinSchlott you do know, at least you able to know when std::vector will grow. It does not happen suddenly and it is documented. Yes if you prefer you do not have to use std::vector as a buffer, but you should not recommend that to others. That's my opinion. – Slava Oct 23 '13 at 14:07
  • 1
    I added a better explanation for the reason I recommend (in my opinion) pointer_ptr. As he ask for advise so I thought it was legit to point out my view of things. – Martin Schlott Oct 23 '13 at 17:07
  • @MartinSchlott thanks for sticking with this - I know you got a lot of opinions different from yours, but I really appreciate you taking the time to explain your reasoning and keeping the discussion cool and objective, which is good to see anywhere, but especially on SO. Thanks again. – Roger Rowland Oct 23 '13 at 18:53
  • See my recently added answer for other reasons to avoid std::vector for blind buffers. – Steve Broberg Jun 08 '16 at 17:35
2

In addition - to ensure a minimum of allocated memory:

void MyClass::OnImageReceived(unsigned char *pPixels, unsigned int uPixelCount)
{
    m_pImageBuffer.swap(std::vector<unsigned char>(
         pPixels, pPixels + uPixelCount));
    // ... process image etc. ...
}

vector::assign does not change the amount of memory allocated, if the capacity is bigger than the amount needed:

Effects: erase(begin(), end()); insert(begin(), first, last);

2

Please, consider this:

void MyClass::OnImageReceived(unsigned char *pPixels, unsigned int uPixelCount)
{
    // called when a new image is available
    if (m_pImageBuffer.size() != uPixelCount) // maybe just <  ??
    {
        std::vector<unsigned char> temp;
        temp.reserve(uPixelCount);        // no initialize
        m_pImageBuffer.swap(temp) ;       // no copy old data
    }

    m_pImageBuffer.assign(pPixels, pPixels + uPixelCount);  // no reallocate

    // ... process image etc. ...
}

My point is that if you have a big picture and need a litter bigger pic, your old pic will get copy during the reserve and/or resize into the new allocated memmory, the excess of memmory initialized, and then rewrited with the new pic. You colud directly assing, but then you will no be able to use the info you have about the new size to avoid posible reallocations (maybe the implementation of assign is allready optimize for this simple case ????).

qPCR4vir
  • 3,521
  • 1
  • 22
  • 32