I have a list of Slide objects. Each Slide object has a vector of slide elements, the slide element vector is of another class,say ItemProperties that has members
int Resize
int Rotate
int Move
and the functions to modify these values
void ResizeItem(int resize_val);
void RotateItem(int rotate_val);
void MoveItem(int move_val);
I am iterating the list of object like this
"Iterating Part"
auto slidelist_ptr=VDECK.getSlideList(); /*returns list<Slide> by reference */
auto slidelist_itr=slidelist_ptr.begin(); /*returns iterator to beginning of Slide list*/
auto slidelemnts_itr=slidelist_itr->getSlideElemnt().begin(); /*returns iterator to beginning of slide list elements*/
in1=_getch();
if(in1!=KEY_ARROW_1)
{
**VDECK.selectedSlideOperations((*slidelist_itr),in1-48);**
throw std::runtime_error("Operation success\n");
}
++slidelist_itr;
slidelemnts_itr=slidelist_itr->getSlideElemnt().begin();
In the function selectedSlideOperations , I am passing the particular slide object and taking it as reference in function like this
void Deck::selectedSlideOperations(Slide &obj,int choice)
{
switch(choice)
{
case 5:
ChangeSlideItemProperties(obj);
break;
}
}
In ChangeSlideItemProperties , I am again taking the object as reference
void Deck::ChangeSlideItemProperties(Slide &obj)
{
for(auto p=obj.getSlideElemnt().begin();p!=obj.getSlideElemnt().end();p++)
{
std::cout<<"before \n";
p->PrintItemProperties();
p->RotateItem(input);
std::cout<<"\nafter \n";
p->PrintItemProperties();
}
}
Inside this I am iterating the Slide object's slide elements(which is a vector) and trying to modify the value of member "Rotate" for all the elements in vector.
After modification when I print "before" and "after" the members ,the value reflects properly for member Rotate.
However after modification ,when I exit this menu and print the slide elements using iteration as shown in "Iterating part" , the values are 0's for all the members.
I am fairly new to C++ , please bear with me.... I am not sure what am I missing here.
Edit: Sorry I changed the slide elements to list instead of vector , so I have lists only and no vectors
Edit 2: Adding minimal reproducible example
using namespace std;
class Itemprop
{
int Rotate;
public:
Itemprop():Rotate{0}{}
void Set_Rotate(int val)
{
Rotate = val;
}
int Get_Rotate()const
{
return Rotate;
}
};
class Dummy
{
public:
Dummy();
list<Itemprop>& getList(void)
{
return v;
}
virtual ~Dummy();
protected:
private:
list<Itemprop> v;
};
int main()
{
Dummy dobj;
Itemprop iobj;
iobj.Set_Rotate(1);
dobj.getList().emplace_back(iobj);
Itemprop iobj2;
iobj2.Set_Rotate(2);
dobj.getList().emplace_back(iobj2);
for(auto p:dobj.getList())
{
cout<<"before\n";
cout<<p.Get_Rotate()<<endl;
p.Set_Rotate(3);
cout<<"after\n";
cout<<p.Get_Rotate()<<endl;
}
for(auto p:dobj.getList())
{
cout<<p.Get_Rotate()<<"<--new\n";
}
Output is
before
1
after
3
before
2
after
3
1<--new
2<--new
Because your range loop that uses auto keyword is taking the elements of your list (the list itself is taken by reference) by copy.
Quick fix is to take the elements by reference as well, e.g.:
for(auto& p:dobj.getList())
Or you can use the std::list iterators, like in the previous code you posted:
for(auto it = dobj.getList().begin(); it != dobj.getList().end(); it++)
{
cout<<"before\n";
cout<<it->Get_Rotate()<<endl;
it->Set_Rotate(3);
cout<<"after\n";
cout<<it->Get_Rotate()<<endl;
}
Related
I'm trying to delete an item from a vector with erase() function but I keep getting an error. I
searched everywhere but can't find an answer
#include <iostream>
#include <vector>
#include <map>
#include <iterator>
#include <algorithm>
using namespace std;
class Person{
private:
string name;
public:
void set_name(string name){
this->name = name;
}
string get_name(){
return name;
}
class Record{
private:
vector <Person> book;
public:
void delete_person(string name){
for(Person p : book){
if(book.get_name() == name){
book.erase(p);
}
}
}
};
int main(){
// nothing in main yet
return 0;
}
I get en error in the delete_person() function in the record class: No matching member function for call to 'erase'
void delete_person(string name){
for(Person p : book){
if(book.get_name() == name){
book.erase(p);
}
}
}
fails for several reasons.
std::vector::erase does not accept items, it accepts iterators, locations of items to be removed.
Range-based for loops are very simple and limited in their abilities. They go from start to finish and are extremely intolerant of changes to the container while iterating. If you add or remove an item while iterating it, the hidden bookkeeping used by the loop becomes invalid and the loop breaks. And not the nice break sort of breaking. They tend to take the whole program down with them.
In Person p : book p is a new object that is a copy of an item in book. It's not the original or a reference to the original in the container. C++ defaults to values instead of references in almost every case. Unless you specifically request otherwise, you pass by value, return by value, and iterate by value.
Instead, employ the Erase-Remove Idiom. Here is an example with added commentary where I saw it fitting or educational.
#include <iostream>
#include <vector>
#include <map>
#include <iterator>
#include <algorithm>
using namespace std;
class Person
{
private:
string name;
public:
Person(const std::string & name) // added for testing
: name(name) // this is a member initializer list In C++ all class members
// and base classes must be initialized before the program can
// enter the body of the constructor. This trick allows us to
// initialize members rather than initializing them to their
// defaults (if the type has a default) and then setting them
// inside the body and wasting time doing two things where one
// thing was required
{
}
void set_name(string name) // side note consider saving construction of a new
// string and accepting name by const reference rather
// than by value and potentially making a copy.
// void set_name(const string & name)
// const because we do not intend to change `name`
// and because the compiler can take advantage of the
// promise not to change it in many interesting ways.
{
this->name = name;
}
string get_name() const // const because getters generally should not change the
// object this allows us to keep the class "const-correct"
// side note consider saving construction of a new
// string and returning by const reference rather than
// by value and making a copy.
// const string & get_name() const
{
return name;
}
};
class Record
{
private:
vector<Person> book;
public:
void add_person(const std::string & name) // added for testing
{
book.emplace_back(name);
}
void delete_person(string name) // again consider passing name by const reference
{
book.erase(std::remove_if(book.begin(), // from start of list
book.end(), // to the end
[name](const Person &p)
{
return p.get_name() == name;
}), // moves all items to be removed to the end of the
// list, then returns start of range to erase
book.end()); // erase to the end of the list
// Why erase separately? Because remove functions don't actually remove. They
// move the unwanted values to the end of the list. Looks silly, but much easier
// and safer to write. For example, this won't change the size of the list and
// break loops that count on the size to remain the same.
}
friend std::ostream & operator<<(std::ostream & out,
const Record & rec) // added for testing
{
for (const auto & item: rec.book) // print all items in book
// const because printing should not change
// the printed
// auto to let the compiler figure out the type
// & because we don't want to make a copy
{
out << item.get_name() << '\n';
}
return out;
}
};
int main()
{
Record r;
r.add_person("Bill");
r.add_person("Ted");
r.add_person("Rufus");
std::cout << r << std::endl;
r.delete_person("Ted");
std::cout << r << std::endl; // Ted should now be gone from the list
return 0;
}
Expected output:
Bill
Ted
Rufus
Bill
Rufus
book.erase(p);
book is a vector. The parameter to a vector's erase() method is an iterator.
for(Person p : book){
p is the value in the vector, and actually it is a copy of the value in the vector. You cannot pass a value to erase(). You must pass an iterator as a parameter. Passing some random copy of some random value in a vector to its erase() method is not going to accomplish anything useful.
std::vector has begin() and end() methods that return the iterator to the beginning and the end of a sequence that defines the contents of the vector.
This may be used with various algorithms, like std::find_if or std::remove_if, together with std::vector::erase to effect the removal of a value or multiple values from your vector.
I have two classes, each has a vector of pointers to Data. What I want to do is to assign pointers in the vector of the class Sample2 to pointers in the vector of the class Sample1.
The problem is that as I assign pointers in the second vector, the order in witch they are stored is that of the first vector. I would like to store them in the order of insertion.
Here is a minimal reproducible example of the code:
#include <iostream>
#include <vector>
using namespace std; //for sample purposes
// For simplicity, the data is just a string in this example.
using Data = string;
// In the real code there is a class with a certain vector as a member,
// but for this example we can reduce it to just the vector.
using Sample1 = vector<Data*>;
Class Sample2 — the problem is here
class Sample2 {
vector<Data*> autodromos2;
public:
vector<Data*>& getAutodromos() { return autodromos2; }
// ** This is the function with the problem. **
void addAutodromos2(vector<string>& arguments, vector<Data*>& autodromos)
{
for (Data* a : autodromos) {
for (string &s : arguments) {
if (s == *a) { // The real test is more complex.
getAutodromos().push_back(a);
break;
}
}
}
}
};
Main function (generate data and call addAutodromos2)
int main()
{
// Create the list of elements to add to a `Sample2`.
// Note that these are strings, not Data objects (in the real code).
vector<string> arguments { "fourth", "first", "third" };
// Create the `Sample1` data with which to work.
Sample1 s1 {
new Data("first"), new Data("second"), new Data("third"),
new Data("fourth"), new Data("fifth")
};
// Create the `Sample2` data from the list and `s1`.
Sample2 s2;
s2.addAutodromos2(arguments, s1);
// Diagnostic:
for (Data* a : s2.getAutodromos()) {
cout << *a << endl;
}
}
The output is
first
third
fourth
when it should be
fourth
first
third
Actually the sequence problem with loops in addAutodromos2() you need to change function with below code:
for (string s : arguments)
{
for (Data* a : autodromos)
{
if (s == *a) { // The real test is more complex.
getAutodromos().push_back(a);
break;
}
}
}
Switch the for-loops. output is fourth first third
Hope this will help.
There is a school of thought that says if you have nested loops inside a function, you probably are not thinking abstractly enough. While that might be an overstatement at times, it does have value in this situation. Let's look at the inner loop.
for (string s : arguments) {
if (s == *a) {
getAutodromos().push_back(a);
break;
}
}
This loop searches for *a in arguments and if found does something. The search is a concept that could be abstracted away into its own function, let's call it found, a function that returns a bool.
// Preliminary revision
void addAutodromos2(vector<string>& arguments, vector<Data*>& autodromos)
{
for (Data* a : autodromos) {
if ( found(arguments, *a) ) {
getAutodromos().push_back(a);
}
}
}
With only one loop to look at, it should be clearer what the problem is. Elements are added to getAutodromos() in the order they appear in autodromos. To use the order within arguments, you need to loop through it. (I'll change the name of the helper function to find_by_name and have it return either an iterator to the found element or the end iterator. A boolean return value is no longer adequate.)
// Final revision
void addAutodromos2(vector<string>& arguments, vector<Data*>& autodromos)
{
for (string s : arguments) {
auto result = find_by_name(autodromos, s);
if ( result != autodromos.end() ) {
getAutodromos().push_back(*result);
}
}
}
A missing piece here is the find_by_name function. The good news is that this task is so common, that functionality is part of the standard library, in the header <algorithm>. The bad news is that there is a bit of typing to use the library function, as the arguments are more complex (for greater flexibility). You may want to define a wrapper to specialize it to your case.
// Returns an iterator to the element with the indicated name, or
// autodromos.end() if not found.
static auto find_by_name(const vector<Data*> & autodromos, const string & name)
{
return std::find_if(autodromos.begin(), autodromos.end(), [&name](Data *a){
return name == *a; // or name == a->get_name(), when Data is more complex
});
}
Note that if the real test was as simple as comparing name == *a, then std::find could be used instead of std::find_if, and there would be no need to use a lambda.
Don't forget to #include <algorithm> earlier in the file.
so I have a struct bullet:
struct Bullet {
int posX; int posY;
int verticalBulLimiter = 0;
int verticalBulLimiterCheck = 35; // aka movement Speed
void draw() {
gotoxy(posX,posY);
setColor(14);
cout << bullet << endl;
if (posY - 1 == 0){
/// I want to delete struct here
}
if (verticalBulLimiter == verticalBulLimiterCheck) {
posY--;
clearPixel(posX, posY + 1);
verticalBulLimiter = 0;
}
else {
verticalBulLimiter++;
}
}
};
as You can see I want to delete the struct once it's variable posY reaches certain value.
So how do I do that. Do I tried using keyword "this" but I dont really know how does it work. Is there any function for 'self destruction' ?
While this can be possible using delete this, it is most definitely not what you want.
Objects in C++ do not usually own themselves, so you should have the owner of the bullet (maybe a scene or world entity) delete it.
Instead of trying to "destroy a struct from within", you should be checking and removing instances of struct that have met some conditions.
There's an algorithm that does just that, like so.
#include <vector>
struct Bullet { ... };
std::vector<Bullet> bullets;
void clean_bullets(){
bullets.erase(
std::remove_if(bullets.begin(), bullets.end(),
[](const Bullet& i){return /* your condition when a bullet should be removed */}),
v.end()
);
}
int main(){
// Add some bullets
bulltes.push_back({/* create a bullet here */});
.
.
.
clean_bullets();
}
The std::vector<Bullet> bullets contains all the bullets, and is responsible for memory, removing, adding elements etc.
You can read more about std::vector here: https://en.cppreference.com/w/cpp/container/vector
And about removing the elements like above, it's called the remove/erase idiom https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Erase-Remove
I am farily new to c++ and have already read some topics about storing pointers to objects or the objects themselves in a vector.
I decided to store the objects in the vector, because I do not push_back many objects at runtime, the vector is just created once and leaved like this.
My problem now is, that I have another object that gets a vector as argument and searches for a certain object in the passed vector. If it finds this object, it stores a pointer to it, if not, the variable is set to NULL.
Eventhough I do not push_back any items, the pointer seems to point to a wrong location in other functions.
The object that searches for the element in the vector has a public function in which the pointer should be returned. It would be very slow if I search for the object at every function call, so this should not be an option.
Are there other solutions or do I have to switch to a vector of pointers?
Some code snippets:
Constructor of the object that searches the vector:
MySearch::MySearch(QVector<Obj> objVector)
:objVector(objVector) {
found = NULL
foreach(Obj o, this->objVector) {
if(..found..) {
found = &o;
break;
}
}
}
Getter function:
Obj* MySearch::getObject() {
return found;
}
The problem is because the variable o is local and will be out of scope as soon as the loop ends. If you take the address of the vector element instead of the o, it will works.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class MySearch
{
public:
MySearch(const vector<string> &items)
: items_(items)
{
// Skipping validation
found_ = &(items_[5]);
}
string *getObject() {return found_;}
private:
vector<string> items_;
string *found_;
};
int main()
{
string str = "test#";
vector<string> aux;
for (int i = 0; i < 10; ++i)
aux.push_back(str + (char)('0' + i)); // test#0 ... test#9
MySearch ms(aux);
cout << *(ms.getObject()) << endl; // test#5
return 0;
}
foreach(Obj o, this->objVector) {
if(..found..) {
found = &o;
break;
}
} // life time of o ends here.
o resides on stack and it's life-time is limited to the loop only. Having reference to it and later returning causes undefined behavior.
If you were to use BOOST_FOREACH (from the boost C++ libraries), then you could use a non-const reference to the objects in the vector. Q_FOREACH does not support non-const references:
BOOST_FOREACH(Obj& o, this->objVector) {
if(..found..) {
found = &o;
break;
}
}
Alternatively use iterators and a for loop.
How do I get the position of an element inside a vector, where the elements are classes. Is there a way of doing this?
Example code:
class Object
{
public:
void Destroy()
{
// run some code to get remove self from vector
}
}
In main.cpp:
std::vector<Object> objects;
objects.push_back( <some instances of Object> );
// Some more code pushing back some more stuff
int n = 20;
objects.at(n).Destroy(); // Assuming I pushed back 20 items or more
So I guess I want to be able to write a method or something which is a member of the class which will return the location of itself inside the vector... Is this possible?
EDIT:
Due to confusion, I should explain better.
void Destroy(std::vector<Object>& container){
container.erase( ?...? );
}
The problem is, how can I find the number to do the erasing...? Apparently this isn't possible... I thought it might not be...
You can use std::find to find elements in vector (providing you implement a comparison operator (==) for Object. However, 2 big concerns:
If you need to find elements in a container then you will ger much better performance with using an ordered container such as std::map or std::set (find operations in O(log(N)) vs O(N)
Object should not be the one responsible of removing itself from the container. Object shouldn't know or be concerned with where it is, as that breaks encapsulation. Instead, the owner of the container should concern itself ith such tasks.
The object can erase itself thusly:
void Destroy(std::vector<Object>& container);
{
container.erase(container.begin() + (this - &container[0]));
}
This will work as you expect, but it strikes me as exceptionally bad design. Members should not have knowledge of their containers. They should exist (from their own perspective) in an unidentifiable limbo. Creation and destruction should be left to their creator.
Objects in a vector don't automatically know where they are in the vector.
You could supply each object with that information, but much easier: remove the object from the vector. Its destructor is then run automatically.
Then the objects can be used also in other containers.
Example:
#include <algorithm>
#include <iostream>
#include <vector>
class object_t
{
private:
int id_;
public:
int id() const { return id_; }
~object_t() {}
explicit object_t( int const id ): id_( id ) {}
};
int main()
{
using namespace std;
vector<object_t> objects;
for( int i = 0; i <= 33; ++i )
{
objects.emplace_back( i );
}
int const n = 20;
objects.erase( objects.begin() + n );
for( auto const& o : objects )
{
cout << o.id() << ' ';
}
cout << endl;
}
If you need to destroy the n'th item in a vector then the easiest way is to get an iterator from the beginning using std::begin() and call std::advance() to advance how ever many places you want, so something like:
std::vector<Object> objects;
const size_t n = 20;
auto erase_iter = std::advance(std::begin(objects), n);
objects.erase(erase_iter);
If you want to find the index of an item in a vector then use std::find to get the iterator and call std::distance from the beginning.
So something like:
Object object_to_find;
std::vector<Object> objects;
auto object_iter = std::find(std::begin(objects), std::end(objects), object_to_find);
const size_t n = std::distance(std::begin(objects), object_iter);
This does mean that you need to implement an equality operator for your object. Or you could try something like:
auto object_iter = std::find(std::begin(objects), std::end(objects),
[&object_to_find](const Object& object) -> bool { return &object_to_find == &object; });
Although for this to work the object_to_find needs to be the one from the actual list as it is just comparing addresses.