Trouble iterating through a list - c++

I'm working on this project that basically reads information from a file, use that information on an object and then creates a list that contains the objects.
I have a class named Acao which basically contains a few pieces of information, some strings and some floats. Pretty simple;
What I'm trying to do in order to check if my list is being correctly built is to output a float named cMed using the getcMed() member from Acao class.
Ok, first of all:
I'm getting three errors while trying to iterate through my list, being with operators =, != and ++.
All of them being - repectively:
error C2679: binary '=' : no operator found which takes a right-hand operand of type 'std::_List_iterator<std::_List_val<std::_List_simple_types<Acao>>>' (or there is no acceptable conversion)
As much as I don't think that really matters in this case, these are my included libs:
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <list>
#include <stdlib.h>
#include <sstream>
Now then, my second problem with this piece of code is with the line:
cout << (*it)->getcMed();
Both my list and the iterator it are of Acao type, but my compiler (I'm using VS 2013 for my IDE and compiler) gives me the following error:
error C2039: 'getcMed' : is not a member of 'std::list>'
Here's the chunk of code in question (also note: I'm using namespace std for this):
list<Acao> novaListaAcoes(){
fstream file;
streampos begin;
list<Acao> listaAcoes, it;
Acao A;
string linha, papel, companhia, tipo;
float min, med, max;
file.open("G:\\VS\\ConsoleApplication4\\BDINaux.txt");
file.clear();
file.seekg(0, ios::beg);
listaAcoes.clear();
while (!file.eof()){
getline(file, linha);
if (file.eof()){ break; }
vector<char> vector(linha.begin(), linha.end());
min = calcMin(vector);
max = calcMax(vector);
med = calcMed(vector);
papel = lePapel(vector);
companhia = leComapanhia(vector);
tipo = leTipo(vector);
vector.clear();
A.setCompanhia(companhia);
A.setCotacao(med, min, max);
A.setNomePapel(papel);
cout << papel<< endl;
listaAcoes.push_back(A);
}
cout << "fim loop\n";
for (it = listaAcoes.begin(); it != listaAcoes.end(); ++it){
cout << (*it)->getcMed();
}
return listaAcoes;
}

