Passing array of objects to a class - c++

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

Related

Initializing a Struct's variable const char* const* with C++

I am trying to solve a coding question that requires the results be returned using a given struct. The struct is defined as:
struct Answer
{
const char* const* lastNames;
unsigned numberOfPeople;
}
Where the lastNames is a pointer to last names that are each terminated by a non-alpha char. I can not seem to find any way to convert the vector of strings that I am using to compile all the last names into a variable that I can assign to lastNames. I have tried making a single string with all the last names and assigning it with c_str() like so:
Ans->lastName = allNames.c_str(); but this gives me an error. Due to the limitations of the question I am unable to change the struct variable to anything else. How can I assign a string to a const char* const*
The structure being used effectively uses a C-style approach to defining a variable sized array of pointers to char (with const sprinkled over it). You’ll need storage for both the array of char const* as well as the entities pointed to. Here is how you could build it from a std::vector<std::string>:
std::vector<std::string> strings = somehow_compute_the_strings();
std::vector<char const*> array;
for (std::string const& s: strings) {
array.push_back(s.c_str());
}
Answer answer = { array.data(), array.size() };
Of course, you can’t return answer without the pointer inside pointing to stale data: you’d need to keep the two std::vectors alive. Potentially these two objects could be made members of an object the function is called on. To actually return an object of type Answer without a place to hold on to the std::vectors you could allocate the relevant entities and accept that the result will yield a memory leak unless the caller can clean the result up.
You can't just cast stuff. struct Answer is expecting a char**, so you are going to have to build it and keep it valid as long as the struct Answer is in use. At least they were kind enough to let us know they don't intend to modify it or mess with cleaning up the memory, since it takes "const char * const *".
#include <iostream>
#include <vector>
#include <string>
#include <assert.h>
typedef std::vector<std::string> VectorOfStrings_type;
struct Answer
{
const char* const* lastNames;
unsigned numberOfPeople;
};
class AnswerWrapper
{
private:
// construct and maintain memory so the pointers in the Answer struct will be valid
char ** lastNames;
unsigned int numberOfPeople;
public:
AnswerWrapper(const VectorOfStrings_type &input){
numberOfPeople = input.size();
// create the array of pointers
lastNames = static_cast<char**>(
malloc(numberOfPeople * sizeof(char*))
);
// create each string
for (unsigned int i = 0; i < numberOfPeople; ++i){
const std::string &name = input[i];
// allocate space
lastNames[i] = static_cast<char*>(
malloc(name.size() + 1)
);
// copy string
strncpy(lastNames[i], name.data(), name.size());
// add null terminator
lastNames[i][name.size()] = '\0';
}
}
operator Answer (){
return Answer{ lastNames, numberOfPeople };
}
~AnswerWrapper(){
// critcally important, left as an exercise
assert(0);
}
};
void SomeFunctionWhichUsesAnswer(Answer a){
// presumably you have some legacy C code here
// but here's a quick and easy demo
for (unsigned int i = 0; i < a.numberOfPeople; ++i)
std::cout << a.lastNames[i] << std::endl;
}
int main() {
// Here is your vector of strings
VectorOfStrings_type myData { "custom formatted data goes here", "and more here", "and again" };
// You must construct a buffer for the "Answer" type, which must remain in scope
AnswerWrapper temp{ myData };
// AnswerWrapper is currently in scope, so inside this function, the pointers will be valid
SomeFunctionWhichUsesAnswer(temp);
}
Also, I noticed that the strings in Answer are not referred to as null terminated. That is a separate issue you can take care of.
A const member variable can only be assigned in the constructor.
if you can add to the struct, define a constructor, and use the : lastname(value) syntax; or use the struct Answer myVar{value,number}; initialization, right where you declare your instance.
Another - ugly, dangerous, and frowned upon - alternative is a cast: (char**) lastname = value;, or in C++ syntax reinterpret_cast<char**>(lastname) = value.
If someone is teaching you either of those approaches, change the teacher.

Array of pointers to structs

