strings and a store - c++

In the below program a string is added to an empty store. Then the address of this store element is stored in the pointer 's1'. Then another string is added and this somehow causes the pointer to the original element to fail.
#include <iostream>
#include <string>
#include <vector>
class store2
{
public:
void add(std::string s) {words.push_back(s); last_added2 = &words.at(words.size() - 1);}
std::string* last_added() {return last_added2;}
private:
std::string* last_added2;
std::vector<std::string> words;
};
void main()
{
store2 store;
store.add("one");
std::string* s1 = store.last_added();
std::cout<<*s1<<std::endl;
store.add("two");
std::cout<<*s1<<std::endl; // crash
}

When you add a new item to an std::vector, the vector might require to expand its buffer, and by doing this it will probably move the buffer in a different memory area. Thus pointers to its element become invalid. To make it short, pointers to the items of a vector are not guaranteed to be valid after resizing a vector, and push_back may resize the vector if it hasn't got enough reserved space.
You could reserve space for the vector at the beginning, but then you'll have a limit to the number of items you can allocate into your vector.

If you need to assure that pointers into the collection remain valid, you probably want something other than a vector (e.g., you could use a std::deque or std::list instead -- with std::deque generally being preferred between the two).
Alternatively, instead of returning a pointer (generally a poor idea anyway), you could return the index of the string, and provide a member function that indexes into the vector when it's used.

Do you have any particular reason you want to use pointers(heap)?
If not, just do:
class store2
{
public:
void add(std::string s) {words.push_back(s);}
std::string last_added() { if (words.size() == 0) return "";
return words[words.size()-1];}
private:
std::vector<std::string> words;
}
;

