How to declare an array with a variable number of elements [C++]? - c++

How do I [dynamically?] declare an array with an 'i' number of elements?

By using a container of the standard-library instead, most commonly std::vector<>.
Other solutions contain manual allocation and deallocation of memory and are probably not worth the effort.

You could use std::vector<T>. It works like
std::vector<int> a;
a.push_back(2); // add 2 to the array
a.push_back(4);
You could go on and on, and you won't need to worry about memory allocation issues.

You have to use pointers.
Example:
float *ptrarray = new float [10];
Basically, type * pointername = new type [i];
And don't forget to clean your memory:
delete [] ptrarray;
Or it will be reserved until the program ends.

Create a Dynamically Sized Array
std::size_t N = 10;
SomeType *a = new SomeType[N];
for (auto i=0; i<N; ++i) {
std::cout << a[i] << std::endl;
}
delete [] a;
A C++ array is a pointer to a contiguous chunk of memory containing a specific type. This snippet allocates space for an array of 10 SomeType instances, constructs the array which initializes each object using the default constructor, iterates over the array, prints out each element, and then deallocates the memory by calling delete [].
Key Points:
you are required to call delete [] to deallocate the array. The array form is required to ensure that the destructor is called on each object.
after allocation, there is no way to recover the size of the array.
you can iterate over the array by index (or pointer)
This is not the way to do what you want, keep reading.
Using a vector
std::size_t N = 10;
std::vector<SomeType> v(10);
for (auto iter=v.begin(); iter!=v.end(); ++iter) {
std::cout << *iter << std::endl;
}
This snippet uses std::vector which manages a contiguous block of memory for you. Note the usage of an iterator to walk over the vector instead of using indexing. Vectors do support direct indexing so the for loop used in the previous example would work here as well.
for (auto i=0; i<v.size(); ++i) {
std::cout << v[i] << std::endl;
}
Using an iterator is the best practice and idiomatic though using a for loop may not be but I digress.
Key Points:
memory management is automatic in the case of a vector
you can append using push_back
a vector knows how many elements are in it -- call v.size() to get the number of elements
iteration is performed using the iterators returned from v.begin() and v.end()
Just use std::vector. If you need raw access to the underlying pointer, then use &v[0] or v.data() but don't do that unless you need to. Also, don't use std::auto_ptr for arrays. You will be tempted to but don't do it.

Related

C++ : Size of the array didn't change even after deleting it

Assuming I have this program below
The size of the array is 3 and basically when deleting it and then try to print the size again , I still get the value 3 .
Why is that ?
#include <vector>
#include <iostream>
int main(){
std::vector<int*> v;
v.push_back(new int{1});
v.push_back(new int{2});
v.push_back(new int{3});
std::cout << v.size() << '\n';
for (auto iptr : v){
delete iptr;
}
std::cout << v.size() << '\n';
return 0;
}
Because you deleted whatever was pointed by the pointer
vector<CPerson *> Persons;
As far as the vector is concerned, it still has 3 pointers pointing somewhere.Beware, you are not permitted to dereference them pointers, otherwise you will invoke undefined behavior).
You have a vector of pointers. Using delete on a pointer in that vector will delete the thing the pointer is pointing at. It does not remove the pointer itself from the vector, and so does not change the size of the vector. Why should it? How could it?
Here the code you were looking for (I guess):
for (int i = 0; i < Persons.size(); i++)
{
CPerson *Student = Persons[i];
if (Student)
{
Persons.erase(Persons.begin() + i);
delete Student;
i--;
}
}
Note:
vector::erase is (one way) to remove items from a vector.
The cast is unnecessary, because your base class has a virtual destructor.
Erasing items from a vector while you are iterating through it is tricky because the size of the vector changes when you erase an item and all the later items in the vector move down one place, that why I have i-- to compensate, without that you will at least skip items and at worst iterate off the end of the vector.
When you delete the pointers that are in the vector, it just deletes the memory that you assigned to them with new, it doesn't do anything about the fact that you have a pointer, there for you are cleaning up memory that is outside of the vector, the vector will clean it self up at the end of the scope. (In this case, the end of the function.) Just remember, the vector doesn't clean up your new memory, you have to clean that up.

