How do I write setters and getters for an array? (c++) - c++

Im writing a class within c++, however I am not certain on how to create the setters and getters for the arrays (sorry for it being a basic question!) I am getting the following error:
expected primary expression before ']' token
Here is my code:
Class planet: public body
{
private:
string name[];
string star[];
public:
void nameSetter (string h_name[])
{
name[] = h_name[];
}
};
Once again I am sorry for such I silly question, I know I am not passing an index through, however, when I create an index it throws up a large amount of errors!

string name[];
This is not an array, it is a pointer. Use vectors instead:
#include <vector>
class planet: public body
{
private:
vector<string> name;
vector<string> star;
public:
void nameSetter (const vector<string> &h_name)
{
name = h_name;
}
};

Arrays in C++ have compile-time fixed sizes. You can't have a declaration like string name[]; because it leaves the size empty. You can't do that unless you provide an initialization list from which the size is determined.
In addition, array type arguments are transformed to pointer arguments. So your string h_name[] argument is actually a string* h_name.
name[] = h_name[];
This line doesn't make much sense. It's almost like you're trying to access elements of name and h_name without giving an index. Perhaps you were intending to assign the h_name array to the name array, like so:
name = h_name;
However, as we've just seen, h_name is actually a pointer. And in fact, you can't assign to an array anyway, so even if h_name were an array, this still wouldn't work.
You'll be much better off using a standard container like std::vector. It appears that you want dynamically sized arrays anyway, so this will make that easy.

Even though an answer has been selected, I think maybe the original question may have been misunderstood.
I think what the OP intended was that each instance of planet should have 1 name and 1 star; so the array notation he's used in his code is a misunderstanding on his part about arrays and strings. Based on this assumption I will continue.
When you declare
string name[];
I believe you just want to hold the name of 1 planet, in which case you don't need and array, you just need a single string.
ie
string name;
The same goes for star.
This would make the code
Class planet: public body
{
private:
string name;
string star;
public:
void nameSetter (const string& h_name)
{
name = h_name;
}
};

Related

How can I send a struct of arrays into a function when using the String object?

I'm learning to code c++ and I've come to this problem:
I have this struct:
struct storeData
{
string name;
string username;
string fav_food;
string fav_color;
}data[30];
And I need to check if two usernames are equal so I made this statement:
for(i=0;i<c;i++){
if(data[c].username.compare(data[i].username)==0){
cout<<"Username already taken"<<endl;
}
}
And it works well, the problem that I have is that I'm required to make a function let's call it: isTaken that returns the error message, so I can use it whenever I need to for example delete a username so I don't have to copy/paste the code again.
So I began looking for an answer for that, many forums present a way to send the whole struct like this:
void isTaken(struct storeData *data)
which I understand but because I'm using string is not working, so I guess it's because string is an object? I'm using the library <string> I'm sorry if I'm not being that clear at the moment, I'm looking for a way to use isTaken(data[c].user); but I don't know how to declare the function, I think is also because string is not the same as C string but I'm not really sure I've been looking for a solution and could not find it.
I tried: void isTaken(struct storeData *data) but I got an error saying that I can't convert std::string to basic_string which makes sense if I'm correct about string I tried converting string into c string but could not get anywhere. I'm open to suggestions/corrections because I want to improve my code, also I could not find the answer here, so If someone's got a link to a problem like this please let me know.
Thank you so much for you time, have a good day.
Do you mean an array of structs instead of a struct of arrays?
In the example you are giving I see only an array of structs each of which has multiple string objects in it. You see, a string is a class coming from std and I wouldn't call it an array. If you want to know how to pass an array to a function, you should read about it (I'm sure you can find such a question in SO). If you want to have an array within your struct, then the struct will take care of the memory of the array, but you should definitely read about constructors.
You got an error because you are passing an string argument to a function which requires struct pointer
void isTaken(struct storeData *data);
...
isTaken(data[c].user);
but what you actually need is to have a function which takes an array of your users, its size and username you want to check
bool IsUsernameTaken(struct storeData data[], int dataSize, const string &username){
for(int i = 0; i<dataSize; i++){
if(username == data[i].username)
return true;
}
return false;
}
A C string looks like this
data
A C++ string usually looks like this
size
capacity
ptr
|
v
data
or if using short string optimization and the string is short enough
size
data
data
all are zero terminated.
Making a shallow copy a C string only cost the copy of the pointer to it. Where a copy of a might cost just copying the 3 members and possible an allocation of data, which is not ideal, therefor most C++ functions use a reference to a string making the cost equivalent to the C string.
All code is untested.
bool Find(const std::string& target);
Making a deep copy of a C string would also cost an allocation.
In C++ you have many options to do a search, for your struct it could look like this. In case your member variables are private you must use an access function
auto found = std::find(std::begin(data), std::begin(data)+c, [&target](const storeData& auser) { return auser.GetName() == target });
return (found != std::begin(data)+c);
The first two parameters are the range that is search, not including the 2nd. A lambda is used to check the name, a free function with the right declaration would also do.
std::string& GetName() { return name; }
The higher C++ protection schemes would advice adding 2 consts to that in case you don't need to change name.
const std::string& GetName() const { return name; }
Meaning the returned string cant be changed and the 2nd says it wont change anything in your class. This const version would be required as I used a const storeData& auser in the lambda to satisfy the constness of the struct.

