Creating class with vector in class - c++

I have a class which includes a vector if strings so I can have infinite answers (I'm using this to study by essentially making mock tests). However, when I create thingy, it gets mad at me while trying to have values in the vector. I've tried a lot of ways to get it to work, but it can't.
#include <iostream>
#include <vector>
using namespace std;
string input;
class answer {
public:
vector<string> answers;
};
class qANDa {
public:
string question;
answer answers;
string correct;
};
void askQuestion (qANDa bob) {
cout << bob.question;
getline(cin, input);
input[0] = tolower(input[0]);
if (input == bob.correct) {
cout << "Correct!\n";
} else {
cout <<"Incorrect. Study more. Loser.\n";
};
}
vector<qANDa> thingys;
int main(int argc, const char * argv[]) {
qANDa thingy = {"The correct answer is \"A\". What's the correct answer.", {} "A"}
askQuestion(thingys.at(0));
}
I've tried putting strings inside the brackets, I've tried using parenthesis inside the bracket, I've put strings inside the parenthesis inside the bracket, but none of it works.

Your class answer cannot be initialized just from the empty brackets {}, you might give a default constructed rvalue reference though:
qANDa thingy =
{ "The correct answer is \"A\". What's the correct answer."
, answer()
, "A" }
Also note that at the point you're calling
askQuestion(thingys.at(0));
thingys contains no element. Change that to
qANDa thingy =
{ "The correct answer is \"A\". What's the correct answer."
, answer()
, "A"};
thingys.push_back(thingy);
askQuestion(thingys.at(0));

qANDa has three strings, so the initializer can look like {"one", "two", "three"}.
Oh sorry, I didn't see that the middle was of type answer, which is a vector<string>, not just a single string. If it were a single string the above would have worked. Just do e.g.
qANDa thingy = {"The correct answer is \"A\". What's the correct answer.", answer(), "A"};
Note also the added semicolon at the end.
There is a problem with the askQuestion code storing a character in input[0], when there's no guarantee that the global string-variable input has length >= 1.
To fix that, I suggest changing the type of input from std::string to just char.
Using a global variable to communicate a function result is fraught with dangers. Instead, consider using function results. You will find them discussed in your C++ textbook.

Related

How can I initialize an array of objects?

I've written this code but I have some errors when I try to initialize an array of Critter objects and don't know what they're about.
My code:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Critter {
private:
string crName;
public:
Critter(string = "Poochie");
string getName() const { return crName; }
};
Critter::Critter(string n) {
crName = n;
}
int main() {
Critter c[10] = { "bob","neo","judy","patrik","popo" }; //here
return 0;
}
The errors:
E0415 - no suitable constructor exists to convert from "const char [4]" to "Critter"
...
4 more like this.
This code worked on a friend's Visual Studio 2017, but not in mine which is the 2019 version.
Thanks.
The initialization you have is for an array of strings, for the objects you need:
Critter c[10] = {Critter("bob"), Critter("neo"), Critter("judy"),
Critter("patrik"), Critter("popo")};
Or
Critter c[10] = {{"bob"}, {"neo"}, {"judy"}, //(*)
{"patrik"}, {"popo"}};
*This second method is credited to #drescherjm comment followed by #dxiv's answer, both mentioned it first.
This second initialization may be what your friend used, and maybe you forgot the braces, the IDE version difference doesn't seem relevant here.
Note that C++ provides a container for fixed size arrays, std::array:
std::array<Critter, 10> c = {Critter("bob"), Critter("neo"),
Critter("judy"), Critter("patrik"), Critter("popo")};
On a side note:
You should avoid using namespace std;
Critter c[10] = { "bob","neo","judy","patrik","popo" };
This defines an array of const char *. To initialize an array of Critter with those strings, instead:
Critter c[10] = { {"bob"}, {"neo"}, {"judy"}, {"patrik"}, {"popo"} };
[ EDIT ] It was pointed out that the same answer was first posted in a comment, only it was hidden in an external link with no indication of what's behind it, which I did not see before posting the above. Credit goes to #drescherjm so I'll leave this here as a CW.
string c[10] = {"bob","neo","judy","patrik","popo"}; Would be correct.
{"bob","neo","judy","patrik","popo"} is an array containing string elements.
You need to do
Critter c[10]={ Critter("bob"),Critter("neo"),Critter("judy"),Critter("patrik"),Critter("popo")};
C++ only allows one "user-defined conversion" at a time.
You're providing char const[N]s (let's call them char const*s for the sake of argument), which need to be turned into std::strings (which count as "user-defined" in this context), which need to be turned into Critters.
That's two conversions, not one.
That's just a limitation of C++, I'm afraid. You'll have to temporarily instantiate either strings or Critters within that initialiser.