Your declaration:
list<Acao> listaAcoes, it;
Doesn't match the type needed for the assignment statement in the for loop initializer:
for (it = listaAcoes.begin(); // <<<
Make a separate declaration for it:
list<Acao>::iterator it;
An iterator is a concept for c++ container classes, but not equivalent to a class instance of those itself!
The idiom I personally prefer, is to declare variables closest to their use, such for a for loop:
for (std::list<Acao>::iterator it = listaAcoes.begin();
it != listaAcoes.end();
++it)
{
// Access it's underlying Acao instance using -> or * dereference operators
}

Related

How to shuffle an array in C++?

I have an array:
names[4]={john,david,jack,harry};
and i want to do it randomly shuffle, like:
names[4]={jack,david,john,harry};
I tried to use this but it just shuffled the letters of the first word in the array:
random_shuffle(names->begin(), names->end());
Here is the full code, it reads names from a .txt file and puts in an array:
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
ifstream readName("names.txt");
string names[197];
int i = 0;
for (string line; getline(readName, line); ){
readName >> names[i];
i++;
}
readName.close();
random_shuffle(names->begin(), names->end());
for (int i = 0; i < 197; i++) {
cout << names[i] << endl;
}
return 0;
}
I tried few other things from different people but I couldn't manage to work it.. Anything helps, thank you!
Here's your code with what I felt were the smallest amount of changes. One could argue that I didn't need to change your first for loop as much, but I figure that if you have the prescience to know how many names you're reading, you might as well use the knowledge.
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator> // std::begin(), std::end(); required for C-arrays
#include <random> // std::mt19937; needed to feed std::shuffle()
#include <string>
// using namespace std; // BAD PRACTICE
int main() {
constexpr int size = 4; // Give your magic number a name; only need to change
// a single location
std::ifstream readName("names.txt");
if (!readName) { // Always check that you successfully opened the file.
std::cerr << "Error opening file.\n";
return 1;
}
std::string names[size];
// int i = 0;
for (int i = 0; i < size; ++i) { // Retool the loop entirely
std::getline(readName, names[i]);
}
readName.close();
// This is a fragile solution. It's only working because the array is in
// scope
std::shuffle(std::begin(names), std::end(names),
std::mt19937{std::random_device{}()});
for (int i = 0; i < size; i++) {
std::cout << names[i]
<< '\n'; // Don't use std::endl unless you actually need it
}
return 0;
}
This is not ideal code, though. Any change to the size of the input file requires changing the code and re-compiling. The biggest single change was to get rid of std::random_shuffle and use std::shuffle() instead. std::random_shuffle was deprecated with C++14 and removed in C++17. It's bad to use it. std::shuffle() does add the requirement of providing a PRNG, but it's not so bad. It leads to better code if you have a PRNG that needs to randomize many different things in a bigger program. This is because it's good to have a single PRNG and let it live for the length of your program as opposed to constantly building new ones.
And the C-array just makes things a bit clunkier. Enter std::vector.
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
#include <random> // std::mt19937; needed to feed std::shuffle()
#include <string>
#include <vector>
int main() {
std::ifstream readName("names.txt");
if (!readName) { // Always check that you successfully opened the file.
std::cerr << "Error opening file.\n";
return 1;
}
std::vector<std::string> names;
std::string name;
while (std::getline(readName, name)) { // Retool the loop entirely
names.push_back(name);
}
readName.close();
std::shuffle(std::begin(names), std::end(names),
std::mt19937{std::random_device{}()});
for (const auto& i : names) {
std::cout << i << '\n';
}
return 0;
}
The vector can grow as needed, so you see how much simpler the loop that reads the names becomes. It's also more flexible since you don't have to know ahead of time how many entries to expect. It will "just work." With the call to std::shuffle() I kept the std::begin(names) syntax because many consider this a best practice, but you could have also used names.begin() if you wanted since the vector class provides its own iterators.
Lets have a look at you main question:
I tried to use this but it just shuffled the letters of the first word in the array:
random_shuffle(names->begin(), names->end());
The reason that it only shuffles the first word is because of the types and usage.
So names is an array of strings.
string names[197];
The problem stems from the C world. Were arrays decay into pointers exceedingly easily (just by being used in an expression). So here names-> has decayed into a pointer to the first element of the array. This allows you to use the -> operator which normally only works on pointers. So you are calling the functions begin() and end() on the pointer to the first element in the array. Thus only the first name is being shuffled.
The fix this problem use std::begin() method.
// here std::begin / std::end find the beginning and end
// of the array. So you are shuffling the array.
random_shuffle(std::begin(names), std::end(names));
But I would note that random_shuffle() is outdated. As mentioned by #sweenish you should use std::shuffle() See his answer for details.
A couple of things we can improve:
You use C array to store the names. Sure it works, but it is susceptible to a couple of issues because it can not be re-sized (and unless you think the file is never going to be changed that may be an issue). Potentially a hidden issue to a far distant maintainer.
std::vector<std::string> names; // resizeable container.
I would note that the current implementation ignores the first line. Then reads the first word from each subsequent line. There is also a slight issue that the last line may be empty and you read an empty name into the last element of the array (but you don't track how many names you read so unless you use all elements in the array you may never notice that).
I would change that. As it is not obvious. I would delibrately and seprately ignore the first line. Then I would simply read all the first words into a vector (so you know the size).
std::string line
std::getline(file, line); // Ignore the first line.
std::string word
while(file >> word) {
names.push_back(word);
std::getline(file, line); // ignore the rest of the line.
}
We could get fancy. Use iterators to simply create the array directly.
class Line
{
std::string firstWord;
friend std::istream& operator>>(std::istream& stream, Line& data) {
stream >> data.firstWord;
stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); retrun stream;
}
operator std::string() const {
return firstWord;
}
};
Now you can create and load the vector in one line:
std::vector<std::string> names(std::istream_iterator<Line>(file),
std::istream_iterator<Line>{});
Then finally copy the names out can be made easier using foreach loop. Also don't use std::endl in a loop like this. It forces a flush of the underlying bugger after each new line. This is very ineffecient.
for(auto const& name: names) {
std::cout << name << "\n";
}
So the result is:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
class Line
{
std::string firstWord;
friend std::istream& operator>>(std::istream& stream, Line& data) {
stream >> data.firstWord;
stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); return stream;
}
operator std::string() const {
return firstWord;
}
};
int main()
{
std::ifstream file("names.txt");
std::string line
std::getline(file, line); // Ignore the first line.
std::vector<std::string> names(std::istream_iterator<Line>(file),
std::istream_iterator<Line>{});
random_shuffle(std::begin(names), std::end(names));
for(auto const& name: names) {
std::cout << name << "\n";
}
}