EDIT: Im quite new to c++ and programming as a whole.
I'm supposed to make a program where i use stucts and and an array of structs.
Security council < > Member of Security council
My task was to use the concept of "UML aggregation" to create a program where I use structs and struct arrays. (I hope you understand what I'm trying to say)
Since a Member of a Security council is a part of a Security council, and not the other way around, the struct of Security council must have an array of its members.(bear with me)
//example
struct Member_sc{
char * name;
int age;
};
struct Security_council{
Member_sc members[10];
};
Now, I've created this program and everything works perfectly (according to my teacher), but now she told me create an exact copy, but instead of the "members" array I must use an array of pointers to the Member_sc structs. Since I havent completely figured out how pointers work, I have come across some problems.
I can post the code to the original program if needed, but it contains 4 files(main, header, and some function files) and it would be a pain to try and post it here.
here is the prototype (all in one file, for now)
#include <iostream>
using namespace std;
struct member_sc{
string name;
};
struct security_council{
member_sc *point;
security_council **search; // ignore this for now
int n;
security_council():n(0){}
};
void in_mem( member_sc &x){
getline(cin,x.name);
}
void out_mem(member_sc &x){
cout<<x.name<<endl;
}
void in_SC(security_council &q, member_sc &x){
int num; //number of members
cin>>num;
for(int i=0; i<num; ++i){
in_mem(x);
q.point[q.n]=x;
q.n++;
}
}
void out_SC(security_council &q,member_sc &x){
for(int i=0; i<q.n; ++i){
out_mem(q.point[i]);
}
}
int main(){
member_sc y;
security_council x;
in_mem(y); // works
out_mem(y); // works
in_SC(x,y); // crashes after i input the number of members i want
out_SC(x,y); //
system("pause");
return 0;
}
The program crashes after you input the number of members you want in your Security council.
Is my way of thinking right? or should I use dynamic memory allocation?
in addition to that (my teacher gave me an additional task) create a search function using pointers. i thought that pointer to pointer may be good for that, but im not sure.
Any help or advice would be greatly appreciated.
( i think ill figure out the search thingy once i figure out how pointers to structs work)
The first part of your issue is this:
cin >> num;
this reads only the digits that have been typed and stops at the newline. Then, in in_mem the call to getline immediately reads a newline. You need to do:
cin >> num;
cin.ignore();
this will drain the input stream of any remaining input, or catch up so to speak.
However your core problem is that you don't allocate any memory for "point" to point to.
A pointer is just a variable holding a value that happens to be the address (offset from 0) of a thing in memory. If you are going to the airport and write "Gate 23" on a post-it note, the post it note is a pointer and "Gate 23" is the value.
In your code, that variable is uninitialized and will either be 0, if you are lucky, or some random address in memory if you aren't so lucky.
To the airport analogy: you arrive at the airport and find that your post-it note has "pizza" written on it. Not helpful.
Your teacher has actually specified an "array of pointers". Break that down: pointer to what? member_sc, that's member_sc*. And now make it an array
member_sc* pointers[10];
NOTE: This is not good, modern C++ - in modern C++ you would use something called a smart pointer (std::unique_ptr) probably.
std::unique_ptr<member_sc[]> pointers(new member_sc[10]);
Now you have 10 pointers instead of just one, and all of them will need some allocation to point to. The easiest way to do this is with the new keyword and the copy constructor:
for (int i = 0; i < num; i++) {
in_mem(x);
pointers[q.n] = new member_sc(x); // make a clone of x
q.n++;
}
or in modern C++
for (int i = 0; i < num; i++) {
in_mem(x); // x is temporary for reading in
pointers[q.n] = std::make_unique<member_sc>(x);
q.n++;
}
However there is a limitation with this approach: you can only have upto 10 security council members. How do you work around this? Well, the modern C++ answer would be to use a std::vector
std::vector<member_sc> members;
// ditch n - vector tracks it for you.
// ...
for (int i = 0; i < num; ++i) {
in_mem(x);
q.members.push_back(x);
// q.n is replaced with q.members.size()
// which is tracked automatically for you
}
but I'm guessing your teacher wants you to actually understand pointers before you get to forget about them with modern luxuries.
We need to re-use the pointer stuff we've just used above and change "pointers" to an array of pointers.
Which means we're going to want a pointer to a set of pointer-to-member_sc.
member_sc** pointers;
We'll need to assign some memory for this to point to:
cin >> num;
cin.ignore();
if (num == 0) {
// do something
return;
}
pointers = new member_sc[num];
luckily, using a pointer to an array is as easy as using an array, the only major difference being that you lose the size-of-array information -- all you have is the address, not the dimensions.
for (int i = 0; i < num; i++) {
in_mem(x);
q.pointers[i] = new member_sc(x);
q.n++;
}
I'm deliberately not providing you with a complete working example because this is obviously for a class.
You never initialize the memory that the point member refers to, yet then in statement q.point[q.n]=x; you attempt to use it.
Basically, after you read in the number of members, and before the for loop where you read in the individual members, you need to allocate an array of an appropriate number of member_sc objects and store it in q.point. Don't forget to free this memory when done using it.
Once you do that, you can also remove the member_sc &x argument from both in_SC and out_SC, as that will become unnecessary.
Finally, some validation of your input seems to be in place. Consider what will happen if the user enters a negative number, and you attempt to use that directly to determine the size of memory to allocate.
Here's a simple example showing how to use a dynamically allocated array of structures:
#include <iostream>
#include <string>
struct member_sc {
std::string name;
};
void test_array(int count)
{
if (count <= 0) {
return; // Error
}
// Allocate an array of appropriate size
member_sc* members = new member_sc[count];
if (members == nullptr) {
return; // Error
}
// ... fill in the individual array elements
for(int i(0); i < count; ++i) {
// ... read from input stream
// I'll just generate some names to keep it simple
members[i].name = "User A";
members[i].name[5] += i; // Change the last character, so we have different names
}
// Now let's try printing out the members...
for(int i(0); i < count; ++i) {
std::cout << i << ": " << members[i].name << "\n";
}
delete[] members;
}
int main(int argc, char** argv)
{
for(int count(1); count <= 10; ++count) {
std::cout << "Test count=" << count << "\n";
test_array(count);
std::cout << "\n";
}
return 0;
}
Example on Coliru
Of course, there are many other issues with this style of code, but I believe that's beside the point of this question. For example:
Instead of using bare pointers, it would be more appropriate to use some kind of a smart pointer.
Instead of a simple array, use some kind of collection, such as a vector.
Since you are asked to use an array of pointers, do so: replace
Member_sc members[10];
with
Member_sc* members[10];
Then fill out that array using dynamic memory allocation. As a matter of good form, at the end of the program remember to release the dynamic memory you have used.

Changing variable name inside a loop

I'm trying to create a loop that will will create a new variable but also change the name of the variable, such as increasing in value, automatically. Not sure if this is possible because you cant have dynamic variables?
if (cin.get() == '\n')
{
m ++; // Add an integer to m
string (1 + m); //Trying to name the string the value of m + 1, i.e 3
cin.ignore():
getline(cin, (1 + m))
myfile << (1 + m) << endl;
}
That is my current code which is full of errors but hopefully readable enough to gain an understanding of what I'm trying to do.
Bad title, don't know what else to call it.
You're misunderstanding C++ if you try to "rename a variable".
C++ is a compiled language in which the names of the variables only exist in your source code as a means of handling them, but not in the compiled program.
You hence can't also create a variable with a specific name at runtime -- variable names simply don't exist then.
You probably want some kind of container that maps values to keys, think of a dictionary, where you can say "for this integer value 12, I store the string monkey" or so. Have a look at std::map.
Now, you're really not making much sense right now; and try to do things that aren't really the way C++ works for anyone who learned it in a ordered manner. I'd really recommend getting a C++ book or tutorial and start with that. It's going to turn out to safe you a lot of time! Here's a list of recommended C++ books and ressources.
You can't. It's not possible in C++.
To declare and initialize a variable in each iteration is however possible:
for (int i = 0; i != 10; ++i) {
int var = i; // Declare 'var' and assign value of 'i' to it.
} // 'var' object goes out of scope and is destroyed
Names are visible from the point where they are declared until the end of the scope in which the declaration appears. Names have scope and Objects have lifetimes.
The closest thing to "renaming" in C++ would be declaring a reference of the "old named" variable with the new name.
int a = 2;
int &b = a;
//you can now call either `b` or `a` to get the value
Creating new variables on the fly is not possible. The correct insert any compiled language name here way of doing it is by pushing values into a container (array, map, stack..etc). With maps, you can do something similar to what you want, but it's different one only "maps" a string to a value.
#include <map>
#include <string>
#include <sstream> //for int to string conversion
std::string stringify(int val)
{
std::stringstream ss;
ss << n;
return ss.str();
}
int main()
{
std::map<std::string, int> values;
for (int i = 0; i < 10; i ++){
values[stringify(i)] = i*100;
}
std::cout << values["1"]; // prints 100
std::cout << values["9"]; // prints 900
return 0;
}

cpp(15): error C2182: 'input' : illegal use of type 'void'

after making a fresh start on a new program i made for learning how arrays work in combinatio0n with void ive ran into the following problem.
cpp(15): error C2182: 'input' : illegal use of type 'void'
Does anyone know what causes this? I am new to the concept of void and array.
#include "stdafx.h"
#include <iostream>
using namespace std;
void input (int x );
int main()
{
int x = 0;
int a[ 5 ];
input ( a[ 5 ]);
{
void input(x);
for(int i = 1; i < 5; i++) {
cin >> a [ i ];
}
cin.get();
cout << a [ 3 ];
cin.get();
}
}
Your code has many problems with it. It's just not valid C++ as it is. Remember that C++, like any other programming language, is unforgiving when it comes to syntax. If it's not exactly the right syntax, it's not going to compile. You can't just write what you think makes sense. You need to learn the correct syntax and apply it.
It looks like you want everything from the for loop to the last cin.get() to be part of a function called input. To do that, you need to use the appropriate syntax for defining a function and you need to do it outside any other functions:
void input(int x) {
for(int i = 1; i < 5; i++) {
cin >> a [ i ];
}
cin.get();
cout << a [ 3 ];
cin.get();
}
This still has a problem though. The parameter type is int, yet it looks like you want to pass the entire array:
void input(int x[])
Note that this is not actually an array type parameter, but is really a pointer. When you pass an array to this function, x will be a pointer to its first element. The [] is just a convenient syntax.
Then, instead of passing a[5] to the function (which is an element that does not exist, since only a[0] to a[4] exist), you should be passing just a:
input(a);
You also loop from 1 to 4 - I'm not sure if this is intentional. If you want to input a value for each element of the array, you should be looping from 0 to 4.
You're going to have more errors after resolving the current one. Here's some quick pointers that may help. I don't want to just give you a solution because you are still learning and that won't help:
void as a keyword refers to the "nothing type" and is used in functions to denote having no return value
curly braces {} denote scope and can be used to define the body of a function, loop, or control statement
Functions themselves need to be declared and defined. The definition, or body of the function, can be later on in your code but the declaration needs to be present before you call it
Here's an example program to illustrate basic function parts:
#include <iostream>
// declaration
void Welcome();
int main()
{
// function call
Welcome(); // displays "Hello World"
return 0;
}
// definition
void Welcome()
{
std::cout << "Hello World" << std::endl;
}
More on functions
As far as arrays they are basically a contiguous block of memory large enough to hold a given amount of the same type. Here's a few things to remember about arrays:
They work with integral types as well as objects but are usually used for plain old data. e.g. int intArray[5]; is an array of 5 int types.
The index starts at 0 meaning intArray[0] from previous example is the first integer.
Using the array operator you can get and set values e.g. int last = intArray[4]; or intArray[0] = -1;
More on arrays
Check out the other answers for more on how to pass arrays as parameters but I also recommend picking a Good C++ Book ;-)