Passing a variable to function for reference an array into

I need to call function which contains many cases.
I'd like to pass it a variable which can reference different arrays.
This example demonstrates more clearly what I'm trying to achieve:
void bob(int debut, int fin, string flag){
string arrayflag = "pfhistory_FR_" + flag;
for (i = debut; i < fin; i++){
std::cout << arrayflag[i].DP << endl
}
};
If you want a variable which can reference different array's, you are require a 2D array. A 2D array can be thought of as an array of arrays, and you can index the arrays using a variable.
Roughly speaking C++ is a statically-bound language. This means that objects are resolved at compile time, not run time. As such, what you're trying to do is not possible in C++, but has to be implemented manually. Something like this:
void bob(int debut, int fin, string flag){
auto *arrayflag =
flag == "1" ? prhistory_FR_1 :
flag == "2" ? prhistory_FR_2 :
prhistory_FR_3;
for (i = debut; i < fin; i++){
std::cout << arrayflag[i].DP << endl
}
};
But this has all the markings of XY problem. You probably are asking something completely different, except that you think that this approach is the answer to your real question. But it's not. Whatever you're trying to do, the real answer is most likely something else. You just need to figure out what you're really asking.

Why doesn't while (*s++=*t++); work for me?

I came across this function on a blog and I found it really cool. I understand how it works conceptually since C++ was my first language.
However, when I tried actually writing it out in a program of mine, it doesn't seem to work. I've googled all over but I've only found explanations as to how it works so I've been really stumped.
Instead of copying the NULL char[5] (which should evaluate to false, copy nothing and break the loop), it gives two compilation errors, saying that "I can't increment value of type char[6]" (out-of-bounds error for both arrays).
Why doesn't my loop break at char[5]?
I'm guessing it's something to do with the subtleties of char and string, I tried initialising strings instead with cstring included, that didn't work either, the similar error of "cannot increment type string" shows up.
#include <iostream>
using namespace std;
int main () {
char s[] = "hello";
char t[] = "house";
while (*s++ = *t++);
cout << s;
return 0;
}
You can't increment an array. What would that even mean?
Try this:
#include <iostream>
using namespace std;
int main ()
{
char s[] = "hello";
char t[] = "house";
char *ss = s;
char *tt = t;
while (*ss++ = *tt++);
cout << s << endl;
}
The line " while (*ss++ = *tt++) " is always going to be true, because = is the assignment operator. You probably want " while (*ss++ == *tt"")", which compares equality.

Why I can't change char array attribute of a struct? [duplicate]

This question already has answers here:
Why can't I assign an array variable directly to another array variable with the '=' operator?
(5 answers)
Closed 9 years ago.
The question is very simple but I'm confused that why the struct is behaving like this as all of its members are by default public, have a look at following code
struct Student
{
char name[20];
}
int main()
{
Student s;
s.name="ahmed";
}
This code gives an error that name should be a modifiable lvalue.
But if I assign value in a loop char by char, then it works fine like this
s.name[0]='a';//works fine
Arrays in C are non-assignable and non-copy-initializable and in C++ this behavior was inherited. On the right-hand side arrays decay to pointers but in your case your copy-initialization is just not allowed.
If you try this:
{
char name[20];
name="ahmed";
}
you're actually copy-initializing the array - and that's disallowed.
The only exception is initializing them with a string literal
char c[] = "abc";
In your case you should rather use a function called strcpy to accomplish that:
{
char name[20];
strcpy(&name[0],"ahmed"); // Obviously make sure that there's enough space
}
It's worth noting that you can also use strings to accomplish this task:
#include<iostream>
#include<string>
using namespace std;
struct Student{
string name;
};
int main()
{
Student s;
s.name = "ahmed";
}
Structure is not a problem in your case. Arrays in C are not assignable.
Instead of s.name = "ahmed" , use strcpy(s.name,"ahmed")
name is an array, and you can't assign to an array name. Instead, you should use std::strcpy in cstring to copy the contents of the C-style string.
std::strcpy(s.name, "ahmed");
Note that the problem has nothing to do with the fact that name is part of a struct. You would have the same problem with a native char array.
You must use:
struct Student
{
char *name;
};
int main()
{
Student s;
s.name = "ahmed";
return 0;
}