Buffer Overflow a Vector

I am attempting to create a buffer-overflow-proof program and I was considering using an stl vector for this, but I have read that this still would not perform bounds checking and could still be hacked. I am mainly concerned about overriding a return or function call or another variable in my program. Would a vector work in this case?
You can declare a vector like this
std::vector<int> v;
This is an empty vector of ints called v. It has 0 elements. Attempting to access any of its elements is undefined behavior. So yes, it's possible to create a buffer overflow in a std::vector if you treat it poorly.
v[0] = 1; // undefined because we're out of bounds
v[1] = 2; // as is this
Normally when adding elements to a std::vector, you use push_back or emplace_back, though, not direct element access
v.push_back(0); // v is now 1 element long: { 0 }
v.emplace_back(1); // v is now 2 elements long: { 0 , 1 }
Using the [] operator does give you a reference to the underlying item, so you can use it both for retrieval and assignment, as long as you're sure the index is valid. You can use the size() function to determine what the appropriate max element index is (it's size() - 1)
for (std::size_t i = 0; i < v.size(); ++i)
{
v[i] = 10;
}
// v was size 2, we've replaced both elements at 0 and 1 with value 10
When using your std::vector, use the interface provided to you. Use push_back or emplace_back or insert to add elements. pop_back or erase to remove elements. A std::vector knows its begin and end, so you can iterate through it like this:
// prints the elements in the vector
for (auto it = v.begin(); it != v.end(); ++it)
std::cout << *it << " ";
std::cout << "\n";
or a ranged-based for loop, since it provides the begin() and end() functions
// prints the elements in the vector
for (const auto& elem : v)
std::cout << elem << " ";
std::cout << "\n";
It's possible to overrun your std::vector buffer, but very unlikely if you follow the rules. A std::vector is nicer than an array, so if you don't mind your elements existing in the free store (which is most non-high-performance applications), go for it. The ease of use and flexibility is well worth the extra overhead.
Further, because the data in your std::vector does NOT exist on the stack (while a local array is placed on the stack) you're very unlikely to overwrite your stack if you do overrun your std::vector. If you're lucky, your program will crash, but don't rely on this.
In conclusion, a std::vector can have its buffer overrun, but its interface makes it very hard to do so. If you ever do overrun its buffer, you're overwriting data in the free store, not on the stack, so you won't be modifying your return address. While you could potentially have a second item in the free store be written over by a vector buffer overrun, I believe this is non-deterministic. While anyone with access to your source code with an array could easily identify what input would produce a stack overwrite, if any.
See https://en.cppreference.com/w/cpp/container/vector for more information on the std::vector interface.

C++: how to loop through integer elements in a vector

