Inserting shared pointers to strings from cin into vector - c++

I am trying to insert shared pointers to strings into a vector using the copy algorithm. Is it possible to read lines from the istream and store shared_ptr's to those strings in a vector using the copy algorithm? So far I'm stuck with the following class.
class DVector
{
typedef vector<shared_ptr<string>> PTSVector;
PTSVector data;
public:
void push_back()
{
copy(istream_iterator<string>(cin),
istream_iterator<string>(),
back_inserter(data));
}
};
Can anyone tell me what might go wrong?
Thanks!

std::copy isn't appropriate here, because you're reading strings, but trying to store pointers to strings.
Your source and destination types are different, so you need std::transform, instead. This function is similar to copy, but it applies a function to transform the input before writing it to the output iterator.
transform(istream_iterator<string>(cin), istream_iterator<string>(), back_inserter(data),
[](const string& str) { return make_shared<string>(str); });

Related

Interpret a std::string as a std::vector of char_type?

I have a template<typename T> function that takes a const vector<T>&. In said function, I have vectors cbegin(), cend(), size(), and operator[].
As far as I understand it, both string and vector use contiguous space, so I was wondering if I could reuse the function for both data types in an elegant manner.
Can a std::string be reinterpreted as a std::vector of (the appropriate) char_type? If so, what would the limitations be?
If you make your template just for type const T& and use the begin(), end(), etc, functions which both vector and string share then your code will work with both types.
Go STL way and use iterators. Accept iterator to begin and iterator to end. It will work with all possible containers, including non-containers like streams.
There is no guarantee the layout of string and vector will be the same. They theoretically could be, but they probably aren't in any common implementation. Therefore, you can't do this safely. See Zan's answer for a better solution.
Let me explain: If I am a standard library implementer and decide to implement std::string like so....
template ...
class basic_string {
public:
...
private:
CharT* mData;
size_t mSize;
};
and decide to implement std::vector like so...
template ...
class vector {
public:
...
private:
T* mEnd;
T* mBegin;
};
When you reinterpret_cast<string*>(&myVector) you wind up interpreting the pointer to the end of your data as the pointer to the start of your data, and the pointer to the start of your data to the size of your data. If the padding between members is different, or there are extra members, it could get even weirder and more broken than that too.
So yes, in order for this to possibly work they both need to store contiguous data, but they also need quite a bit else to be the same between the implementations for it to work.
std::experimental::array_view<const char> n4512 represents a contiguous buffer of chars.
Writing your own is not hard, and it solves this problem and (in my experience) many more.
Both string and vector are compatible with an array view.
This lets you move your implementation into a .cpp file (and not expose it), gives you the same performance as doing it with std::vector<T> const& and probably the same implementation, avoids duplicating code, and uses light weight contiguous buffer type erasure (which is full of tasty keywords).
If the key point is that you want to access a continuous area in memory where instances of a specific char type are stored then you could define your function as
void myfunc(const CType *p, int size) {
...
}
to make it clear that you assume they must be adjacent in memory.
Then for example to pass the content of a vector the code is simply
myfunc(&myvect[0], myvect.size());
and for a string
myfunc(mystr.data(), mystr.size());
or
myfunc(buffer, n);
for an array.
You can't directly typecast a std::vector to a std::string or vice versa. But using the iterators that STL containers provide does allow you to iterate both a vector and a string in the same way. And if your function requires random access of the container in question then either would work.
std::vector<char> str1 {'a', 'b', 'c'};
std::string str2 = "abc";
template<typename Iterator>
void iterator_function(Iterator begin, Iterator end)
{
for(Iterator it = begin; it != end; ++it)
{
std::cout << *it << std::endl;
}
}
iterator_function(str1.begin(), str1.end());
iterator_function(str2.begin(), str2.end());
Both of those last two function calls would print the same thing.
Now if you wanted to write a generic version that parsed only characters only stored in a string or in a vector you could write something that iterated the internal array.
void array_function(const char * array, unsigned length)
{
for(unsigned i = 0; i < length; ++i)
{
std::cout << array[i] << std::endl;
}
}
Both functions would do the same thing in the following scenarios.
std::vector<char> str1 {'a', 'b', 'c'};
std::string str2 = "abc";
iterator_function(str1.begin(), str1.end());
iterator_function(str2.begin(), str2.end());
array_function(str1.data(), str1.size());
array_function(str2.data(), str2.size());
There are always multiple ways to solve a problem. Depending on what you have available any number of solutions might work. Try both and see which works better for your application. If you don't know the iterator type then the char typed array iteration is useful. If you know you will always have the template type to pass in then the template iterator method might be more useful.
The way your question is put at the moment is a bit confusing. If you mean to be asking "is it safe to cast a std::vector type to a std::string type or vice versa if the vector happens to contain char values of the appropriate type?", the answer is: no way, don't even think about it! If you're asking: "can I access the contiguous memory of non-empty sequences of char type if they're of the type std::vector or std::string?" then the answer is, yes you can (with the data() member function).