Passing array of objects to a class

#include "average.c++"
#include "name.c++"
class Grade {
public:
Grade() {}
void searcharray(Name *array[]) {
int i;
for(i = 0; i <= 10; i++){
printf("%s", array->name);
}
}
};
int main() {
int i;
char line[64];
Name *names[10];
for(i = 0; i < 5; i++){
scanf("%s", &line);
names[i] = new Name(line);
}
Grade *test;
test = new Grade();
test->searcharray(names);
}
This code gives the error
"grade.c++ in member function 'void Grad::searcharray(Name*)':
grade.c++:11:25: error: request for member 'name' in ' array', which is of pointer type 'Name*' (maybe you meant to use '->' ?)"
I need help making this work. I am guessing it is something simple like extending the class like you would in Java but not sure how this works in c++.
I am assuming you can pass an array of objects to a class like you would in C with just an array.
The root to my question is to find a solution and to get a reason for this code being wrong.
Your code can be substantially improved by taking advantage of the Standard library. The problem with your initial code was that you were doing array->name where array was a C-style array (technically the pointer into which it decayed). An expression like that isn't possible unless you obtain the pointer at the index first:
array[i]->name;
Moreover, the for loop in which that line was written is traversing the array 1 too many times. The conditional statement i <= 10 should be i < 10 so you won't dereference an address past the end of the array.
Anyway, instead of showing your code with the corrections, I thought I might as well show you what it should look like if you use vectors, memory-management, and std::string. I hope this helps:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
class Grade
{
public:
Grade() { }
static void searcharray(const std::vector<std::unique_ptr<Name>>& array)
{
for (const auto& obj : array)
{
std::cout << obj->name;
}
}
};
int main()
{
std::string name;
std::vector<std::unique_ptr<Name>> names;
while (std::cin >> name)
names.push_back(std::unique_ptr<Name>(new Name(name)));
// names.push_back(std::make_unique<Name>(name))
Grade::searcharray(names);
}
Note that I also made searcharray static since it has nothing to do with a given instance of Grade.
As others have pointed out the problem is that you're using a parameter declared Name *array[] like array->name.
Remember that C++ built on top of C, which follows a rule 'declaration mimics use', which means that the way a variable is declared looks like the way it is used. So with the declaration:
Name *array[]
The way you get a name out of this is:
*array[i]
And name is a member of Name so you have to get a Name object first. Then you can tack on member access:
(*array[i]).name
And then you can use the -> shortcut where (*x).y is the same as x.y:
array[i]->name
Other issues:
Your code appears to be heavily influenced by the style of code required for the 1989 or 1990 version of C. You should try to avoid that as it makes writing C++ code much worse than it has to be.
You declare a Grade * and allocate it immediately. You can combine the declaration with initialization into:
Grade *test = new Grade();
But you don't need to use a pointer anyway: use Grade test; (and if you did need a pointer then you should use a smart pointer. Never use 'naked' new.)
Similarly you can avoid new when you create Names.
Name names[10]; // assuming that Name is default constructible
for(...) {
...
name[i] = Name(line);
}
You should avoid a fixed size array here. Instead you should default to using std::vector:
std::vector<Name> names;
for (...) {
...
names.push_back(Name(line)); // or in C++11 names.emplace_back(line);
}
You should declare the variable i as part of the for loop, not as a variable outside it:
for (int i=0; i<10; ++i)
When you read input you should avoid scanf and fixed sized buffers. Instead, if you're reading lines you should probably start off with std::getline and std::string.
std::string line;
while (std::getline(std::cin, line)) { // read as many lines as there are, not just 10 no matter what
names.emplace_back(line);
}