how to fill a set in C++ with input from user

how can we fill a set in C++ from user input in one liner
I know we can store input in a variable and then use
set.insert(some_variable)
but cant we take input from user directly?
like set.insert(cin>>set[i])?
I am just a beginner.
You can copy the values into a std::set with std::copy_n. (Please see here)
The source will be the std::istream_iterator (described here) and the destination will be the std::inserter wrapper (described here).
Please see:
#include <iostream>
#include <set>
#include <iterator>
#include <algorithm>
int main() {
// Our set
std::set<int> s{};
// How many values do we want to read
const size_t numberOfUserInputs{ 3 };
// Copy values from std::cin to set
std::copy_n(std::istream_iterator<int>(std::cin), numberOfUserInputs, std::inserter(s, s.begin()));
// Debug output
for (int i : s) std::cout << i << '\n';
return 0;
}
no, you can't do this.
things that go with vector like
std::vector<int> he;
auto endf = he.end();
*he.insert(endf, 1) = 5;
will not go with set. insert returns const_iterator.

is there any way to define dynamic array without Determine the size of it

I need a dynamic array that I don't have to scale(Determine) to a fixed number like the following
string* s;
I have this code so far, but obviously it doesn't work.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
fstream f;
f.open("resa.txt");
string* s;
int i = 0;
while (f.good())
{
f >> *(s + i);
i++;
}
return 0;
}
This is my task:
Now we change the class definitions a bit. No static arrays can occur anymore. The fact that the arrays instead become dynamic means that some class methods need to be modified, and that some / some of the classes need copy constructors and assignment methods (or superimposed assignment operator). [...]"
This means, that I just can't use data structures.
It's not automatic, you have to allocate more memory every time you want to resize, copy elements into new array and delete the old one. Fortunately, standard library got you covered with std::vector - an automatically resizable array.
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
using namespace std;
int main()
{
fstream f;
f.open("resa.txt");
string temp;
std::vector<std::string> s;
while (f >> temp)
{
s.push_back(temp);
}
return 0;
}
I also fixed your input reading - see Why is iostream::eof inside a loop condition (i.e. while (!stream.eof())) considered wrong? (applies to good() as well).
Alternatively, you can use std::istream_iterator to initialize vector in one line instead of using loop (credit to Ayxan):
vector<string> s{ istream_iterator<string>{f}, {} };

C++ how to insert wstring into vector at given position

