In the following code, I print the first element of a vector and its size, before and after calling clear( ) method of the in-built vector.
However, even after calling clear( ), I can still refer to the element of the vector( although the size is reset to '0').
Is this expected behaviour or have I not understood the behaviour of clear( ) well?
CODE:
#include <vector>
#include <iostream>
using namespace std;
int main(int argc, char *argv[]){
vector<int> vec = {1, 2, 3, 4, 5};
cout << vec[0] << " " << vec.size() << endl;
vec.clear();
for(auto i : vec)
cout << i << " ";
cout << endl;
cout << vec[0] << " " << vec.size() << endl;
return 0;
}
OUTPUT:
1 5
1 0
The indexing operator of std::vector<> does not check the index. Since it's Undefined Behavior, you might get "what was there before", or it might crash (or "whatever").
You have the at member function, which will throw if index is out of range. That is:
cout << vec.at(0) << " " << vec.size() << endl;
near the end of your main() will throw.
Related
How to clear content in a simple way?
If I use vec_vec.clear(); only, there is still something in the vector that has not been cleaned up.
#include <iostream>
#include <vector>
int main()
{
std::vector<std::vector<int>> vec_vec(10);
vec_vec[0].push_back(1);
vec_vec[0].push_back(2);
vec_vec[0].push_back(3);
vec_vec[0].push_back(4);
for (auto i : vec_vec[0])
std::cout << i << " ";
std::cout << "." << std::endl;
vec_vec.clear();
for (auto i : vec_vec[0])
std::cout << i << " ";
std::cout << "." << std::endl;
vec_vec[0].clear();
for (auto i : vec_vec[0])
std::cout << i << " ";
std::cout << "." << std::endl;
for (int i=0; i<vec_vec.size(); i++)
vec_vec.erase(vec_vec.begin() + i);
for (auto i : vec_vec[0])
std::cout << i << " ";
std::cout << "." << std::endl;
return 0;
}
1 2 3 4 *
0 0 3 4 *
*
*
vec_vec.clear();
for (auto i : vec_vec[0])
After this clear, vec_vec is empty, so the expression vec_vec[0] has undefined behavior.
Undefined behavior means anything at all might happen, and it's the fault of the program, not the fault of the C++ compiler, library, etc. So it might act like an empty vector, it might crash your program, it might print some values, or it might do what you expect today, then break at the worst possible time later on.
See also this Q&A on Undefined, unspecified, and implementation-defined behavior.
I have used a decent amount of C++, but not so much std::list ..
In my current project I need a std::list<..> data member, as well as keep track to a position in the list with a std::list<..>::iterator. The object must also be movable, but a default move constructor is not possible in my case. Here std::list does something that surprises me.
Consider
#include <list>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
template<typename T>
void test() {
T l { 1, 2, 3, 4, 5 };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
auto pos = find(l.begin(), l.end(), 6);
if (pos == l.end()) cout << "l end\n";
cout << "---- moving l > lmv ----" << endl;
T lmv { std::move(l) };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
cout << "lmv = "; for(const auto& e: lmv) cout << e << " "; cout << endl;
if (pos == l.end()) cout << "l end\n";
if (pos == lmv.end()) cout << "lmv end\n";
}
int main() {
cout << "___vector___\n";
test<vector<int>>();
cout << "___list___\n";
test<list<int>>();
}
This outputs
___vector___
l = 1 2 3 4 5
l end
---- moving l > lmv ----
l =
lmv = 1 2 3 4 5
lmv end
___list___
l = 1 2 3 4 5
l end
---- moving l > lmv ----
l =
lmv = 1 2 3 4 5
l end
I.e. the iterator that pointed to the moved-from lists end, does not point to the moved-to lists end.
But it does for vector, which is what I would always expect, if iterators are essentially pointers. Why is list different? Memory location of elements should not change with move .. does lists move change list iterators? Why?
I am using "g++.exe (Rev1, Built by MSYS2 project) 10.2.0"
under MSYS2 on Windows 10
Iterators should be preserved when moving a container.
However end iterators of a container don't point to an element and are therefore allowed to be invalidated when moving a container.
If you change your code to work with begin rather than end then it works as you expect.
#include <list>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
template<typename T>
void test() {
T l { 1, 2, 3, 4, 5 };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
auto pos = find(l.begin(), l.end(), 1);
if (pos == l.begin()) cout << "l begin\n";
cout << "---- moving l > lmv ----" << endl;
T lmv { std::move(l) };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
cout << "lmv = "; for(const auto& e: lmv) cout << e << " "; cout << endl;
if (pos == l.begin()) cout << "l begin\n";
if (pos == lmv.begin()) cout << "lmv begin\n";
}
int main() {
cout << "___vector___\n";
test<vector<int>>();
cout << "___list___\n";
test<list<int>>();
}
Note that comparing the iterators from two different containers is undefined behaviour so the final pos == l.begin() is undefined behaviour and visual studio's debug builds at least will throw assertions when running this code.
I imagine your original code works because the std::vector end iterator is usually just implemented as pointing to one after the last element. I would imagine the std::list end iterator holds a null pointer and a pointer to the list.
If you add such horrible lines at the end of your test function
(this is totally incorrect, the sanitizer will insult you!),
you can see that in the case of a vector the end() iterator
designates something which is past-the-end of the buffer containing
the stored elements, but in the case of a list the end iterator
designates some kind of marker which is stored inside the list
structure itself.
Then, after moving, the buffer of the vector is still the same
but it does not belong to l anymore, so the address past-the-end
of this buffer is equivalent to end() for lmv.
On the other hand, after moving the list, pos which designated
an address inside l still designated the same address (although
l is moved from) but does not designate the end() marker inside
lvm which didn't even exist when pos was initialised.
std::cout << "pos: " << (void *)(&*pos) << '\n';
std::cout << "l: " << (void *)(&l) << '\n';
std::cout << "l.begin(): " << (void *)(&*l.begin()) << '\n';
std::cout << "l.end(): " << (void *)(&*l.end()) << '\n';
std::cout << "lmv: " << (void *)(&lmv) << '\n';
std::cout << "lmv.begin(): " << (void *)(&*lmv.begin()) << '\n';
std::cout << "lmv.end(): " << (void *)(&*lmv.end()) << '\n';
Given the following code:
#include <vector>
#include <iostream>
struct number {
int n{666};
};
int main()
{
std::vector<number> vec;
std::cerr << vec.size() << std::endl;
number n;
vec.push_back(n);
std::cerr << vec.size() << std::endl;
auto b = std::move(vec.front());
std::cerr << "b: " << b.n << std::endl;
std::cerr << vec.size() << std::endl;
}
I get the following output:
0
1
b: 666
1
Shouldn't the last 1 be 0?
std::move doesn't even know that the thing it moved was in a container. The block of memory that the vector owns is still there, just in an unspecified state. It's up to you to manage the vector.
Once you have used the value at the front and you want to get rid of it you will need to erase it from the vector. I won't talk about the front() returning a ref as 0x5453 mentioned that. But there is no reason to use std::move there - all that does is cast the value to a rvalue reference it does not actually "move" anything on its own.
#include <vector>
#include <iostream>
struct number {
int n{666};
};
int main()
{
std::vector<number> vec;
std::cerr << vec.size() << std::endl;
number n;
vec.push_back(n);
std::cerr << vec.size() << std::endl;
// Since this is a ref, and your struct is simple - just copy
auto b {vec.front()};
// Now remove the element
vec.erase(vec.begin());
std::cerr << "b: " << b.n << std::endl;
std::cerr << vec.size() << std::endl;
}
In this sample program, why the value of the data pointed by the iterator is kept, even after the list is empty?
Is it something bound to happen due to the implementation of iterators in C++ (i.e. the value of the object is kept into the iterator) or is it because the segment of the memory was declared as free for used, but hasn't been changed yet?
#include <iostream>
#include <list>
using namespace std;
int main ()
{
list<int> mylist;
list<int>::iterator it = mylist.begin();
cout << "printing: " << *it << endl;
mylist.push_back(77);
mylist.push_back(22);
// now front equals 77, and back 22
mylist.front() -= mylist.back();
it = mylist.begin();
cout << "printing: " << *it << endl;
cout << "mylist.front() is now " << mylist.front() << '\n';
// trying to delete all elements and then see how the iterators are handling it
it = mylist.begin();
cout << "printing: " << *it << endl;
mylist.remove(55);
cout << "printing: " << *it << endl;
mylist.remove(22);
cout << "printing: " << *it << endl;
cout << "mylist size: " << mylist.size() << endl;
cout << "mylist.front() is now " << mylist.front() << '\n';
cout << "printing: " << *it << endl;
return 0;
}
And this is the output:
printing: 495034304
printing: 55
mylist.front() is now 55
printing: 55
printing: 55
printing: 55
mylist size: 0
mylist.front() is now 38375440
printing: 55
Is it something bound to happen due to the implementation of iterators in C++
No, it's undefined behaviour. The iterator has become invalid, and can't be used.
is it because the segment of the memory was declared as free for used, but hasn't been changed yet?
Yes, that's why you observed what you observed. But the memory could be reused for something else, or made inaccessible - you can't rely on any observations of undefined behaviour.
Iterator is invalidated by you operations, but it may still point to memory with the previous value. Anyway, accessing it after the value is removed from the list is undefined behaviour.
#include <stdio.h>
int main(int argc, char **argv)
{
int *p = NULL;
p = (int*)malloc(sizeof(int));
*p = 5;
printf("P: %d\n", *p);
free(p);
printf("P: %d\n", *p);
}
Why is this still a surprise? Marking a pointer as invalid has nothing to do with what is stored where it used to point.
I'm trying to add a string to the middle of a vector, but I don't want to lose the data that is being replaced. I want everything below that element to shift down one. Is that possible?
Here is what I have so far
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<string> v;
v.push_back("Rich");
cout << v.back() << endl;
v.push_back("Debbie");
cout << v.back() << endl;
v.push_back("Robin");
cout << v.back() << endl;
v.push_back("Dustin");
cout << v.back() << endl;
v.push_back("Philip");
cout << v.back() << endl;
v.push_back("Jane");
cout << v.back() << endl;
v.push_back("Joseph");
cout << v.back() << endl;
cout << "Removing Joseph from the vector"<<endl;
v.pop_back();
cout << "Adding my name to the vector" << endl;
vector<string>::iterator pos = v.find(v.begin(),v.end(), "Robin");
if (pos != v.end())
{
++pos;
}
v.insert(pos, "Jimmy");
cout << "The vector now contains the names:";
for (unsigned i=0; i<v.size(); i++)
cout << " " << "\n" << v.at(i);
cout << "\n";
return 0;
}
I'm also getting an error on this find function. Any help would be appreciated.
Error 1 error C2039: 'find' : is not a member of 'std::vector<_Ty>' d:\pf3\lab3b\lab3b\3b.cpp 28
2 IntelliSense: class "std::vector<std::string, std::allocator<std::string>>" has no member "find" d:\pf3\lab3b\lab3b\3b.cpp 28
Like this:
#include <vector> // for std::vector
#include <algorithm> // for std::find
v.insert(std::find(v.begin(), v.end(), "Robin"), "Jimmy");
std::vector has no find function, use std::find instead:
vector<string>::iterator pos = std::find(v.begin(),v.end(), "Robin");
this operation is O(N) in vector. if you will use it frequently please use linked list.