Strings in Vectors. and placing them in order

So I am placing objects in a vector. I want to drop them in order as they are added. the basics of the object are
class myObj {
private:
string firstName;
string lastName;
public:
string getFirst;
string getLast;
}
I also have a vector of these objects
vector< myObj > myVect;
vector< myObj >::iterator myVectit = myVect.begin();
when I add a new object to the vector I want to find where it should be placed before inserting it. Can I search a vector by an object value and how? This is my first attempt
void addanObj (myObj & objtoAdd){
int lowwerB = lower_bound(
myVect.begin().getLast(), myVect.end().getLast(), objtoAdd.getLast()
);
int upperB = upper_bound(
myVect.begin().getLast(), myVect.end().getLast(), objtoAdd.getLast()
);
from there i plan to use lowwerB and upper B to determine where to insert the entry. what do I need to do to get this to work or what is a better method of tackling this challenge?
----Follow up----
the error I get when I attempt to compile
error C2440: 'initializing' : cannot convert from 'std::string' to 'int'
No user-defined-conversion operator available that can perform this conversion,
or the operator cannot be called
The compiler highlights both lower_bound and upper_bound. I would guess it is referring to where I am putting
objtoAdd.getLast()
-----More Follow up-----------------
THis is close to compiling but not quite. What should I expect to get from lower_bound and upper_bound? It doesnt match the iterator i defined and im not sure what I should expect.
void addMyObj(myObj myObjtoadd)
vector< myObj>::iterator tempLB;
vector< myObj>::iterator tempUB;
myVectit= theDex.begin();
tempLB = lower_bound(
myVect.begin()->getLast(), myVect.end()->getLast(), myObjtoadd.getLast()
);
tempUB = upper_bound(
myVect.begin()->getLast(), myVect.end()->getLast(), myObjtoadd.getLast()
);
Your calls to std::lower_bound and std::upper_bound are incorrect. The first two parameters must be iterators that define a range of elements to search and the returned values are also iterators.
Since these algorithms compare the container elements to the third parameter value you'll also need to provide correct operator< functions that compare an object's lastName and a std::string. I've added two different compare functions since std::lower_bound and std::upper_bound pass the parameters in opposite order.
I think I have the machinery correct in this code, it should be close enough for you to get the idea.
class myObj {
private:
std::string firstName;
std::string lastName;
public:
std::string getFirst() const { return firstName; }
std::string getLast() const { return lastName; }
};
bool operator<(const myObj &obj, const std::string &value) // used by lower_bound()
{
return obj.getLast() < value;
}
bool operator<(const std::string &value, const myObj &obj) // used by upper_bound()
{
return value < obj.getLast();
}
int main()
{
std::vector<myObj> myVect;
std::vector<myObj>::iterator tempLB, tempUB;
myObj objtoAdd;
tempLB = std::lower_bound(myVect.begin(), myVect.end(), objtoAdd.getLast());
tempUB = std::upper_bound(myVect.begin(), myVect.end(), objtoAdd.getLast());
}
So this is definitely not the best way to go. Here's why:
Vector Size
A default vector starts out with 0 elements, but capacity to hold some number; say 100. After you add the 101st element, it has to completely recreate the vector, copy over all the data, and then delete the old memory. This copying can become expensive, if done enough.
Inserting into a vector
This is going to be even more of a problem. Because a vector is just a contiguous block of memory with objects stored in insert order, say you have the below:
[xxxxxxxzzzzzzzz ]
if you want to add 'y', it belongs between x and z, right? this means you need to move all the z's over 1 place. But because you are reusing the same block of memory, you need to do it one at a time.
[xxxxxxxzzzzzzz z ]
[xxxxxxxzzzzzz zz ]
[xxxxxxxzzzzz zzz ]
...
[xxxxxxx zzzzzzzz ]
[xxxxxxxyzzzzzzzz ]
(the spaces are for clarity - previous value isn't explicitly cleared)
As you can see, this is a lot of steps to make room for your 'y', and will be very very slow for large data sets.
A better solution
As others have mentioned, std::set sounds like it's more appropriate for your needs. std::set will automatically order all inserted elements (using a tree data structure for much faster insertion), and allows you to find particular data members by last name also in log(n) time. It does this by using bool myObj::operator(const & _myObj) const to know how to sort the different objects. If you simply define this operator to compare this->lastName < _myObj.lastName, you can simply insert into the set much quicker.
Alternately, if you really really want to use vector: instead of sorting it as you go, just add all the items to the vector, and then perform std::sort to sort them after all the inserts are done. This will also complete in n log(n) time, but should be considerably faster than the current approach because of the vector insertion problem.

How do I build a vector<> of search results from a source vector<>?

Considering this example:
std::vector<Student> students;
//poplate students from a data source
std::vector<Student> searched(students.size());
auto s = std::copy_if(students.begin(), students.end(), searched.begin(),
[](const Student &stud) {
return stud.getFirstName().find("an") != std::string::npos;
});
searched.resize(std::distance(searched.begin(), s));
I have the following questions:
Is it ok to allocate memory for searched vector equals to the initial vector? There may be 500 not small objects and maybe none satisfying the search criteria? Is there any other way?
When copying to the searched vector it is called the copy assignment operator and ..obviously a copy is made. What if from those 500 objects 400 satisfying the search criteria?
Isn't just memory wasting?
I am a c++ noob so I may say something stupid. I don't see why to ever use vector<T> where T is a object. I would always use vector<shared_ptr<T>>. If T is a primitive type like an int i guess it's kinda straight forward to use vector<T>.
I considered this example because I think it's very general, you always have to pull some data out of a database or xml file or any other source. Would you ever have vector<T> in your data access layer or vector<shared_ptr<T>>?
Concerning your first question:
1 - Is it ok to allocate memory for searched vector equals to the initial vector? There may be 500 not small objects and maybe none satisfying the search criteria? Is there any other way?
You could use a back inserter iterator, using the std::back_inserter() standard function to create one for the searched vector:
#include <vector>
#include <string>
#include <algorithm>
#include <iterator> // This is the header to include for std::back_inserter()
// Just a dummy definition of your Student class,
// to make this example compile...
struct Student
{
std::string getFirstName() const { return "hello"; }
};
int main()
{
std::vector<Student> students;
std::vector<Student> searched;
// ^^^^^^^^^
// Watch out: no parentheses here, or you will be
// declaring a function accepting no arguments and
// returning a std::vector<Student>
auto s = std::copy_if(
students.begin(),
students.end(),
std::back_inserter(searched),
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Returns an insert iterator
[] (const Student &stud)
{
return stud.getFirstName().find("an") != std::string::npos;
});
}
Concering your second question:
2 - When copying to the searched vector it is called the copy assignment operator and ..obviously a copy is made. What if from those 500 objects 400 satisfying the search criteria? Isn't just memory wasting?
Well, if you have no statistical information on the selectivity of your predicate, then there is not much you can do about it. Of course, if your purpose is to process somehow all those students for which a certain predicate is true, than you should use std::for_each() on the source vector rather than create a separate vector:
std::for_each(students.begin(), students.end(), [] (const Student &stud)
{
if (stud.getFirstName().find("an") != std::string::npos)
{
// ...
}
});
However, whether this approach satisfies your requirements depends on your particular application.
I don't see why to ever use vector<T> where T is a object. I would always use vector<shared_ptr<T>>.
Whether or not to use (smart) pointers rather than values depends on whether or not you need reference semantics (apart from possible performance considerations about copying and moving those objects around). From the information you provided, it is not clear whether this is the case, so it may or may not be a good idea.
What are you going to do with all those students?
Just do that instead:
for(Student& student: students) {
if(student.firstNameMatches("an")) {
//.. do something
}
}

How can one implement a custom string class using the STL?

In C++ - The Complete Reference, the author gives us a challenge after showing how he implements a custom C++ string class. Excerpt from the book:
A Challenge:
Try implementing StrType (the string class) using the STL. That is, use a container to store the characters that comprise a string. Use iterators to operate on the strings, and use the algorithms to perform the various string manipulations.
I understand the basic concept here, but am having trouble implementing it. should I do std::vector < char > and push_back for every char or something like that? What about the string manipulations? Need some help. Sample code will be accepted gratefully, or you can explain how I may be able to implement this.
Yes, std::vector<char> sounds like a great idea. It will save you from the troubles of writing a custom destructor, copy constructor and copy assignment operator. Plus all the iterator member functions (begin, end and co.) can just delegate to the std::vector<char> versions.
can u give some code on how to do string manipulations? e.g concatenation ?
Sure thing, here is how I would overload operator+= and operator+ for the string type:
class StrType
{
std::vector<char> vec;
public:
// ...
StrType& operator+=(const StrType& rhs)
{
vec.insert(vec.end(), rhs.vec.begin(), rhs.vec.end());
return *this;
}
};
StrType operator+(StrType lhs, const StrType& rhs)
{
lhs += rhs;
return lhs;
}
There's probably a more efficient version of operator+, but you can figure that out on your own.
Using std::vector<char> would probably be the best container to use in this case (random access iterators and low overhead make it an attractive choice for a string).
Further to your comment on FredOverflow's answer, you can perform a string concatenation as follows:
std::vector<char> firstString;
firstString.push_back('A');
firstString.push_back('B');
std::vector<char> secondString;
secondString.push_back('X');
secondString.push_back('Y');
firstString.insert( firstString.end(), secondString.begin(), secondString.end() );
for( auto it = firstString.begin(); it != firstString.end(); ++it )
{
std::cout << (*it);
}
In this case this would print out: ABXY. You can see it here: http://ideone.com/OmdoU

C++ using STL List, how to copy an existing list into a new list

Right now I'm working with a copy constructor for taking a list called val of type char, and I need to take all the elements of a string v that is passed into the copy constructor and put them into the val list.
Public:
LongInt(const string v);
Private:
list<char> val;
So here in the public section of the LongInt class I have a copy constructor which takes the val list and copies the v string into it. Can anyone help me figure out how to do this? Thanks in advance!
You'll have to iterate over the string and extract the data character by character. Using the std::copy algorithm should work:
std::copy(v.begin(), v.end(), std::back_inserter(val));
In your LongInt constructor just use the iterator, iterator list constructor:
LongInt(const string v) : val(v.begin(), v.end()) { }
That being said, have you considered actually using string or possibly deque<char> to manipulate your sequence rather than list? Depending on your needs, those alternatives might be better.
LongInt::LongInt( const string v ) : val(v.begin(), v.end())
{
}
First, use std::string if it's a string you're storing. It's a container like any other. If you can't or don't want to store a string, use std::vector. But that would boil down to a less-functional std::string anyway, so just use std::string.
For the copying:
std::copy( v.begin(), v.end(), std::back_inserter(val) );
But just use a std::string if it's a list of chars you're storing.