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.
Related
Problem Statement: Iterate over an array of objects and check if the object exists in an unordered_set.
Goal: I could have thousand of objects in one container to check their existence in millions of objects in another container. I choose unordered_set for its constant finding complexity and vector for iterating. I'm new to this and if you have any alternate approach, I'd really appreciate it.
Issue: unordered_set find isn't working as expected or I got the concept wrong!
Main:
int main() {
std::vector<std::unique_ptr<Block>> vertices;
vertices.push_back(std::make_unique<Block>("mod1", "work"));
vertices.push_back(std::make_unique<Block>("mod2", "work"));
vertices.push_back(std::make_unique<Block>("mod3", "work"));
std::unordered_set<std::unique_ptr<Block>> undefs;
undefs.insert(std::make_unique<Block>("mod1", "work"));
undefs.insert(std::make_unique<Block>("mod2", "work"));
for(auto& vertex : vertices) {
auto search = undefs.find(vertex);
if(search != undefs.end()){
std::cout << "Block: " << vertex->getName() << "\n";
}
}
}
Block Class Overload:
bool Block::operator==(std::unique_ptr<Block>& block) const {
return block->getName() == mName;
}
Expected Output:
mod1
mod2
Block:
#pragma once
#include <string>
#include <memory>
using std::string;
class Block {
private:
string mName;
string mLib;
public:
Block(string const& name, string const& lib);
string getName() const;
string getLib() const;
bool operator==(std::unique_ptr<Block>& block) const;
};
You are trying to compare pointers, not values.
You need to specify hashing function for class Block.
For example, if you want to use mName as key the code will be the following:
class Block {
private:
string mName;
string mLib;
public:
Block(string const& name, string const& lib)
{
mName = name;
mLib = lib;
}
string getName() const {
return mName;
};
string getLib() const {
return mLib;
}
bool operator==(const Block & block) const;
};
template<> struct std::hash<Block> {
std::size_t operator()(const Block & block) const noexcept {
return std::hash<std::string>{}(block.getName());
}
};
bool Block::operator==(const Block & block) const {
return block.getName() == mName;
}
int main() {
std::vector<Block> vertices;
vertices.emplace_back(Block("mod1", "work"));
vertices.emplace_back(Block("mod2", "work"));
vertices.emplace_back(Block("mod3", "work"));
std::unordered_set<Block> undefs;
undefs.emplace(Block("mod1", "work"));
undefs.emplace(Block("mod2", "work"));
for (auto& vertex : vertices) {
auto search = undefs.find(vertex);
if (search != undefs.end()) {
std::cout << "Block: " << vertex.getName() << "\n";
}
}
}
An unordered_set requires a hashing function and a comparison function. You are using the existing hashing and comparison functions for std::unique_ptr, which is definitely not what you want.
I would not suggest trying to change the behavior of std::unique_ptr<Block> because that will lead to confusion in other code that wants normal semantics for pointers. Instead, add normal hashing and comparison functions for Block and pass customized ones to the constructor of the unordered_set.
The problem there is that you are trying to compare pointers, which are different!
I'dont know the reasons behind using unique_ptr<> but by doing that you are actually trying to compare identities, instead of states which is what you want.
So you can see what I mean, let's say the first Block object is at position 100 in your memory. That would be its identity. So we have object1 whose state is "mod1, work" and whose identity is 100. Then we have object2, whose identity is 150 but its state is the same as object1, "mod1, work".
All what you have both inside the vector and the unordered_set are pointers, so you have memory positions. When inserting them in the vector, you inserted, let's say, position 100. But in the unordered_set you inserted 150. They have the same state, but find method is looking for a memory position.
I hope my answer was helpful. If you find any mistakes here or think differently please let me know. Good luck! :)
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.
I am trying to write an algorithm to check if a graph is connected, (building a board game in which the map is contained as a graph where Regions are Vertices and Borders are edges).
Each region contains a vector of regions that are its neighbors (vector neighbors).
I build the map and check if its connected in the main() function here:
int main()
{
Map map;
Region r1("R1");
Region r2("R2");
Region r3("R3");
r1.addNeighbor(r2);
r2.addNeighbor(r1);
r2.addNeighbor(r3);
r3.addNeighbor(r2);
map.addRegion(r1);
map.addRegion(r2);
map.addRegion(r3);
map.traversal(r1);
map.isConnected();
return 0;
}
And here is my traversal() and isConnected() method implementation:
void Map::traversal(Region currentNode)
{
visited.push_back(currentNode.getRegionName());
Region* current = ¤tNode;
cout << (*current).getRegionName() << " loc: " << current << endl;
for (auto const & neighbor : (currentNode).getNeighbors())
{
if (std::find(visited.begin(), visited.end(), neighbor.getRegionName()) != visited.end()) {
}
else {
cout << (neighbor).getRegionName() << " neighbors: " << (neighbor).getNeighbors().size() << " location: " << &(neighbor) << endl;
traversal(neighbor);
}
}
}
bool Map::isConnected()
{
cout << visited.size() << endl;
cout << regions.size() << endl;
vector<string> regionList;
for (int i = 0; i < regions.size(); i++)
{
regionList.push_back(regions[i].getRegionName());
}
if (visited.size() == regionList.size())
{
return true;
}
else
{
return false;
}
}
The issue I have here is that for some reason, when I get the neighbors of nodes other than the starting node during the recursion of the traversal function, the function for some reason sometimes no longer remembers the neighbors of the current node being traversed (the output of the (neighbor).getNeighbors().size() will sometimes be equal to 0). Also, the address of the currentNode is not always the same as the address of the original object being referenced, leading me to believe that it is copying the object rather than directly pointing to its memory location.
Any help would be appreciated. I am still very new to C++ and the concept of pointers.
Here's the code for my Region class by request:
Region.h
#pragma once
#include <string>
#include <vector>
using namespace std;
class Region
{
private:
string owner;
string regionName;
int numTokens;
public:
vector<Region> neighbors;
void setOwner(string playerName);
void setRegionName(string name);
void setNumTokens(int num);
void addNeighbor(Region r);
vector<Region> getNeighbors() const;
string getOwner() const;
string getRegionName() const;
int getNumTokens() const;
Region();
Region(string regionName);
~Region();
};
Region.cpp
#include "stdafx.h"
#include "Region.h"
Region::Region()
{
}
Region::Region(string name)
{
regionName = name;
owner = "none";
numTokens = 0;
}
Region::~Region()
{
}
void Region::setOwner(string playerName)
{
playerName = owner;
}
string Region::getRegionName() const
{
return regionName;
}
int Region::getNumTokens() const
{
return numTokens;
}
void Region::setRegionName(string name)
{
regionName = name;
}
void Region::setNumTokens(int num)
{
numTokens = num;
}
void Region::addNeighbor(Region r)
{
neighbors.push_back(r);
}
vector<Region> Region::getNeighbors() const
{
return neighbors;
}
string Region::getOwner() const
{
return owner;
}
In
void Map::traversal(Region currentNode)
currentNode is passed by value. This means currentNode is independent of and (anywhere it matters) a copy of the Region provided as a parameter when invoking traversal. This is the different addresses you are noting with
cout << (*current).getRegionName() << " loc: " << current << endl;
Fix with
void Map::traversal(Region & currentNode)
although
void Map::traversal(const Region & currentNode)
is preferred if you do not intend on changing currentNode inside the function (or as a result of the function). It prevents mistakes, and since you have promised not to change the provided Region, the compiler can take advantage of some tricks and optimizations.
The next boobytrap is
vector<Region> neighbors;
stores copies of whatever is placed in them, not the original. So
r1.addNeighbor(r2);
calls
void Region::addNeighbor(Region r)
which is also pass by value (r is a copy of r2) and
neighbors.push_back(r);
places a copy of r into the vector. End result is r1 does not really know r2, it knows a copy. Modifying r2 after the copy does not effect the copy. You are trapped. You must store pointers.
Region needs to look something more like
class Region
{
private:
string owner;
string regionName;
int numTokens;
public:
vector<Region *> neighbors; // change here
void setOwner(string playerName);
void setRegionName(string name);
void setNumTokens(int num);
void addNeighbor(Region * r); // change here
vector<Region *> getNeighbors() const; // change here
string getOwner() const;
string getRegionName() const;
int getNumTokens() const;
Region();
Region(string regionName);
// ~Region(); not required.
};
Unrelated: Region contains no resources that are not self managed and as a result can take advantage of The Rule of Zero. You can safely remove its destructor. Yes. I know it contains a vector of raw pointers. More on this below.
This can lead you into a memory management nightmare, so you have to make sure you have ownership of those Regions everyone is pointing at nailed down.
Ownership is basically "Who's responsible for the clean-up? Who makes sure what's being pointed at is freed when its no longer required?"
In the case of your example,
Region r1("R1");
Is an Automatic variable. It manages it's own lifetime. It is released when it goes out of scope. You can
r1.addNeighbor(&r2); //pass raw pointer to r2
and r2 will be destroyed on schedule right before r1, which if you think about it is kinda dangerous. r1 still holds a pointer to r2, but r1 is also going out off scope, so you'd need to do something stupid in the destructor or go multi threaded. Region doesn't need a destructor, so you're safe, and if you are multi threaded a whole new set of assumptions are required, like why are you going out of scope in main while you still have threads running?
But what about less trivial cases where you're adding and removing Regions and reshaping the graph dynamically?
This gets ugly fast.
Often people will elect to go with a smart pointer in neighbors to manage the
memory for them. Doesn't work well in your case. Doesn't work well for them in a lot of cases, either.
vector<unique_ptr<Region>> (one-and-only-one pointer) doesn't make any sense because there can be many Regions all pointing at the same Region. So much for uniqueness.
shared_ptr also doesn't make sense because r1 points to r2 and r2 points back to r1. Direct action will have to be taken to eliminate the cycle and this mostly defeats the point of a smart pointer. What if you forget or get derailed by an exception?
There are games one can play with weak_ptr, but in a bidirectional graph who is the shared and who is the weak?
Opinion Warning: I favour Regions using raw pointers and a master list of Regions (vector<unique_ptr<Region>> again, but this time it works) as a member of a Graph class that manages access, insertion, removal, and all the other manipulations the same way you would with a linked list managing the linked nodes.
There may be sharper solutions out there that I'm not seeing.
Edit: Here's one based on M.M.'s comment
class Region
{
private:
string owner;
string regionName;
int numTokens;
public:
vector<string> neighbors; // change here. If you have large numbers of neighbours
// consider using a std::set in place of the vector
void setOwner(string playerName);
void setRegionName(string name);
void setNumTokens(int num);
void addNeighbor(const string & name); // change here. Accepting const reference
// reduces unnecessary copying. May want
// to use the same trick for other methods
// receiving a string
const vector<string> & getNeighbors() const; // change here. Returning const
// reference reduces unnecessary copying
string getOwner() const;
string getRegionName() const;
int getNumTokens() const;
Region();
Region(string regionName);
// ~Region(); not required.
};
This assumes regionName is unique and uses it as the key to access the Region from a master list that looks something like map<string, Region> masterlist;. masterlist manages storage for all of your Regions. Remove a Region from it and If the regionName cannot be found in masterlist you don't have to worry about invalid pointers, you just take note and remove it from neighbors.
Remember to be careful with the subscript operator. In masterlist[name] if name cannot be found, a Region will be default constructed for it and stored. Prefer to use the find method if you are looking for a Region that should exist.
If you have a fixed number of regions, consider using a simple array or std::array in place of the map for masterlist and use the index of the Region in the array as the identifier in place of string.
Supplemental reading: What's the difference between passing by reference vs. passing by value?
I am trying to read/deserialize a list of elements from a file (and then filter out some of them). It is a useful approach to use an iterator for this purpose?
My current try is
#include <boost/iterator/iterator_adaptor.hpp>
class ReadIterator : public boost::iterator_adaptor<ReadIterator, Elem *, boost::single_pass_traversal_tag>
{
public:
explicit ReadIterator(const char *filename) : reader(filename) {}
private:
friend class boost::iterator_core_access;
void increment() {
this->base_reference() = reader.readNext();
}
Reader reader;
};
This does not properly deallocate memory (e.g., readNew returns a pointer to a new Elem), what is the right way to do this? Also, how would one actually use such an iterator, how can the end be determined? Or is there a better approach than using an iterator?
The easy way to do this is to use the std::istream_iterator
std::vector<YourObjectClass> data;
std::remove_copy_if(std::istream_iterator<YourObjectClass>(file),
std::istream_iterator<YourObjectClass>(),
std::back_inserter(data),
YourFilter
);
The standard algorithm copies objects (of type YourObjectClass) from the input file and places them into the vector data if the filter functor returns true.
The only conditions are:
YourObjectClass must have an input stream operator
YourFilter must overload the operator() for objects of YourObjectClass or be a function that takes a parameter of type YourObjectClass.
Simple Working Example:
My object is a line.
Filter out line(s) that start with the letter 'A'
Exmpale:
#include <vector>
#include <string>
#include <fstream>
#include <iterator>
#include <algorithm>
struct Line
{
std::string data;
};
std::istream& operator>>(std::istream& stream, Line& line)
{
return std::getline(stream, line.data);
}
struct AFilter
{
bool operator()(Line const& line) const
{
return line.data.size() > 0 && line.data[0] == 'A';
}
};
int main()
{
std::ifstream file("Plop");
std::vector<Line> data;
std::remove_copy_if(std::istream_iterator<Line>(file),
std::istream_iterator<Line>(),
std::back_inserter(data),
AFilter()
);
}
Rather than readNext() returning a raw pointer to an element, can you construct the call so that it returns a reference-counted smart-pointer that will automatically release it's resources when the reference-count to the pointer goes to zero? Either that, or you're going to have to find a way to fetch the pointer back so you can call delete on it before you allocate more memory via the next call to readNext() when increment() is called again.
As for the "end", what you could do in this case is have some test in your Reader class that detects if you've reached the end of the file or some other ending scenario. If you have, then return false, otherwise return true. For example:
bool increment()
{
if (reader.not_end_of_file())
{
this->base_reference() = reader.readNext();
return true;
}
return false;
}
So now you could call increment() in some type of loop, and you'll know when you've hit the end-of-file or some other ending because the function will return false.
Using an iterator for the purpose is fine. You haven't given any indication that the existing istream_iterator won't work for your purpose though. At least in most cases, you can just write an operator>> for a single element, and use std::istream_iterator to read a list of those elements from the file.
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).