I am wondering, if anyone could explain to me the second argument in the vector.insert() method:
iterator insert (iterator position, const value_type& val);
For example, I have a vector of type wstring and I would like to insert a wstring at a given position. I have figured out how to set the position using an iterator:
wstring word = "test";
int insertion_pos = 3;
iterator it = words.begin();
words.insert( it + insertion_pos, word );
But what about that second argument? How can I pass a wstring object to the insert() method?
Thanks a lot.
Cheers,
Martin
Full example:
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <wchar.h>
#include <vector>
using namespace std;
int main(void) {
// Initialize the vecor with three words.
vector<wstring> words;
wstring word1 = "FirstWord"; // Error msg: no viable conversion from 'const char [10]' to 'wstring' (aka
// 'basic_string<wchar_t>')
wstring word2 = "SecondWord"; // same here
wstring word3 = "ThirdWord"; // same here
words.push_back(word1);
words.push_back(word2);
words.push_back(word3);
// Now try to insert a new word at position 2 (i.e. between "SecondWord "and "ThirdWord"
int position = 2;
wstring word4 = "InsertThis"; // same error as above
iterator it = words.begin(); // Error: use of class template iterator requires template
// arguments
words.insert( it + position, word4 );
// Invalid arguments ' Candidates are: __gnu_cxx::__normal_iterator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>
// *,std::vector<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>,std::allocator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>>>>
// insert(__gnu_cxx::__normal_iterator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>
// *,std::vector<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>,std::allocator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>>>>,
// const std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>> &) void
// insert(__gnu_cxx::__normal_iterator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>
// *,std::vector<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>,std::allocator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>>>>,
// unsigned long int, const std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>> &) void
// insert(__gnu_cxx::__normal_iterator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>
// *,std::vector<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>,std::allocator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>>>>,
// #10000, #10000) '
return EXIT_SUCCESS;
}
Thanks for the clear example of the issue. Here is a modified version with some comments about changes. It compiles for me using clang on Mac OS X.
One change is the "L" in front of the string literal. This is an indicator that the string literal to follow is of type wchar_t. See also this.
Wide character/unicode/utf support is something I would add only if it's needed in the problem you're trying to solve.
// #include <stdio.h> prefer "cstdio" to stdio.h; not used in example
// #include <stdlib.h> same
#include <iostream>
#include <string>
// #include <wchar.h> not used in example
#include <vector>
using namespace std;
// simplify to main()
int main() {
// Initialize the vecor with three words.
vector<wstring> words;
wstring word1(L"FirstWord"); // Use Constructor, no assignment operator=
wstring word2(L"SecondWord");
wstring word3(L"ThirdWord");
words.push_back(word1);
words.push_back(word2);
words.push_back(word3);
int position = 2;
wstring word4(L"InsertThis");
// iterator depends on type of container
vector<wstring>::iterator it = words.begin();
words.insert( it + position, word4 );
for (const std::wstring& w : words)
std::wcout << w << " ";
std::wcout << std::endl;
return EXIT_SUCCESS;
}
Understanding the insert call
The prototype for the vector's insert member function is:
iterator insert( iterator pos, const T& value );
where T is the type you give as a template parameter, i.e. std::wstring in this case.
Iterators have operator+ overloaded, with the following semantics: iterator it + integer 2 returns a new iterator with a position 2 "increments" past the iterator it.
words.insert( it + position, word4 );
Suggestion
One thing to be careful about it how you determine an inerstion position.
I think it would be better practice (more maintainable) to use an iterator to "walk along" the vector, rather than using iterator+offset. If you're not very comfortable with iterators, this would be a chance to learn how to use them.
This would avoid a potential situation, discussed in a previous version of this answer, where you accidentally offset your iterator past the end of the vector, leading to a segmentation violation.

Equivalent of Enum for Strings

I studied enums which expects only integer inputs and returns corresponding value to it.I want to achieve same thing but I only have strings as a input. I want to make following work -
enum Types {
"Absolute", //"abs"
"PURE", //"PRE"
"MIXED" //"MXD"
}
and probable statment could be -
string sTpes = Types("abs"); //this should return "Absolute"
or
string sTpes = Types("MXD"); //this should return "MIXED"
If not using enums, please suggest me possible ways to achieve this.
Thanks.
There are no "string-enums", but to map from one value to another, you can use std::map, which is a standard template shipped with C++ platforms:
#include <map>
#include <string>
int main() {
using std::map; using std::string;
map<string, string> ss;
ss["abs"] = "Absolute";
const string foo = ss["abs"];
std::cout << ss["abs"] << ", or " << foo << std::endl;
}
In C++0x, if you want "safe" access that throws an exception if the key-type wasn't found, use map::at (actually, afair, the lack of map::at was just an oversight in the current standard):
std::cout << ss.at("weird keY");
or check if it exists:
if (ss.find("weird keY")==ss.end())
std::cout << "key not found\n";
if you are talking about c++/cli you could use this
Hashtable^ openWith = gcnew Hashtable();
// Add some elements to the hash table. There are no
// duplicate keys, but some of the values are duplicates.
openWith->Add("txt", "notepad.exe");
openWith->Add("bmp", "paint.exe");
openWith->Add("dib", "paint.exe");
openWith->Add("rtf", "wordpad.exe");
from http://msdn.microsoft.com/fr-fr/library/system.collections.hashtable.aspx#Y4406
else use map from stdlib.
I think you can also use CMAP from MFC, there is a good article about it here : http://www.codeproject.com/KB/architecture/cmap_howto.aspx
An enum has an integral value. Personally I simply suggest two conversion functions:
enum -> string
string -> enum
The first can be implemented with a simple array, the second require a binary search in a sorted list.
you could use a string array (of size 2) from string.h i think (either that or just string; one is for C and other is for cpp). first string is "abs" second is "absolute".
for example:
#include <string>
...
string abs[2]; //or a better name that's more relevant to you
abs[0] = "abs";
abs[1] = "absolute";
...
//pass it into the function
cout << abs[1] << endl;
...