Defining a character array in header file in C++

I am working on a project where a question is displayed in a game window.
Since this question will need to change a lot, I figure it would be easier to have 5 defined lines of text (1 for question, 4 for MC answers) that are simply edited every time the question changes.
I have tried this in the header file:
struct tagQuestion{
int x, y;
const char* qLine[150];
char ansA[150];
char ansB[150];
char ansC[150];
char ansD[150];
}question[1];
then in my main.cpp
question.qLine[150] = "TEST PHRASE";
but it is returning the error "qLine" in "question", which is of non-class type "tagQuestion[1]"
I have tried both char and const char* to no success.
I am trying to follow an example in my textbook and I think I'm misunderstanding it.
Once I declare the character array in the header file, can't I edit its contents in the main file?
That [1] after question doesn't seem to be making any sense. Remove it.
Your answers are arrays of char, but your question is an array of char pointers. I am pretty sure you don't want your question to consist of 150 char pointers.
If you want to change qLine, don't make it const.
qLine[150] = ... This assigns a value to the 151st element of an array with 150 elements. Neither what you want, nor legal code. You probably want qLine =, but that doesn't work with an array, instead you need to use a function like strcpy that writes data into the array, but I highly advise against that. (see next point)
Don't do C in C++. Use std::string, not arrays of char.
If you have strings that regularly change, create a file where your strings are stored, load them into a map, and pull them from the map wherever and whenever you need them, instead of putting hardcoded strings in your code. That way you don't need to change any code, when you change text.
I suggest something like this:
struct Question {
std::string text;
std::vector<std::string> answers;
};
std::map<std::string, Question> questions;
And then something like this:
ifstream questionsFile("questions.txt");
if (questionsFile.is_open())
{
std::string line;
while (std::getline(questionsFile, line))
{
// Split line into some id, text and answers, how exactly depends on the format you chose
std::string id = ...;
Question question;
question.text = ...;
question.answers.push_back(...);
questions[id] = question;
}
questionsFile.close();
}
else
{
// Handle error
}
And wherever in your code:
// Print question with id "q1":
std::cout << questions["q1"].text << std::endl;
// Print answer #2 for question with id "q1":
std::cout << questions["q1"].answers[1] << std::endl;
Even better would be to make Question a class that hides its internals and has accessor methods and a constructor or static method to create new instances from valid lines with proper error handling.

Class containing strings - Can I skip initialising them and if so how?

happy coders!
I had a plan to try to learn some C++ today and so I thought I could take an old C assignment from a previous course and just do the same thing in C++. The assignment is to read music files and retrieve data from their ID3 tags and sort them in folders according to their artist, album and track title etc etc... this does not really matter but you know at least what I'm going for.
So I played around a little with sets and made my program receive an array of strings specifying different songs which it will loop the algorithm over.
In this next step I got stuck though due to how I tried to copy the behaviour of my old C program which was a struct containing three values being:
int size;
char *tag_name;
char *data;
but so far I have been unable to recreate this dynamic behaviour in C++ where I wish to only have the members defined but not initialised since I wanted to be able to change this data later on. Technically I can do this in a way where I get the data from the file before I create the tag, and therefore give the constructor their initial values and be done with it. But can I do it in the way I want to?
class Tag {
public:
std::string name;
std::string data;
int size;
Tag() {}
Tag(std::string n, std::string d, int s) : name(n), data(d), size(s) { }
void setData(std::string data) { this.data = data }
};
Since I've tried a billion combinations of pointers and whatnot (Googled loads) I just returned to the above and decided to ask you how to actually accomplish this.
My brained is completely mashed but consider the above psuedo code since I bet it is not correct in any way...
So my question is:
How do I define a class so that I get a dynamic string allocation for the members name and data? I was almost thinking of using some good old char* but the point of me trying this was to learn some C++, so I am forcing myself to go through this now.
If I understand your question correctly, your default constructor already takes care of this. Your std::strings will initialize to empty string "". You can assign a different value to
this string at any time.
If you really wanted to, you could change your default constructor to
Tag() : name(""), data(""), size(0) {}