std::vector's iterators can be invalidated when its content is modified. See vector iterator invalidation.
If you really want to keep the existing interface and retain pointers from elements inserted to your vector, you can store string by pointers and not by value, for example:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
class store2
{
public:
store2 ()
{
}
~store2 ()
{
for (std::vector<std::string *>::iterator it =
words.begin (), end_it = words.end ();
it != end_it; ++it)
{
delete *it;
}
words.clear ();
}
void add (const std::string & s)
{
std::auto_ptr<std::string> v (new std::string (s));
words.push_back (v.get ());
v.release ();
}
std::string *last_added ()
{
return words.back ();
}
const std::string *last_added () const
{
return words.back ();
}
private:
std::vector<std::string *> words;
};
int main ()
{
store2 store;
store.add("one");
std::string* s1 = store.last_added();
std::cout<<*s1<<std::endl;
store.add("two");
std::cout<<*s1<<std::endl; // no crash :-)
}
There is also ptr_vector class in Boost that aims to make this kind of solution more reusable and robust (i.e. automatically manages memory, so you don't have to worry about deleting string when erasing its pointer from vector etc).

Related

How to delete an element from a vector of custom objects

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.

Moving elements from std::vector<T1> to std::vector<std::pair<T1,T2>>

What is the most correct and efficient way to std::move elements from a vector of a certain type (T1) into a vector of an std::pair of that same type (T1) and another type (T2)?
In other words, how should I write MoveItems()?
#include <iostream> // For std::string
#include <string> // For std::string
#include <vector> // For std::vector
#include <utility> // For std::pair
using std::vector;
using std::string;
using std::pair;
vector<string> DownloadedItems;
vector<pair<string,bool>> ActiveItems;
vector<string> Download()
{
vector<string> Items {"These","Words","Are","Usually","Downloaded"};
return Items;
}
void MoveItems()
{
for ( size_t i = 0; i < DownloadedItems.size(); ++i )
ActiveItems.push_back( std::pair<string,bool>(DownloadedItems.at(i),true) );
}
int main()
{
DownloadedItems = Download();
MoveItems();
return 0;
}
Thank you for your time and help, I truly appreciate it!
void MoveItems()
{
ActiveItems.reserve(DownloadedItems.size());
for (auto& str : DownloadedItems)
ActiveItems.emplace_back(std::move(str), true);
}
N.B.: For strings as small as the ones in your example, moving may have the same cost as copying due to SSO, or perhaps even slightly more expensive if the implementation decides to empty out the source anyway.
Some things you can do:
At the start of MoveItems(), call ActiveItems.reserve(DownloadedItems.size());. This prevents your array from resizing while you push things into it.
Instead of calling push_back call emplace_back. Here is an explanation of the advantages of doing so.
Worth noting, in this example, you can stop the copy into a new data structure by just constructing the std::pair from the start, and not copying data.

passing an array into a class function from another class's constructor

Sorry for the confusing title, basically I have created two classes, one is an object, and the other being a box that contains an array of such objects. so what I want to do is create a function/constructor inside the object class that takes in an array of ints and stores them inside the box. I want to be able to call this function through the box class constructor to initialize these objects. So ive tried something like below but it isnt working at all, since only the first value of the array gets passed through. What am I doing wrong?
#include <iostream>
#include <string>
class object{
string objectName;
int values[];
public:
void createObject(int[]);
}
class Box{
object objects[100];
public:
Box();
}
Box::Box (void){
int array1[2];
int array2[15];
object[1].createObject(array1);
object[2].createObject(array2);
}
Object::Object(int Values[]){
values = Values;
}
You should really use std::vector. The problem with arrays is that they decay to pointers when passed as arguments to functions. As a consequence, If you want to store a private copy of the elements you are forced to use heap-allocated objects and consequently do memory management by hand (with all the pain it causes).
It is much better to rely on data members that permit applying the rule of zero.
Here's a tentative solution:
#include <iostream>
#include <string>
#include <vector>
class object {
public:
object(std::vector<int> const& v, std::string const& object_name): v_(v.begin(), v.end()), object_name_(object_name) {}
private:
std::vector<int> v_;
std::string object_name_;
};
class box {
public:
box(std::vector<object> const& objects): objects_(objects) {};
private:
std::vector<object> objects_;
};
I recommend you instead use a std::vector. Arrays don't really work well being passed to functions. When you define Object::Object(int Values[]) you are simply passing the first element of this array by value. If you were to use vectors, the function would look like this:
Object::Object(std::vector<int> &Values):
values(Values)
{
}
The problem with the code is in your thinking on what the array is. In C++, all an array is, is a memory pointer. The language allows you to pass an index into the array pointer to access whatever chunk of data lives at that index.
Whenever you pass arrays between functions or classes, pass the array name only. It will be interpreted as a pointer, and won't copy any data. When you do this, you must also pass the length of the array.
Granted, most people stick with vector<> because it's easier, takes care of memory leaks (mostly) and is VERY efficient. But I like doing it myself. It's good for you. I would try:
#include <iostream>
#include <string>
class Object
{
string _objectName;
int *_values;
int _myLength;
Object();
~Object();
void createObject(int *pValues, int arrLength);
}
class Box
{
_Object objects[100];
Box();
}
Box::Box(void) {
int array1[2];
int array2[15];
object[1].createObject(array1, 2);
object[2].createObject(array2, 15);
}
Object::Object() {
_values = null_ptr;
_myLength = 0;
}
Object::~Object() {
delete[] _values;
}
void Object::createObject(int *pvalues, int arrLength) {
_myLength = arrLength;
_values = new int[_myLength];
for(int ndx=0; ndx<arrLength; ndx++) {
_values[ndx] = pvalues[ndx];
}
}
-CAUTION-
I just adapted your code you provided, and added some conventions. There are a couple places in the code where I'm not sure what the purpose is, but there you go. This should give you a good head start.

Get Element Position within std::vector

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.

How to iterate over a std::map full of strings in C++

I have the following issue related to iterating over an associative array of strings defined using std::map.
-- snip --
class something
{
//...
private:
std::map<std::string, std::string> table;
//...
}
In the constructor I populate table with pairs of string keys associated to string data. Somewhere else I have a method toString that returns a string object that contains all the keys and associated data contained in the table object(as key=data format).
std::string something::toString()
{
std::map<std::string, std::string>::iterator iter;
std::string* strToReturn = new std::string("");
for (iter = table.begin(); iter != table.end(); iter++) {
strToReturn->append(iter->first());
strToReturn->append('=');
strToRetunr->append(iter->second());
//....
}
//...
}
When I'm trying to compile I get the following error:
error: "error: no match for call to ‘(std::basic_string<char,
std::char_traits<char>, std::allocator<char> >) ()’".
Could somebody explain to me what is missing, what I'm doing wrong?
I only found some discussion about a similar issue in the case of hash_map where the user has to define a hashing function to be able to use hash_map with std::string objects. Could be something similar also in my case?
Your main problem is that you are calling a method called first() in the iterator. What you are meant to do is use the property called first:
...append(iter->first) rather than ...append(iter->first())
As a matter of style, you shouldn't be using new to create that string.
std::string something::toString()
{
std::map<std::string, std::string>::iterator iter;
std::string strToReturn; //This is no longer on the heap
for (iter = table.begin(); iter != table.end(); ++iter) {
strToReturn.append(iter->first); //Not a method call
strToReturn.append("=");
strToReturn.append(iter->second);
//....
// Make sure you don't modify table here or the iterators will not work as you expect
}
//...
return strToReturn;
}
edit: facildelembrar pointed out (in the comments) that in modern C++ you can now rewrite the loop
for (auto& item: table) {
...
}
Don't write a toString() method. This is not Java. Implement the stream operator for your class.
Prefer using the standard algorithms over writing your own loop. In this situation, std::for_each() provides a nice interface to what you want to do.
If you must use a loop, but don't intend to change the data, prefer const_iterator over iterator. That way, if you accidently try and change the values, the compiler will warn you.
Then:
std::ostream& operator<<(std::ostream& str,something const& data)
{
data.print(str)
return str;
}
void something::print(std::ostream& str) const
{
std::for_each(table.begin(),table.end(),PrintData(str));
}
Then when you want to print it, just stream the object:
int main()
{
something bob;
std::cout << bob;
}
If you actually need a string representation of the object, you can then use lexical_cast.
int main()
{
something bob;
std::string rope = boost::lexical_cast<std::string>(bob);
}
The details that need to be filled in.
class somthing
{
typedef std::map<std::string,std::string> DataMap;
struct PrintData
{
PrintData(std::ostream& str): m_str(str) {}
void operator()(DataMap::value_type const& data) const
{
m_str << data.first << "=" << data.second << "\n";
}
private: std::ostream& m_str;
};
DataMap table;
public:
void something::print(std::ostream& str);
};
Change your append calls to say
...append(iter->first)
and
... append(iter->second)
Additionally, the line
std::string* strToReturn = new std::string("");
allocates a string on the heap. If you intend to actually return a pointer to this dynamically allocated string, the return should be changed to std::string*.
Alternatively, if you don't want to worry about managing that object on the heap, change the local declaration to
std::string strToReturn("");
and change the 'append' calls to use reference syntax...
strToReturn.append(...)
instead of
strToReturn->append(...)
Be aware that this will construct the string on the stack, then copy it into the return variable. This has performance implications.
Note that the result of dereferencing an std::map::iterator is an std::pair. The values of first and second are not functions, they are variables.
Change:
iter->first()
to
iter->first
Ditto with iter->second.
iter->first and iter->second are variables, you are attempting to call them as methods.
Use:
std::map<std::string, std::string>::const_iterator
instead:
std::map<std::string, std::string>::iterator
Another worthy optimization is the c_str ( ) member of the STL string classes, which returns an immutable null terminated string that can be passed around as a LPCTSTR, e. g., to a custom function that expects a LPCTSTR. Although I haven't traced through the destructor to confirm it, I suspect that the string class looks after the memory in which it creates the copy.
In c++11 you can use:
for ( auto iter : table ) {
key=iter->first;
value=iter->second;
}