How can I initialize an array of objects? - c++

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.

Related

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;
}

Creating class with vector in class

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.

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);
}

assigning values to structure of a class using class object

I have got a very strange problem.
Here is my code:
Header File
namespace test
{
class LoadConfigData
{
struct control_params
{
char active[];
char suspended[];
char erased[];
}*ctrl_params;
bool loadConfig();
};
};
Main.cpp
using namespace std;
using namespace test;
namespace test
{
extern LoadConfigData *loadConfigDataobj;
LoadConfigData *loadConfigDataobj = new LoadConfigData;
};
int main()
{
loadConfigDataobj->loadConfig();
cout <<loadConfigDataobj->ctrl_params->active_status_code_v<<endl;
cout <<loadConfigDataobj->ctrl_params->suspended_status_code_v<<endl;
cout <<loadConfigDataobj->ctrl_params->erase_status_code_v<<endl;
return 0;
}
bool LoadConfigData::loadConfig()
{
std::string a = "AC";
std::string b = "SP";
std::string c = "ER";
LoadConfigData::ctrl_params = new LoadConfigData::control_params;
sprintf(loadConfigDataobj->ctrl_params->active,"%s",a.c_str());
sprintf(loadConfigDataobj->ctrl_params->suspended,"%s",b.c_str());
sprintf(loadConfigDataobj->ctrl_params->erased,"%s",c.c_str());
return true;
}
Output:
ER
ER
ER
Which means that it's printing the last copied string for each struct member.
What is wrong in my code.
The problem is that you don't give the character arrays a size:
struct control_params
{
char active[];
char suspended[];
char erased[];
}*ctrl_params;
I'm rather surprised that this compiles; my understanding was that an unsized array was an incomplete type, and so couldn't be a non-static class member. However, my compiler at least (and presumably yours) treats these as arrays of size zero, all at the same location in memory. Therefore, each time you write to one it overwrites whatever you wrote to the others. (Of course, the behaviour is undefined since it writes outside the array bounds).
The simplest solution is to use std::string to represent the strings. That will manage its size automatically, and you can simply write active = a rather than messing around with sprintf (or, slightly more sensibly, strncpy) and hoping you don't get a buffer overrun.
The problem is your usage of sprintf. sprintf will not allocate any memory for you, active, suspended and erased are pointing to strange and undefined addresses. You are invoking undefined behaviour.
Without adding all the normally necessary detail, use std::string and it's operator overloads instead. For simple parsing, use streams. For now, totally avoid any C-string-function, and instead use C++ solely.

C++ Global Array of Queues with variable size

I am working on a project that requires a class QueueArray that is an Array of Queues. It's been a while since I worked with c++ Arrays so I'm having some trouble debugging why my code is throwing errors.
I read Delete an array of queue objects for some inspiration (along with a couple hours on Google), but I am still having errors with the following code:
#include <iostream>
#include <deque>
#include <queue>
using namespace std;
class QueueArray
{
queue<int> theArray[];
QueueArray::QueueArray(int size)
{
queue<int> theArray[] = new queue<int>[size];
//theArray[] = new queue<int>[size]; //this may be closer, but also giving errors
}
};
the errors are:
warning C4200: nonstandard extension used : zero-sized array in struct/union
1> Cannot generate copy-ctor or copy-assignment operator when UDT contains a zero-sized array
and
error C2075: 'theArray' : array initialization needs curly braces
I've read up about the 2nd error, but I can seem to figure out what I need to do to fix it.
I need it to be a variable sized array, with the variable passed to the class, which is why I can not initialize the size of the array up top, and It must be of global scope so I can use it in other functions within the class (the classes can't be passed the array through a parameter).
Later on, the queues will be of a user defined type, but we're letting them be queues of ints right now, not sure if that makes a difference. I keep seeing people suggesting the use of vectors in these cases but I don't have a choice on this one.
Any suggestions would be appreciated.
queue<int>* theArray;
not
queue<int> theArray[];
When allocating an array on the heap you get back a pointer to the start of the array, rather than an array. Thus, you want to declare your array as
queue<int>* theArray;
The use of empty brackets is allowed in some contexts but has specific meaning. It can be used when initializing a statically size array:
queue<int> theArray[] = { queue<int>(), queue<int>() /*...*/ };
creates an array of a size matching the list of initializers and it still can be empty. You can also use empty brackets in the argument list of a function in which case the use is equivalent to using pointer notation, e.g.:
int main(int ac, char* av[])
is identical to
int main(int ac, char** av)
using variable sized arrays is not possible in C++. to make your code working, use a pointer, i.e.
class QueueArray
{
queue<int> * const theArray;
QueueArray::QueueArray(int size)
: theArray (new queue<int>[size])
{ /* ... */ }
~QueueArray()
{ delete[] theArray; }
};
However, in C++ you should really avoid this and use a std::vector instead, i.e.
typedef vector<queue<int>> QueueArray;