String pooling/interning - Is this good practice?

As an example, I have a class which I'm storing some information about in binary files:
class car {
char car_manufacturer;
//other stuff
};
Where the value of car_manufacturer is one of the values in:
enum car_manufacturers : char {
VOLVO = 0,
AUDI,
MERCEDES
};
Now in this example application, the user will want a string representation of their car manufacturer rather than a number so I create an array with the string representations of the enums where the ordering of the array is the same as the enum, so the car_manufacturer can be used as an array index
std::string car_manufacturers_strings[MERCEDES + 1] = {
"Volvo",
"Audi",
"Mercedes"
};
So now after loading the file and creating a car object out of the data, I can get the make of the car, as a string simply by car_manufacturers_strings[car.car_manufacturer];
The advantage of this being that, in the files I don't have to store a bunch of repeated strings, if the cars were the same make thus I save a lot of space. But the drawback of this is that the code is slightly more complex.
So is this good or bad practice?
This is pretty standard practice to provide string representations of enumerators as an array.
One point is that:
std::string car_manufacturers_strings[MERCEDES + 1] = {
"Volvo",
"Audi",
"Mercedes"
};
Stores string literals in the binary, as well as creates copies of those literals as std::string objects at dynamic initialization phase.
You may like to change it to:
char const* const car_manufacturers_strings[MERCEDES + 1] = {
"Volvo",
"Audi",
"Mercedes"
};
So that it does not unnecessarily create those copies.
I generally do it with a function, along the lines of:
char const * to_string(car_manufacturers cm) {
switch (cm) {
#define CASE(CM) case CM: return #CM;
CASE(Volvo)
CASE(Audi)
CASE(Mercedes)
#undef CASE
}
return "Unknown"; // or throw, or whatever
}
Advantages:
no need to convert the argument into a numeric index, so it works with scoped enumerations without need for hideous casting;
my compiler gives a warning if I add an enumerator without updating the function (not as good as removing the duplication, but at least consistency is enforced)
Disadvantages:
switch may (or may not) be less efficient than an array lookup
Capitalisation of the enumerators has to match the strings, so you'd have to drop your fetish for SHOUTY_CAPS. Some would say that's a good thing; they certainly hurt my eyes.
Ewwww! Macros!

Segmentation fault on instationation of more than 1 object

I have a class called "Vertex.hpp" which is as follows:
#include <iostream>
#include "Edge.hpp"
#include <vector>
using namespace std;
/** A class, instances of which are nodes in an HCTree.
*/
class Vertex {
public:
Vertex(char * str){
*name=*str;
}
vector<Vertex*> adjecency_list;
vector<Edge*> edge_weights;
char *name;
};
#endif
When I instantiate an object of type Vector as follows:
Vertex *first_read;
Vertex *second_read;
in.getline(input,256);
str=strtok(input," ");
first_read->name=str;
str=strtok(NULL, " ");
second_read->name=str;
A segmentation fault occurs when more than 1 object of type Vector is instantiated. Why would this occur if more than 1 object is instantiated, and how can i allow multiple objects to be instantiated?
*name=*str;
You cannot dereference a pointer until you first make it point to something.
You probably meant something like:
Vertex(char * str) {
name=strdup(str);
}
But you should really be using std::string.
I think the way how you copy strings is wrong.
*name=*str;
Both name ans str are of type char*. You are dereferencing those pointers. This means you look at the position at the memory where they point and interpret it as char.
When you call it first time something is at location pointed by str and first character of it is copied to random address (as you never initialized name).
Second time you are not so lucky. strtok called on NULL return NULL strtok at cplusplus
Now you tried to work with memorry pointed by null pointer and it is bad.
You need to allocate memory for name and use proper copy function.
name = new char[SomeMaxLenght];
strcpy(name, str);
That's a very C way of doing things, which is incredibly non recommended in modern C++. Remember, C++ should be treated as a different language, not a strict superset of C.
First things first, you should really get a good book by looking at that list, as you seem to be missing many basics.
As for your problem, the main issue is that name is uninitialized, so you run into what is called undefined behavior (i.e. anything can happen; in your case it crashes on the second instantiation). I could go in depth on how to fix it by dynamically allocating memory, but why bother? Just use a std::string:
class Vertex {
std::string name; // string instead of char *
public:
Vertex(const std::string &str) { // pass by const reference
name = str; // I should really use an initializer list there, but oh well
}
// the rest of the class is the same
};
See how that's simpler? Now you don't have to mess around with pointers, which are painful to use. So, in short: use the standard library. And get a good book. Really.