I would like to loop through elements of a vector in C++.
I am very new at this so I don't understand the details very well.
For example:
for (elements in vector) {
if () {
check something
else {
//else add another element to the vector
vectorname.push_back(n)
}
}
Its the for (vector elements) that I am having trouble with.
You'd normally use what's called a range-based for loop for this:
for (auto element : your_vector)
if (condition(element))
// whatever
else
your_vector.push_back(something);
But note: modifying a vector in the middle of iteration is generally a poor idea. And if your basic notion is to add the element if it's not already present, you may want to look up std::set, std::map, std::unordered_set or std::unordered_map instead.
In order to do this properly (and safely), you need to understand how std::vector works.
vector capatity
You may know that a vector works much like an array with "infinite" size. Meaning, it can hold as many elements as you want, as long as you have enough memory to hold them. But how does it do that?
A vector has an internal buffer (think of it like an array allocated with new) that may be the same size as the elements you're storing, but generally it's larger. It uses the extra space in the buffer to insert any new elements that you want to insert when you use push_back().
The amount of elements the vector has is known as its size, and the amount of elements it can hold is known as its capacity. You can query those via the size() and capacity() member functions.
However, this extra space must end at some point. That's when the magic happens: When the vector notices it doesn't have enough memory to hold more elements, it allocates a new buffer, larger1 than the previous one, and copies all elements to it. The important thing to notice here is that the new buffer will have a different address. As we continue with this explanation, keep this in mind.
iterators
Now, we need to talk about iterators. I don't know how much of C++ you have studied yet, but think of an old plain array:
int my_array[5] = {1,2,3,4,5};
you can take the address of the first element by doing:
int* begin = my_array;
and you can take the address of the end of the array (more specifically, one past the last element) by doing:
int* end = begin + sizeof(my_array)/sizeof(int);
if you have these addresses, one way to iterate the array and print all elements would be:
for (int* it = begin; it < end; ++it) {
std::cout << *it;
}
An iterator works much like a pointer. If you increment it (like we do with the pointer using ++it above), it will point to the next element. If you dereference it (again, like we do with the pointer using *it above), it will return the element it is pointing to.
std::vector provides us with two member functions, begin() and end(), that return iterators analogous to our begin and end pointers above. This is what you need to keep in mind from this section: Internally, these iterators have pointers that point to the elements in the vector's internal buffer.
a simpler way to iterate
Theoretically, you can use std::vector::begin() and std::vector::end to iterate a vector like this:
std::vector<int> v{1,2,3,4,5};
for (std::vector<int>::iterator it = v.begin; it != v.end(); ++it) {
std::cout << *it;
}
Note that, apart from the ugly type of it, this is exactly the same as our pointer example. C++ introduced the keyword auto, that lets us get rid of these ugly types, when we don't really need to know them:
std::vector<int> v{1,2,3,4,5};
for (auto it = v.begin; it != v.end(); ++it) {
std::cout << *it;
}
This works exactly the same (in fact, it has the exact same type), but now we don't need to type (or read) that uglyness.
But, there's an even better way. C++ has also introduced range-based for:
std::vector<int> v{1,2,3,4,5};
for (auto it : v) {
std::cout << it;
}
the range-based for construct does several things for you:
It calls v.begin() and v.end()2 to get the upper and lower bounds of the range we're going to iterate;
Keeps an internal iterator (let's call it i), and calls ++i on every step of the loop;
Dereferences the iterator (by calling *i) and stores it in the it variable for us. This means we do not need to dereference it ourselves (note how the std::cout << it line looks different from the other examples)
putting it all together
Let's do a small exercise. We're going to iterate a vector of numbers, and, for each odd number, we are going to insert a new elements equal to 2*n.
This is the naive way that we could probably think at first:
std::vector<int> v{1,2,3,4,5};
for (int i : v) {
if (i%2==1) {
v.push_back(i*2);
}
}
Of course, this is wrong! Vector v will start with a capacity of 5. This means that, when we try using push_back for the first time, it will allocate a new buffer.
If the buffer was reallocated, its address has changed. Then, what happens to the internal pointer that the range-based for is using to iterate the vector? It no longer points to the buffer!
This it what we call a reference invalidation. Look at the reference for std::vector::push_back. At the very beginning, it says:
If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.
Once the range-based for tries to increment and dereference the now invalid pointer, bad things will happen.
There are several ways to avoid this. For instance, in this particular algorithm, I know that we can never insert more than n new elements. This means that the size of the vector can never go past 2n after the loop has ended. With this knowledge in hand, I can increase the vector's capacity beforehand:
std::vector<int> v{1,2,3,4,5};
v.reserve(v.size()*2); // Increases the capacity of the vector to at least size*2.
// The code bellow now works properly!
for (int i : v) {
if (i%2==1) {
v.push_back(i*2);
}
}
If for some reason I don't know this information for a particular algorithm, I can use a separate vector to store the new elements, and then add them to our vector at the end:
std::vector<int> v{1,2,3,4,5};
std::vector<int> doubles;
for (int i : v) {
if (i%2==1) {
doubles.push_back(i*2);
}
}
// Reserving space is not necessary because the vector will allocate
// memory if it needs to anyway, but this does makes things faster
v.reserve(v.size() + doubles.size());
// There's a standard algorithm (std::copy), that, when used in conjunction with
// std::back_inserter, does this for us, but I find that the code bellow is more
// readable.
for (int i : doubles) {
v.push_back(i);
}
Finally, there's the old plain for, using an int to iterate. The iterator cannot be invalidated because it holds an index, instead of a pointer to the internal buffer:
std::vector<int> v{1,2,3,4,5};
for (int i = 0; i < v.size(); ++i) {
if (v[i]%2==1) {
doubles.push_back(v[i]*2);
}
}
Hopefully by now, you understand the advantages and drawbacks of each method. Happy studies!
1 How much larger depends on the implementation. Generally, implementations choose to allocate a new buffer of twice the size of the current buffer.
2 This is a small lie. The whole story is a bit more complicated: It actually tries to call begin(v) and end(v). Because vector is in the std namespace, it ends up calling std::begin and std::end, which, in turn, call v.begin() and v.end(). All of this machinery is there to ensure that the range-based for works not only with standard containers, but also with anything with a proper implementation for begin and end. That includes, for instance, regular plain arrays.
Here is the quick code snippet using iterators to iterate the vector-
#include<iostream>
#include<iterator> // for iterators to include
#include<vector> // for vectors to include
using namespace std;
int main()
{
vector<int> ar = { 1, 2, 3, 4, 5 };
// Declaring iterator to a vector
vector<int>::iterator ptr;
// Displaying vector elements using begin() and end()
cout << "The vector elements are : ";
for (ptr = ar.begin(); ptr < ar.end(); ptr++)
cout << *ptr << " ";
return 0;
}
Article to read more - Iterate through a C++ Vector using a 'for' loop
.
Hope it will help.
Try this,
#include<iostream>
#include<vector>
int main()
{
std::vector<int> vec(5);
for(int i=0;i<10;i++)
{
if(i<vec.size())
vec[i]=i;
else
vec.push_back(i);
}
for(int i=0;i<vec.size();i++)
std::cout<<vec[i];
return 0;
}
Output:
0123456789
Process returned 0 (0x0) execution time : 0.328 s
Press any key to continue.

Order of inserting elements into a 2D Vector in c++

I was practicing with C++ vectors, and found a problem when I was inserting elements into a 2D vector. In the following example:
#include <iostream>
#include <vector>
void fillVector(std::vector<std::vector<int> > &vec) {
std::vector<int> list1;
vec.push_back(list1);
list1.push_back(1);
list1.push_back(2);
std::vector<int> list2;
list2.push_back(3);
list2.push_back(4);
vec.push_back(list2);
return;
}
int main() {
std::vector<std::vector<int> > vect;
fillVector(vect);
std::cout << "vect size: " << vect.size() << std::endl;
for(int i = 0; i < vect.size(); i++) {
std::cout << "vect in size: " << vect.at(i).size() << std::endl;
}
}
the size of the first inner-list is 0, and the size of the second inner-list is 2. The only difference between list1 and list2 is that list1 is first inserted into the vec 2D vector, before elements are inserted into it, while elements are first inserted into list2, before it is itself inserted into the 2D vector. After returning from the function, the elements inserted into list1 are not printed, and its size remains the same.
I also attempted the first method with pointers instead,
std::vector<int> *list3 = new std::vector<int>();
vec.push_back(*list3);
list3->push_back(5);
list3->push_back(6);
However, the size of list3 when read from the calling function is still 0.
I don't understand the difference between the two approaches. Why does the list have to appended after it's elements are inserted?
It almost seems like you are expecting python-like behavior? In any case, in C++ the distinction between references, pointers, and values is very important.
Your fillVector function has the right idea, as it takes a reference to a 2D vector std::vector<std::vector<int> > &vec - notice the &. However, when you create list1, and use push_back() right away
std::vector<int> list1;
vec.push_back(list1);
you are pushing the empty vector. push_back() will create a copy of this vector, which will be contained in vect (in main), and is a completely separate vector from list1.
At this point, if you want to access the vector already pushed, you can use back(), which returns a reference to the last element in the vector vec, that is, the last one pushed.
vec.back().push_back(1);
vec.back().push_back(2);
list2 you modify before pushing back, so when the copy is made, it is made of the already modified vector. Your attempt with list3 doens't really change things much, you dereference the pointer when you push_back() and a copy is made all the same. You could make vect be std::vector<std::vector<int>*>, but I'd strongly advice against it, as you have to do manual memory management - using new.
Note: While it's important for you to learn at some point, you should really try to avoid using pointers whenever possible, specially RAW pointers (look at smart pointers instead). std::vector, and all other std containers I know of, do their own memory management - they are sure to do it more efficiently than you, and BUG FREE.
I would suggest that you simply work on the last vector pushed, as such:
void fillVector(std::vector<std::vector<int> > &vec) {
vec.push_back(std::vector<int>());
vec.back().push_back(1);
vec.back().push_back(2);
vec.push_back(std::vector<int>());
vec.back().push_back(3);
vec.back().push_back(4);
return;
}
as you can see it's pretty much the same code repeated twice, so you can easily loop to get this or other results.
vector.push_back(var) makes a copy of var and inserts it into the vector. If you use push_back() on an empty list, it copies the empty list into the vector. Changing values in the list after this does not affect the copy that was inserted into the vector. This is because you are passing an actual object to push_back(), not a pointer to an object.
In the third example, you take a step in the right direction, but you de-reference the list before you pass it in, so push_back() makes a copy of what is at that address.
A simple solution to the problem is to always set your values before you insert the list into the vector.
If you wish to be able to change the values after the list is inserted, use vect.at(i).push_back(val) to add a value to the list at i.
You could also make the vector contain pointers to other vectors, rather than the vectors themselves:
void fillVector(std::vector<std::vector<int> *> &vec) {
std::vector<int> *list1 = new std::vector<int>(); //Remember to allocate memory since we're using pointers now
list1->push_back(1);
list1->push_back(2);
vec.push_back(list1); // Copy the pointer that is list1 into vec
std::vector<int> *list2 = new std::vector<int>();
vec.push_back(list2); // Copy the pointer that is list2 into vec
list2->push_back(3);
list2->push_back(4);
return;
}
int main() {
std::vector<std::vector<int> *> vect; // Vector of pointers to vectors
fillVector(vect);
std::cout << "vect size: " << vect.size() << std::endl;
for(int i = 0; i < vect.size(); i++) {
std::cout << "vect in size: " << vect.at(i)->size() << std::endl;
}
}
std::vector<std::vector<int> *> vec = new; // Vector of pointers
When you put something into a std::vector, the vector stores a copy. Manipulating the original will have no effect on the copy. If you put a pointer into a vector of pointers the vector still stores a copy of the pointer. Both the original and the copy in the vector point at the same memory, so you can manipulate the referenced data and see a change in the referenced data.
So...
std::vector<int> list1;
vec.push_back(list1);
list1.push_back(1);
list1.push_back(2);
puts a copy of the emptylist1 into vec. Then copies of 1 and 2 are placed into the original list1. The copy of list1 in vec is unaffected.
Writing this as
std::vector<int> list1;
vec.push_back(list1);
vec.back().push_back(1);
vec.back().push_back(2);
will correct this. As will a slightly cleaner version
vec.push_back(std::vector<int>());
vec.back().push_back(1);
vec.back().push_back(2);
as it doesn't have a waste list1 hanging around cluttering up the scope.
And
vec.push_back(std::vector<int>{1,2});
will simplify even further if your compiler supports C++11 or better.
On the other hand...
std::vector<int> list2;
list2.push_back(3);
list2.push_back(4);
vec.push_back(list2);
puts copies of 3 and 4 into list2 and then puts a copy of list2, complete with copies of the copies of 3 and 4.
Similar to above,
std::vector<int> list2{3,4};
vec.push_back(list2);
can reduce the workload.
Unfortunately your experiment with list3 fails because while list3 is a pointer vec does not hold a pointer, so list3 is dereferenced and the vector referenced is copied. No pointer to the data list3 references is stored, and vec contains an empty vector for the same reason as above.
std::vector<int> *list3 = new std::vector<int>();
vec.push_back(*list3);
list3->push_back(5);
list3->push_back(6);
A few notes on storing pointers in vectors
The vector only stores a copy of the pointer. The data pointed at must be scoped in such a way that it will not be destroyed before the vector is done with it. One solution is to dynamically allocate the storage.
(This applies to dynamically allocated pointers in general) If you dynamically allocate, sooner or later someone has to clean up the mess and delete those pointers. Look into storing smart pointers rather than raw pointers and not storing pointers at all.
Familiarize yourself with the Rule of Three. vector looks after itself, but if you have two copies of the vector and you remove and delete a pointer from only one of them, you're going to have some debugging to do.

Resizing std::vector without destroying elements

I am using all the time the same std::vector<int> in order to try to avoid allocating an deallocating all the time. In a few lines, my code is as follows:
std::vector<int> myVector;
myVector.reserve(4);
for (int i = 0; i < 100; ++i) {
fillVector(myVector);
//use of myVector
//....
myVector.resize(0);
}
In each for iteration, myVector will be filled with up to 4 elements. In order to make efficient code, I want to use always myVector. However, in myVector.resize() the elements in myVector are being destroyed. I understand that myVector.clear() will have the same effect.
I think if I could just overwrite the existing elements in myVector I could save some time. However I think the std::vector is not capable of doing this.
Is there any way of doing this? Does it make sense to create a home-grown implementation which overwrites elements ?
Your code is already valid (myVector.clear() has better style than myVector.resize(0) though).
'int destructor' does nothing.
So resize(0) just sets the size to 0, capacity is untouched.
Simply don't keep resizing myVector. Instead, initialise it with 4 elements (with std::vector<int> myVector(4)) and just assign to the elements instead (e.g. myVector[0] = 5).
However, if it's always going to be fixed size, then you might prefer to use a std::array<int, 4>.
Resizing a vector to 0 will not reduce its capacity and, since your element type is int, there are no destructors to run:
#include <iostream>
#include <vector>
int main() {
std::vector<int> v{1,2,3};
std::cout << v.capacity() << ' ';
v.resize(0);
std::cout << v.capacity() << '\n';
}
// Output: 3 3
Therefore, your code already performs mostly optimally; the only further optimisation you could make would be to avoid the resize entirely, thereby losing the internal "set size to 0" inside std::vector that likely comes down to an if statement and a data member value change.
std::vector is not a solution in this case. You don't want to resize/clear/(de)allocate all over again? Don't.
fillVector() fills 'vector' with number of elements known in each iteration.
Vector is internally represented as continuous block of memory of type T*.
You don't want to (de)allocate memory each time.
Ok. Use simple struct:
struct upTo4ElemVectorOfInts
{
int data[4];
size_t elems_num;
};
And modify fillVector() to save additional info:
void fillVector(upTo4ElemVectorOfInts& vec)
{
//fill vec.data with values
vec.elems_num = filled_num; //save how many values was filled in this iteration
}
Use it in the very same way:
upTo4ElemVectorOfInts myVector;
for (int i = 0; i < 100; ++i)
{
fillVector(myVector);
//use of myVector:
//- myVector.data contains data (it's equivalent of std::vector<>::data())
//- myVector.elems_num will tell you how many numbers you should care about
//nothing needs to be resized/cleared
}
Additional Note:
If you want more general solution (to operate on any type or size), you can, of course, use templates:
template <class T, size_t Size>
struct upToSizeElemVectorOfTs
{
T data[Size];
size_t elems_num;
};
and adjust fillVector() to accept template instead of known type.
This solution is probably the fastest one. You can think: "Hey, and if I want to fill up to 100 elements? 1000? 10000? What then? 10000-elem array will consume a lot of storage!".
It would consume anyway. Vector is resizing itself automatically and this reallocs are out of your control and thus can be very inefficient. If your array is reasonably small and you can predict max required size, always use fixed-size storage created on local stack. It's faster, more efficient and simpler. Of course this won't work for arrays of 1.000.000 elements (you would get Stack Overflow in this case).
In fact what you have at present is
for (int i = 0; i < 100; ++i) {
myVector.reserve(4);
//use of myVector
//....
myVector.resize(0);
}
I do not see any sense in that code.
Of course it would be better to use myVector.clear() instead of myVector.resize(0);
If you always overwrite exactly 4 elements of the vector inside the loop then you could use
std::vector<int> myVector( 4 );
instead of
std::vector<int> myVector;
myVector.reserve(4);
provided that function fillVector(myVector); uses the subscript operator to access these 4 elements of the vector instead of member function push_back
Otherwise use clear as it was early suggested.