C++ Passing pointer constant as parameter from main to method

I am attempting to initialize variables within my object, using a function with const pointers as parameters.
I keep getting errors in many of the ways i attempted, here is my code:
class Molecule
{
private:
char s[21];
char d[21];
double w= 0;
public:
Molecule();
void set(const char*, const char*, double);
void display() const;
};
int main() {
int n;
cout << "Molecular Information\n";
cout << "=====================" << endl;
cout << "Number of Molecules : ";
cin >> n;
Molecule *molecule = new Molecule[n];
for (int i = 0; i < n; i++) {
char symbol[21];
char description[21];
double weight;
molecule[i].set(&symbol,&discription,weight);
//...
}
//implementation of class
#include "Molecule.h"
#include <iostream>
#include <cstring>
void Molecule::set(const char*, const char*, double)
{
s = &symbol;
d = &discription;
w = &weigth;
}
My question is: How would i correctly call the member function from an array of objects, using constant chars as parameter, and what is the correct way to set them to my variables in my class.
P.S: I have been trying to figure this out for a long time, and posting here is a last resort.
There are multiple errors in your code
&symbol (where symbol is char[21]) yields char(*)[21], use symbol directly and let it decay to char* or use explicitly &symbol[0]
double weight; is uninitialized local variable, using it results in undefined behavior - you should initialize it: double weight = 0.0;
double w= 0; used to declare a member of class Molecule is invalid, you could use constructor's initializer list:
Molecule() : w(0.0) { } // initializes `w` to `0.0`
s = symbol; where s is char[21] and symbol is char* will not copy strings, for C-style copying strcpy could be used (note that C and C++ are different languages)
you have called new[] so it would be nice and appropriate to call delete[] as well and instead of relying on OS cleaning it up after the program terminates: (otherwise follow the point 6)
Molecule *molecule = new Molecule[n];
...
delete[] molecule;
If you are allowed to use vectors, replace Molecule *molecule = new Molecule[n]; with std::vector<Molecule> molecules(n);
If you are allowed to use std::string1) objects, replace char[21] / char* with std::string objects
Other suggestions:
use meaningful names for variables, if you want to explicitly distinguish private members from other local variables, good convention is to use _ at the end of the name:
class Molecule {
private:
std::string symbol_;
std::string description_;
double weight_;
1) Basically what you need to know about std::string is that it is a template that wraps raw char* and it already contains well-defined copying, concatenation using operator + and most important: you don't need to bother with memory management. Just #include <string>
In the call
molecule[i].set(&symbol,&discription,weight);
you are passing a pointer to a char array. This does not match the char* that set expects.
The easiest/best fix is to change this to
molecule[i].set(symbol,description,weight);
relying on the symbol and description char arrays automatically decaying to pointers.
Alternatively, you could also write
molecule[i].set(&symbol[0],&description[0],weight);
to explicitly pass char*
[Note that there are many other errors in the code posted. Based on the question, I'm guessing they are just typos. Please update your question if you'd like more info onn any of the other errors.]