I have an array char* source
and a vector std::vector<char> target
. I'd like to make the vector target
point to source
in O(1), without copying the data.
Something along these lines:
#include <vector>
char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);
target.setData(source); // <- Doesn't exist
// OR
std::swap(target.data(), source); // <- swap() does not support char*
delete[] source;
Why is it not possible to manually change where a vector points to? Is there some specific, unmanageable problem that would arise if this was possible?
An ordinary vector encountered in C++ programming, is a vector of objects of the same type. These objects can be fundamental objects or objects instantiated from a class. This article illustrates examples of vector of pointers, to same object type.
You can store pointers in a vector just like you would anything else. Declare a vector of pointers like this: vector<MyClass*> vec; The important thing to remember is that a vector stores values without regard for what those values represent.
C++ is a statically-typed language. A vector will hold an object of a single type, and only a single type.
The syntax for modifying values from a vector vectorname. assign(InputIterator first, InputIterator last) Parameters: first - Input iterator to the initial position range. last - Input iterator to the final position range.
C++ vector
class supports adding and deleting elements, with guaranteed consecutive order in memory. If you could initialize your vector
with existing memory buffer, and add enough elements to it, it would either overflow or require reallocation.
The interface of vector
assumes that it manages its internal buffer, that is, it can allocate, deallocate, resize it whenever it wants (within spec, of course). If you need something that is not allowed to manage its buffer, you cannot use vector
- use a different data structure or write one yourself.
You can create a vector
object by copying your data (using a constructor with two pointers or assign
), but this is obviously not what you want.
Alternatively, you can use string_view
, which looks almost or maybe exactly what you need.
std::vector
is considered to be the owner of the underlying buffer. You can change the buffer but this change causes allocation i.e. making a copy of the source buffer which you don't want (as stated in the question).
You could do the following:
#include <vector>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);
target.assign(source, source + 3);
delete[] source;
return 0;
}
but again std::vector::assign
:
Replaces the contents with copies of those in the range [first, last).
So copy is performed again. You can't get away from it while using std::vector
.
If you don't want to copy data, then you should use std::span
from C++20 (or create your own span) or use std::string_view
(which looks suitable for you since you have an array of char
s).
1st option: Using std::string_view
Since you are limited to C++17, std::string_view
might be perfect for you. It constructs a view of the first 3 characters of the character array starting with the element pointed by source
.
#include <iostream>
#include <string_view>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::string_view strv( source, 3 );
delete[] source;
return 0;
}
2nd option: Using std::span
from C++20
std::span
comes from C++20 so it might not be the most perfect way for you, but you might be interested in what it is and how it works. You can think of std::span
as a bit generalized version of std::string_view
because it is a contiguous sequence of objects of any type, not just characters. The usage is similar as with the std::string_view
:
#include <span>
#include <iostream>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::span s( source, 3 );
delete[] source;
return 0;
}
3rd option: Your own span
If you are limited to C++17, you can think of creating your own span
struct. It might still be an overkill but let me show you (btw take a look at this more elaborated answer):
template<typename T>
class span {
T* ptr_;
std::size_t len_;
public:
span(T* ptr, std::size_t len) noexcept
: ptr_{ptr}, len_{len}
{}
T& operator[](int i) noexcept {
return *ptr_[i];
}
T const& operator[](int i) const noexcept {
return *ptr_[i];
}
std::size_t size() const noexcept {
return len_;
}
T* begin() noexcept {
return ptr_;
}
T* end() noexcept {
return ptr_ + len_;
}
};
int main() {
char* source = new char[3] { 1, 2, 3 };
span s( source, 3 );
delete[] source;
return 0;
}
So the usage is the same as with the C++20's version of std::span
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With