Consider the following program. It creates a set of pointer-to-ints, and uses a custom indrect_less comparator that sorts the set by the value of the pointed-to integer. Once this is done, I then change the value of one of the pointed-to integers. Then, it can be seen the order of the set is no longer sorted (I suppose because the set doesn't know something got changed).
(Don't mind the C++0x loops, I'm running on VS2010)
#include <iostream>
#include <set>
using namespace std;
struct indirect_less {
bool operator()(int* l, int* r) const
{
return *l < *r;
}
};
int main()
{
set<int*, indirect_less> myset;
int* a = new int(5);
int* b = new int(6);
int* c = new int(7);
myset.insert(a);
myset.insert(b);
myset.insert(c);
cout << "Set contains: ";
// (outputs: 5 6 7)
for (auto i = myset.begin(), end = myset.end(); i != end; ++i)
{
cout << **i << " ";
}
cout << endl << "Modifying *a" << endl;
*a = 9; // point of interest
cout << "Set contains: ";
// (outputs: 9 6 7 - unsorted order)
for (auto i = myset.begin(), end = myset.end(); i != end; ++i)
{
cout << **i << " ";
}
cout << endl;
cin.get();
return 0;
}
1) Am I right that I am invoking undefined behavior? Is the entire state of myset invalid after the line *a = 9;?
2) Is the only correct way to do this to erase then re-insert a?
3) Is there any way, once *a = 9; has been run, to re-balance the set in to sorted order, with well-defined behavior?
Yes, std::set assumes elements are immutable. It's possible, if dangerous, to reorder it yourself after each change. I wouldn't recommend it, though: use another collection type.
1) Yes, set doesn't allow modification of it's elements.
2) Besides deleting the old value and inserting the new value, you could also replace the old set with a newly constructed one.
3) No
1) I don't know that the behavior is undefined. The additional twist in this example is that the elements of the set are not altered -- each element of the set is a pointer. Were you to print out the (pointer) elements of the set before and after executing the line '*a = 9", I believe you would find that the pointer values are in the same order before and after the assignment. What has changed is the value that one set element points to. This took place outside the auspices of the set, and so the set has no way of maintaining the order you desire.
2) A qualified "yes". This will force use of indirect_less() to order the elements of the set. Again, be aware that you are ordering the elements of the set, pointers, by the value of each dereferenced pointer. However, this strikes me as somewhat risky, for exactly the reason you describe.
From the legend "Set contains:" in the printed output, I presume that this example strives to form a set of integers. However, the set defined, i.e., "set" actually consists of pointers to integers, not the integers themselves. I believe this misalignment between the desired and actual collections is the underlying cause of the problem.
3) See 2).
Related
Hi I am just starting to learn cpp and I have two examples of getting the size of a vector in the for statements both seem to work but which is right and why? sizeof(vector) or vector.size()?
Thanks
Brian
void print_vector(vector<string> vector_to_print){
cout << "\nCars vector = ";
for(int i = 0; i < sizeof(vector_to_print); i++){
cout << vector_to_print[i];
(i < vector_to_print.size()-1) ? (cout << ", ") : (cout << endl); // ? the tenrary operator is an if statement (?) do one outcome (:) or the other
}
}
void print_vector(vector <string> vector_to_print){
cout << "\nVector contents = ";
for( int i = 0; i < (vector_to_print.size()); i++ ){
cout << vector_to_print[i];
(i < (vector_to_print.size()-1)) ? (cout << ", ") : (cout << endl);
}
}
Both seem to work I try to rewrite the same code from memory each day for a week to help me learn it and I couldn't quite get the sizeof() to work so I googled it and the example I found used .size() but when I got home and checked what I did yesterday I had used sizeof().
std::vector<std::string> is a container class, which stores an array of std::strings plus other values to assist with manipulation of the array and providing information about the state of the stored array. When you call sizeof(vector_to_print) you are simply getting the size of the container class not the amount of elements in the array that it is storing.
run the following code to prove it to yourself:
std::vector<std::string> vec{"hello","world"};
std::cout << sizeof(vec) << '\n';
vec.push_back("!");
std::cout << sizeof(vec);
the size of the underlying array is changing it goes from 2 elements to 3, but not only does the sizeof(vec) not change, it is always = to 24!
sizeof(vec) must be the same each time (24 bytes on my machine) because sizeof is a compile time constant, because the size of a type must be a compile time constant.
The latter (.size()) is the only valid method
As you already know there are classes in C++. One class can have many methods doing different things and many fields holding different things. When we create an object of class Vector this object has method .size() which as mentioned here returns the number of elements in our object. The complexity is Constant and there is no problem using .size().
When you are using sizeof() as mentioned here on our object you know the size of the object with all its fields (for example vector can have size_t field where it counts all the elements and some field which holds them) you don't need the actual size of the vector in order to iterate the elements this could be wrong in many cases.
P.S. You must use .size()!
I have the following main program that creates a Stack object, fills it with integers and then pops them. The code files fine, but the pop_back() part does not seem to work, even after pop_back() it prints all values. How is this possible?
#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<int> myVector; //initalization
int value;
//input in a vector using push_back
for (int i = 0; i < 6;i++){
cin >> value;
myVector.push_back(value);
}
cout<<"Initial vector size:" << myVector.size() <<endl;
for (int i = 0; i < 6;i++){
cout << myVector[i];
}
cout << endl;
myVector.pop_back();
cout<<"Vector size after pop back: " << myVector.size() << endl;
cout << endl;
cout << "First element is: " << myVector.front() << endl;
cout << "Last element is : " << myVector.back() << endl;
for (int i = 0; i < 6;i++){
cout << myVector[i];
}
return 0;
}
Everyone has focused on saying this is undefined behavior fix code, but question was why it works.
To understand why it works you must understand how vector works more or less.
Vector allocates some block of memory to store items.
size of this block is defined by capacity, which says how many items can be stored in vector, without allocating new block of memory
capacity is usually bigger then size of the vector
now when you remove items form vector, capacity and allocated block of memory doesn't change. This is an optimization.
when you remove item from back just destructor is called (for int it does nothing) and size of vector is reduced.
your value is not cleared just marked as out of vector size
so when you use operator[] it doesn't check if you exceed its size. It just returns value at specific adders
since pop_back just reduced size you value is still there
Note if you call shrink_to_fit after pop_back there is a great chance it will and with crash or you will not receive same value. Still this is undefined behavior and anything can happen.
Another way to see your code is bad is to use at which checks if index is in valid range.
std::vector::pop_back function works just fine. After you perform a call to it, you try to print all 6 values instead of 5. Therefore, you are accessing invalid memory. In your case, program prints out the value that was removed but in some other case it could print some garbage value. That's why this is UB - Undefined Behavior.
Try the following and you will see that last element is not in the std::vector:
for (int i = 0; i < myVector.size(); i++) {
std::cout << myVector[i];
}
or, even better, use range-based for loop:
for (auto const i : myVector) {
std::cout << i;
}
The problem is in the way you loop through the vector - you are expecting it to have 6 elements even after you have removed the last element. This is undefined behavior.
Using a range based for would be preferred in both cases where you want to output the contents of the vector:
for (auto i:myVector) {
cout << i;
}
I wanted to distinguish between the end of an array an the rest of the elements (in a for loop), but most examples initializes a variable outside the loop, which I think clutters the loop. The shortest example I have achieved is by looking at pointer addresses in a ranged-based for-loop:
for(auto& x : arr){
cout << x;
if(&x != &*end(arr)-1)
cout << ", ";
}
This doesn't need an extra variable, but I am not 100% sure of the implications from using pointers in C++.
A more (or less?) readable example where I initialize a variable in the for-statement, in a way that looks quite intuitive (edit doesn't give portability to fuctions):
for(int i{0}, len{sizeof(arr)/sizeof(*arr)}; i<l; i++){
cout << arr[i];
if(i!=len-1)
cout << ", ";
}
Is there a more readable/better/shorter way to do this without extra includes?
Are there any cons to these approaches?
Why not do the following?
bool not_first_item = false;
for(auto x : arr){
if (not_first_item) {
cout << ", ";
not_first_item = true;
}
cout << x;
}
It will print a comma before each item except the first one. It will get the result you require without the need of using complicated pointers.
If all you have is a pointer to an element in an array, there is no portable way of detecting the position of that element in an array.
Alternatives; best first:
Use a std::vector. That has similar semantics as a plain old array and has the benefit of carrying in the size.
Pass the size of the array as an additional parameter, with size_t type.
Use a magic value to signify the end of an array.
Note that using &x is pointless as x is a value copy. Consider auto& x instead?
This might help
l=sizeof(arr)
for(int i{0}; i<l-1; i++){
cout << arr[i];
cout << ", ";
}
cout << arr[sizeof(arr)];
or
for(int i{0}; i<sizeof(arr)-1; i++){
cout << arr[i];
cout << ", ";
}
cout << arr[sizeof(arr)];
there is no extra condition.
If showing is the main intention
A concise way that I like, without extra branch:
const char* sep = "";
for (const auto& x : arr) {
std::cout << sep << x;
sep = ", ";
}
Yes it uses extra variable.
As a general rule, you should not discard information that you need. When you use a range-based for loop you abstract away the position of an element in the container (and in fact the form of the container). Therefore, it is not the most appropriate tool for the job. You could do this with an index or an iterator because those hold enough information for you to tell whether the element you are iterating over is the last one.
The standard library offers two helpful functions begin and end that either call the member functions begin and end of STL containers, or pointers to the first and past-the-end elements for C-style arrays. Because of the way the end condition is checked, you don't need anything more than a forward iterator.
assert(std::begin(arr) != std::end(arr));
for (auto it = std::begin(arr); it + 1 != std::end(arr); ++it) {
std::cout << *it << ", ";
}
std::cout << *(std::end(arr) - 1) << '\n';
The above code is fine if you know that you're never going to attempt to print an empty container. Otherwise, you'll need an extra if statement to check for that. Note that even if you have a random access iterator and you use the condition it < std::end(arr) - 1 you might reason that it's fine even for empty arrays, but it is undefined behavior and it might lead to some unexpected bugs when optimizations are turned on.
Not 100% sure what I was looking for but I think that the real answer might be to check for the starting item like #ed_heel proposed, but I really only needed to change what I checked for in the range-based for-loop to make it much neater:
for(auto &x : arr)
{
cout << (&x == arr ? "" : ", ") << x;
}
works since arr is an array (a.k.a. pointer in disguise).
I have the following program, which is storing four strings in map and printing first time. Now its running the another time to retrieve the stored values. But the second resutls are not same as first time results.
#include <map>
using namespace std;
void fun_call(void **,char * );
main(){
void *data=NULL;
char value[100];
int i=0,j=0;
char key[][10]={"disk1","disk2","disk3","disk4"};
cout << "printing all mapped values " << endl ;
data = (void *) malloc( 100);
for(j=0;j<2;j++){
for(i=0;i<4;i++){
fun_call(&data,key[i]);
memcpy(value,data,100);
cout << "key ="<<key[i]<<" value is " << value << endl;
}
cout <<"====================="<< endl;
}
}
void fun_call(void **tmp,char name[10])
{
void *tmp_data;
char str[100]="ravindra";
int len =0;
static std::map<std::string,void *> name_data_map;
std::map<std::string,void *>::iterator iter ;
iter=name_data_map.find(name) ;
if ( iter == name_data_map.end())
{
len=strlen(str)+strlen(name)+1;
tmp_data = (void *) malloc ( len );
strcat(str,name);
memcpy(tmp_data,str,len);
name_data_map[name]=tmp_data;
cout << "Inside the if" << endl ;
}
else
cout << "disk pos "<< iter->first << endl;
cout << "Outside the if" << endl ;
iter=name_data_map.find(name) ;
memcpy(*tmp,iter->second,len);
}
Output:
$ ./a.out
printing all mapped values
Inside the if
Outside the if
key =disk1 value is ravindradisk1
Inside the if
Outside the if
key =disk2 value is ravindradisk2
Inside the if
Outside the if
key =disk3 value is ravindradisk3
Inside the if
Outside the if
key =disk4 value is ravindradisk4
=====================
disk pos disk1
Outside the if
key =disk1 value is ravindradisk4
disk pos disk2
Outside the if
key =disk2 value is ravindradisk4
disk pos disk3
Outside the if
key =disk3 value is ravindradisk4
disk pos disk4
Outside the if
key =disk4 value is ravindradisk4
any idea why the second iteration is giving all data as : "ravindradisk4"
len is set to 0 in the beginning of fun_call, so if in the second run it doesn't go into your if, memcpy copies 0 bytes in the end. So the last value in main() from the first iteration remains the same regardless of key.
There are a lot of things wrong with your code, if it is intended to be a valid (or remotely idiomatic) C++ program.
As #starbugs points out, you're not using the right length the second time through to copy your result out. The one-line "fix" would be to change:
memcpy(*tmp,iter->second,len);
...to:
memcpy(*tmp,iter->second,strlen((char*)iter->second)+1);
For some basics on why brittle C string techniques are best replaced with C++ methodology, I like to show people this:
Learning Standard C++ As A New Language (PDF) by Bjarne
Once you've grasped that you might be more able to embrace the spirit in which C++ and the standard library should be used.
Your program is so trivial that it's easy to show how it can be simplified to produce idiomatic code which is far more robust and easy to read:
#include <map>
#include <iostream>
#include <string>
using namespace std;
string fun_call(string name)
{
static map<string,string> name_data_map;
map<string,string>::iterator iter;
iter = name_data_map.find(name);
if (iter == name_data_map.end()) {
string mapvalue = "ravindra";
mapvalue += name;
name_data_map[name] = mapvalue;
cout << "Inside the if" << endl ;
}
else
cout << "disk pos "<< iter->first << endl;
cout << "Outside the if" << endl;
iter = name_data_map.find(name) ;
return iter->second;
}
int main() {
string keys[] = {"disk1","disk2","disk3","disk4"};
cout << "printing all mapped values " << endl ;
for(int j = 0; j < 2; j++) {
for(int i = 0; i < 4; i++){
string value = fun_call(keys[i]);
cout << "key =" << keys[i] <<" value is " << value << endl;
}
cout << "=====================" << endl;
}
}
I'll stop there at providing a basically equivalent program with the same output and control flow.
Notes:
In standard C++, main must have an int as the return type (though it doesn't need arguments or a return statement, oddly enough)
The using namespace std; line frees you from having to type std:: in front of things in front of standard library classes like string, map, and their iterators. But don't put that in header files because it can cause problems with other source files that include them and have their own definitions which might conflict with the standard names when not disambiguated.
If you use the standard library then value types do their memory management under the hood, and the memory they use is allocated inside the class and freed in the destructor. Should you ever need to do explicit memory management then use new and delete.
First off, in general, in C++ consider using new/delete instead of malloc()/free().
I am not sure what you are exactly trying to accomplish (i.e. why you continuously copy values) but you have no length set so memcpy() doesn't copy anything.
Another simple fix to this issue is to use the pointer stored in iter->second (note that you would be able to modify data then and update that map entry - so perhaps this is not what you want).
For instance, do not allocate memory for your data variable in main and simply change this line
memcpy(*tmp, iter->second, len);
to
*tmp = iter->second;
Now the pointer address of data in main is set to the pointer address stored in the map.
First of all, I'm not even sure how your code compiles. Your main function lacks a return type and void/no-return is just bad practice. Restructure it to accomodate a simple return of 0 and make its return type int.
Furthermore, several includes are lacking before it even compiles (namely, iostream and string). Instead of using using namespace std, try to "pull" only the things you need from the std namespace. Bringing it all in is a potential hazard and bad practice in general, because you might encounter naming convention collisions in the future (and that will bring forth lots of headaches).
Back to the issue at hand. You're, if you're not experimenting/punishing your mind, applying some very bad practices here. This much memory-copying and pointer shifting around I don't get to do even while I'm working with moving vertex buffers around. And match your allocations with deallocations, that's some very bad memory management. And in C++, we use new/delete.
Since you're passing in the address of the pointer to the data variable, you can simply modify data's pointer by using *tmp.
Since your name_data_map is static, it survives the loop. Therefore, the second data member of the iter is the actual pointer to the data object at hand. Simply change the last line of code of your second function:
*tmp = iter->second;
Anyways, that's my two cents... I don't even get what you're trying to do. Good luck!
How can I access elements from myVector like i would do with arrays ( for(i = 0; i < n; i++) cout << v[i] << " "; )
My code:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Month
{
public:
char *name;
int nr_days;
Month(char* c, int nr) : name(c), nr_days(nr){};
~Month() { /* free(name); */}
};
int main()
{
Month January("January", 31);
Month February("February", 28);
Month March("March", 31);
Month April("April", 30);
Month May("May", 31);
Month June("June", 30);
Month July("July", 31);
Month August("August", 31);
Month September("September", 30);
Month Octomber("Octomber", 31);
Month November("November", 30);
Month December("December", 31);
vector<Month> *myVect = new vector<Month>;
myVect->push_back(January);
myVect->push_back(February);
myVect->push_back(March);
myVect->push_back(April);
myVect->push_back(May);
myVect->push_back(June);
myVect->push_back(July);
myVect->push_back(August);
myVect->push_back(September);
myVect->push_back(Octomber);
myVect->push_back(November);
myVect->push_back(December);
for(vector<Month>::const_iterator i = myVect->begin(); i != myVect->end(); i++)
{
/*
Month myMonth = i;
cout << myMonth.name << " " << myMonth.nr_days << endl;
*/
}
free(myVect);
return 0;
}
I would want to be something like a foreach algorithm: foreach(Month in myVect) cout << ...
And another question: why it gives me a run-time error at the destructor if I uncomment my line?
Ok, there are a lot of problems here.
You declare myVect as a pointer to a vector. This is unnecessary. One of the major benefits of using a vector is so that you don't have to worry about memory management as the vector does it for you. You stack allocate the vector, but internally it heap allocates the memory used to store the items it contains.
You never initialize the pointer. You are invoking undefined behavior as that pointer is not valid. To initialize a pointer you use new. All you have is an invalid stack allocated pointer that does not point to a vector on the heap. EDIT: I just realized that the new was edited out, so you can disregard this one. Still, it shouldn't be a pointer at all.
You are using free to deallocate a C++ class (that you never allocated to begin with...). Don't. This isn't C, you use new and delete to manage memory (when necessary!) in C++. free does not call destructors, it simply frees up a chunk of memory. delete on the other hand does as it knows how to deal with complex C++ types. Never mix new/delete with malloc/free.
myVect->begin() returns a const_iterator, not a T (i.e., in this case, not a Month object). Dereferencing the iterator via the * operator will yield the current iteration object, so:
Month myMonth = *i // <--- IMPORTANT!
As an aside, if you are going to be looping over the vector often you may want to typedef the iterator to reduce verbosity, i.e.,
typedef vector<Month>::const_iterator cmonth_iter;
Now you can write
for(cmonth_iter i = myVect.Begin(); i != myVect.end(); ++i )
{
Month m = *i;
// do stuff with m
}
You can access elements using iterator using the * operator:
for(vector<Month>::const_iterator i = myVect->begin(); i != myVect->end(); i++)
{
Month myMonth = *i;
cout << myMonth.name << " " << myMonth.nr_days << endl;
}
Also, you never allocate a vector in your code. You shouldn't use free() on a pointer you haven't received from malloc() earlier. It is undefined behavior to do otherwise and a run-time error is likely to occur at the point you call free().
Try this:
vector<Month> *myVect = new vector<Month>;
...
delete myVect;
If you remove the unitialized pointer bug by changing:
vector<Month> *myVect;
to:
vector<Month> myVect;
Then this will work. (Once you define ostream << Month)
for(i = 0; i < myVect.size(); i++)
cout << v[i] << " ";
You have a pointer myVect, but never assign a value to it before using (turn compiler warnings on). you should do something like myVect = new vector<Month>(). (or do not make it pointer and change -> into .). The rest of your "foreach" implementation looks fine. And you can use [] to access elements as well.
You free constant strings, you did not allocate them, so you need not to free them either.
You're declaring myVect as a pointer but never allocating it, that's going to give you lots of trouble. Just drop the * and you should be fine.
If you insist, you can use an index just like you would with an array:
for(int i = 0; i < myVect.size(); i++)
{
Month myMonth = myVect[i];
cout << myMonth.name << " " << myMonth.nr_days << endl;
}
Although I'd rather use iterators as you have done - just one simple fix:
Month myMonth = *i;
You can use the arrow operator with iterators...
for(vector<Month>::const_iterator i = myVect->begin(); i != myVect->end(); i++)
{
cout << i->name << " " << i->nr_days << endl;
}
note also that it's more idiomatic with iterators using ++i instead of i++ (the reason is that i++ will need to create a copy of the iterator that will be thrown away).
Note also that your code is UB (undefined behavior) because you are using a pointer to a vector, but you are not allocating it. By the way the use of a pointer in this case is nonsense, the code would be correct and simpler with:
vector<Month> myVect;
myVect.push_back(January);
myVect.push_back(February);
...
for(vector<Month>::const_iterator i = myVect.begin(); i != myVect->end(); ++i)
...
My suggestion is also to avoid to try learning C++ just by experimenting with a compiler (something that I've the impression you're trying to do).
C++ is powerful but also complex and unfortunately quite illogical and asymmetrical in many parts (due to its evolution history). Add to this that when you make a mistake (e.g. not allocating the vector in your original code) you cannot expect the compiler to help you and even at runtime the program may do ANYTHING, including apparently work as you expected (the worst possible thing). This combo is deadly.
Complexity, asymmetry and lack of runtime checks all make C++ impossible to learn by experimentation... just get a good book and read it. It's much simpler this way.