How to return an array in method decleration using C++? - c++

I am trying to write C++ code suitable for object oriented programming.
I have two classes, namely, Student and Course. In the Student class, I have quiz_scores which is a 1-D array of 4 integers. I need both set and get methods, both are used in natural common way.
In the following, I implement setQuizScores method:
void Student :: setQuizScores(int* quizscores){
for(int i = 0; i<4; i++){
quiz_scores[i] = quizscores[i];
}
Where quizscores are my private members.
Now, next thing is that I want to return this quiz_scores array in the getQuizScores for each students of Student class.
However, the problem is that C++ does not allow us to return arrays directly. Instead, I want the structure of my code as following:
int Student :: getQuizScores(){
Do something;
return the elements of quiz_scores;
}
How can I do that efficiently?
I prefer not to use the Standard Template Library (STL), so I need to create my own arrays and access them according to the explanation above.

There are a few ways how you could return an array:
Pass in an array to copy to
void Student::getQuizScores(int* out) {
for(int i = 0; i < 4; i++)
out[i] = quiz_scores[i];
}
Student student;
int scores[4];
student.getQuizScores(scores);
// use scores[0], etc...
return a struct containing the array
struct Scores {
int values[4];
};
Scores Student::getQuizScores() {
Scores s;
for(int i = 0; i < 4; i++)
s.values[i] = quiz_scores[i];
return s;
}
Student student;
Scores s = student.getQuizScores();
// use s.values[0], etc...
return a reference to the quiz_scores array inside the class
using Scores = int[4];
Scores const& Student::getQuizScores() const {
return quiz_scores;
}
Student student;
Scores const& scores = student.getQuizScores();
// use scores[0], etc...

Just as setQuizScores() is able to take a pointer to an array, so too can getQuizScores() return a pointer to the quiz_scores member array, eg:
const int* Student::getQuizScores() const {
// do something...
return quiz_scores;
}
The caller can then access the array elements as needed, eg:
Student s;
...
const int *scores = s.getQuizScores();
for(int i = 0; i < 4; ++i){
cout << scores[i] << ' ';
}
Alternatively, since the array is fixed size, you can return a reference to the array instead, eg:
typedef int scoresArr[4];
scoresArr quiz_scores;
...
const scoresArr& Student::getQuizScores() const {
// do something...
return quiz_scores;
}
Student s;
...
const scoresArr &scores = s.getQuizScores();
for(int i = 0; i < 4; ++i){
cout << scores[i] << ' ';
}

You can return a pointer to the quiz_scores array through getQuizScores method as shown below:
Version 1: Using trailing return type
auto getQuizScores() -> int(*)[4]
{
//Do something;
return &quiz_scores;//NOTE THE & INFRONT OF quiz_scores
}
Now you can use this returned pointer to initialize other arrays. One possible example would be:
#include <iostream>
struct Student
{
int quiz_scores[4]= {1,2,3,4};
//getQuizScores returns a pointer to an array of size 4 with element of type int
auto getQuizScores() -> int(*)[4]
{
//Do something;
return &quiz_scores;//NOTE THE & INFRONT OF quiz_scores
}
void setQuizScores(int* quizscores)
{
for(int i = 0; i<4; i++)
{
quiz_scores[i] = quizscores[i];
}
}
};
int main()
{
Student s;
int arr[4];
for(int i = 0; i< 4; ++i)
{
arr[i] = (*s.getQuizScores())[i];
std::cout<<arr[i]<<std::endl;
}
return 0;
}
Version 2: Without using trailing return type
int (*getQuizScores())[4]
{
//Do something;
return &quiz_scores;//NOTE THE & INFRONT OF quiz_scores
}
Version 2 is the same as version 1 except that this time the getQuizScores method does not uses trialing return type.
There are other possibilities also like returning a reference to the quiz_scores array.

Related

How to pass array of object pointers to function?

I am having trouble passing an array of object pointers from main() to a function from different class.
I created an array of object pointers listPin main() and I want to modify the array with a function editProduct in class Manager such as adding new or edit object.
Furthermore, I want to pass the whole listP array instead of listP[index]. How to achieve this or is there any better way? Sorry, I am very new to c++.
#include <iostream>
using namespace std;
class Product
{
protected:
string id, name;
float price;
public:
Product()
{
id = "";
name = "";
price = 0;
}
Product(string _id, string _name, float _price)
{
id = _id;
name = _name;
price = _price;
}
};
class Manager
{
protected:
string id, pass;
public:
Manager(string _id, string _pass)
{
id = _id;
pass = _pass;
}
string getId() const { return id; }
string getPass() const { return pass; }
void editProduct(/*array of listP*/ )
{
//i can edit array of listP here without copying
}
};
int main()
{
int numProduct = 5;
int numManager = 2;
Product* listP[numProduct];
Manager* listM[numManager] = { new Manager("1","alex"), new Manager("2", "Felix") };
bool exist = false;
int index = 0;
for (int i = 0; i < numProduct; i++) { //initialize to default value
listP[i] = new Product();
}
string ID, PASS;
cin >> ID;
cin >> PASS;
for (int i = 0; i < numManager; i++)
{
if (listM[i]->getId() == ID && listM[i]->getPass() == PASS) {
exist = true;
index = i;
}
}
if (exist == true)
listM[index]->editProduct(/*array of listP */);
return 0;
}
Since the listP is a pointer to an array of Product, you have the following two option to pass it to the function.
The editProduct can be changed to accept the pointer to an array of size N, where N is the size of the passed pointer to the array, which is known at compile time:
template<std::size_t N>
void editProduct(Product* (&listP)[N])
{
// Now the listP can be edited, here without copying
}
or it must accept a pointer to an object, so that it can refer the array
void editProduct(Product** listP)
{
// find the array size for iterating through the elements
}
In above both cases, you will call the function as
listM[index]->editProduct(listP);
That been said, your code has a few issues.
First, the array sizes numProduct and numManager must be compiled time constants, so that you don't end up creating a non-standard variable length array.
Memory leak at the end of main as you have not deleted what you have newed.
Also be aware Why is "using namespace std;" considered bad practice?
You could have simply used std::array, or std::vector depending on where the object should be allocated in memory. By which, you would have avoided all these issues of memory leak as well as pointer syntaxes.
For example, using std::vector, you could do simply
#include <vector>
// in Manager class
void editProduct(std::vector<Product>& listP)
{
// listP.size() for size of the array.
// pass by reference and edit the listP!
}
in main()
// 5 Product objects, and initialize to default value
std::vector<Product> listP(5);
std::vector<Manager> listM{ {"1","alex"}, {"2", "Felix"} };
// ... other codes
for (const Manager& mgr : listM)
{
if (mgr.getId() == ID && mgr.getPass() == PASS)
{
// ... code
}
}
if (exist == true) {
listM[index]->editProduct(listP);
}
You cannot have arrays as parameters in C++, you can only have pointers. Since your array is an array of pointers you can use a double pointer to access the array.
void editProduct(Product** listP){
and
listM[index]->editProduct(listP);
Of course none of these arrays of pointers are necessary. You could simplify your code a lot if you just used regular arrays.
Product listP[numProduct];
Manager listM[numManager] = { Manager("1","alex"), Manager("2", "Felix")};
...
for(int i = 0; i < numManager; i++ ){
if(listM[i].getId() == ID && listM[i].getPass() == PASS) {
exist = true;
index = i;
}
}
if(exist == true){
listM[index].editProduct(listP);
}

Can't modify a string in C++ array

Trying to learn datastructures, I made this class for a stack. It works just fine with integers but it throws a mysterious error with strings.
The class List is the API for my stack. Its meant to resize automatically when it reaches the limit. The whole code is just for the sake of learning but the error I get doesn't make any sense and it happens somewhere in some assembly code.
#include <iostream>
#include<string>
using namespace std;
class List {
private:
int N = 0;
string* list = new string[1];
void resize(int sz) {
max = sz;
string* oldlist = list;
string* list = new string[max];
for (int i = 0; i < N; i++) {
list[i] = oldlist[i];
}
}
int max = 1;
public:
void push(string str) {
if (N == max) {
resize(2 * N);
}
cout << max << endl;
list[N] = str;
N++;
}
void pop() {
cout << list[--N] << endl;
}
};
int main()
{
string in;
List list;
while (true) {
cin >> in;
if (in == "-") {
list.pop();
}
else {
list.push(in);
}
}
}
string* list = new string[max]; in the resize method defines a new variable named list that "shadows", replaces, the member variable list. The member list goes unchanged and the local variable list goes out of scope at the end of the function, losing all of the work.
To fix: Change
string* list = new string[max];
to
list = new string[max];
so that the function will use the member variable.
Don't forget to delete[] oldlist; when you're done with it to free up the storage it points at.

Creating a personal string vector class

I am not allowed to make use of the vector class so I need to make my own. I made a int vector class and it works fine, but when trying to make it for strings it compiles but gives me an error because of the pointers. Any hint where I am making the mistake? All I did was change every int element for string, but aparently that does not work. Please help I am very confused.
public:
StringRow(){
elements = new string;
size = 0;
}
~StringRow(){...}
void push_back(string value){...}
};
You defined pointer to variable, not array of variables.
elements = new string;
Replace it with
elements = new string[size];
You can optimize algorithm with defining initial size. Create bigger array only if it's necessary.
There are several problems:
in the constructor you don't need to allocate anything. You don't even need a constructor here, you can initialize the members directly as you declare them.
if you allocate with string* tmpElementsArray = new string[size + 1]; you need to deallocate with delete [] tmpElementsArray;
Corrected working version:
#include <string>
#include <iostream>
using namespace std;
class StringRow {
private:
string* elements = nullptr;
int size = 0;
public:
// constructor not needed
// StringRow() {
// elements = nullptr;
// size = 0;
// }
~StringRow() {
delete []elements;
}
void push_back(string value) {
string* tmpElementsArray = new string[size + 1];
for (int i = 0; i<size; i++) {
tmpElementsArray[i] = elements[i];
}
delete [] elements;
elements = tmpElementsArray;
elements[size] = value;
size++;
}
int length() {
return size;
}
string at(int index) {
if (index<size) {
return elements[index];
}
}
};
int main()
{
StringRow s;
string str1 = "hello";
string str2 = "hello2";
s.push_back(str1);
s.push_back(str2);
cout << s.at(0) << endl ;
cout << s.at(1) << endl;
}
Doing a delete []elements if elements is nullptr is OK.
NB: This is not the most efficient way.

Segmentation fault error with structures

I have no idea where the segmentation error is coming from ... Any ideas?
I am working with structures for an assignment
TestResult testResultFactory(std::string name, double mark)
{
//creating an object of TestResult
TestResult car;
car.name = name;
car.mark = mark;
return car;
}
Student studentFactrory(std::string name)
{
//Creating an object of student
Student house;
house.name = name;
house.testResults = 0;
house.numTestResults = 0;
return house;
}
void addTestResult(Student * student, std::string testName, double testMark)
{
//First we need to create a new array
(student->numTestResults)+=1;
TestResult *newTestArray = new TestResult[(student->numTestResults)];
//Now we loop through the old array and add it to the new one
int index = (student->numTestResults);
for (size_t i = 0; i < (index-1); i++)
{
newTestArray[i] = testResultFactory((student->testResults[i].name),(student->testResults[i].mark));
}
//Now we need to add the new student to the end of the array
newTestArray[index] = testResultFactory(testName, testMark);
(student->testResults) = newTestArray;
}
string studentBest(Student const * student)
{
//create variables as temps
string highestName;
double highestMark;
int index = (student->numTestResults);
//Setting the two variables to the first value
highestName = (student->testResults[0].name);
highestMark = (student->testResults[0].mark);
//Using a while loop to compare and get the best
for (size_t i = 0; i < index; i++)
{
if((student->testResults[i].mark)> highestMark)
{
highestMark = (student->testResults[i].mark);
highestName = (student->testResults[i].name);
}
}
//returning the string they want
string send = (highestName)+ " "+ doubleToString(highestMark)+ "%";
return send;
}
double studentAverage(Student const * student)
{
//Variables used as temps
double average = 0;
double counter = 0.0;
double running = 0;
int index = (student->numTestResults);
//Now we need to loop through each one and add to running and counter
for (size_t i = 0; i < index; i++)
{
counter++;
running += (student->testResults[i].mark);
}
//calculating the average;
average = (running)/counter;
return average;
}
void destroyStudent(Student * student)
{
delete [] (student->testResults);
(student->testResults)=0;
}
Subject subjectFactory(std::string name)
{
//Creating an object to use in subject factory
Subject lamp;
lamp.name = name;
lamp.numStudents = 0;
lamp.studentsAllocated = 0;
lamp.students = 0;
return lamp;
}
MY guess is that the error occurs because of an out of bounds array or an pointer not worked with correctly .
int getStudentIndex(Subject const * subject, std::string studentName)
{
int index;
int count = (subject->numStudents);
//loop to find the names and set index
for (size_t i = 0; i < count; i++)
{
if(studentName == ((subject->students[i].name)))
{
index = i;
}
else index = -1;
}
return index;
}
void addStudent(Subject * subject, std::string studentName)
{
//Variables as temps
Student *pointer =0;
int index = getStudentIndex(subject,studentName);
if(index != -1)
{
//Now we need to see if they are large enough
if((subject->studentsAllocated)==0)
{
//Set the allocated to 2
(subject->studentsAllocated) = 2;
pointer = new Student[2];
//Figure this out later
pointer[1] = studentFactrory(studentName);
(subject->students) = pointer;
}
else
{
//increase SA with 1.5
(subject->studentsAllocated) = (subject->studentsAllocated) * 1.5;
pointer = new Student[(subject->studentsAllocated)+1];
int count = (subject->studentsAllocated);
//Now we need to put all the other students in
for (size_t i = 0; i < count-1; i++)
{
pointer[i] = (subject->students[i]);
}
pointer[(subject->studentsAllocated)+1] = studentFactrory(studentName);
(subject->studentsAllocated) += 1 ;
}
//Once done just seet one equal to
(subject->students) = pointer;
}
else return;
}
void removeStudent(Subject * subject, std::string studentName)
{
//First get temps
int index = getStudentIndex(subject ,studentName);
int number = (subject->studentsAllocated);
int i = index;
//delete student
if(index == -1) return;
destroyStudent(&(subject->students)[index]);
//loop to shift the things
while (i<(number -1))
{
(subject->students)[i] = (subject-> students[i+1]);
}
//Removing the last one
(subject->numStudents) -= 1;
}
bool addTestResult(Subject * subject, std::string studentName, std::string testName, double testMark)
{
int index = getStudentIndex(subject ,studentName);
if(index != -1)
{
addTestResult(&(subject->students [index]),testName,testMark);
return true;
}
else
return false;
}
void printSubjectSummary(Subject const * subject)
{
cout<<(subject->name)<< ": with "<<(subject->numStudents)<<" students"<<endl;
//Variables to use in the loop
size_t indexLoop = subject->numStudents;
int i=0;
while (i< indexLoop)
{
cout<<(subject->students[i].name)<<" Average: "<<studentAverage(&(subject->students[i]))<<", Best: "<<studentBest(&(subject->students[i]))<<endl;
}
}
void destroySubject(Subject * subject)
{
//Variables
size_t indexLoop = subject->numStudents;
for (size_t i = 0; i < indexLoop; i++)
{
destroyStudent(&(subject->students[i]));
}
delete [] subject->students;
subject->students =0;
}
I can not seem to find where the segmentation error is coming from. Even restarted the whole assignment from scratch and still seem to get errors.
Can someone please help or indicate where the fault could be coming from.
Over here we have the structs.h file that is included in my code above
#ifndef STRUCTS_H
#define STRUCTS_H
struct TestResult{
double mark;//the test mark as a percentage
std::string name;//the test name
};
struct Student{
std::string name;
TestResult * testResults;//an arry of TestResults
size_t numTestResults;//the number of results for this student (also the size of the array)
};
struct Subject{
std::string name;
Student * students;//an array of Students
size_t numStudents;//the number of students added to the subject
size_t studentsAllocated;//the size of the Student arry(must never be smaller that numStudents)
};
#endif
There are so many logical errors in there that the root cause (or causes; there are quite a few candidates) could be pretty much anywhere.
getStudentIndex returns -1 unless the student is the last one in the array, and an indeterminate value for the first one you add, so adding the first student to a subject is undefined.
addStudent only adds a student if they're already taking the subject.
It also (for some inexplicable reason) allocates an array of two Students, leaving the first element uninitialised.
Using this first element is, of course, undefined.
In the other branch, it first claims that the number of allocated students is * 1.5, but then only allocates + 1.
This will undoubtedly lead to problems.
There is a recursion in addTestResult that will never terminate.
There are most likely other problems as well – this was just a quick glance.
Start with fixing these.
And do learn about constructors and destructors so you can get rid of those "factory" and "destroy" functions.

Class accept and return array

I try to create a class that accept and return an array but I got some problem. I'm not sure if it is legal to return an array from a class. Or it could be done by returning an pointer to the array. Thank for any solution to the problem.
#include <iostream>
using namespace std;
class myclass {
private:
int Array[10];
public:
myclass (int temp[10]) {
for (int i = 0; i < 10; i++) {
Array [i] = temp [i];
}
}
int returnArray () {
return Array; // error here, I'm not sure if it is legal to return an array.
}
int* returnArray2 () {
return this->Array; // hope it will return a pointer to the array
}
};
int main () {
int Array[10] = {1,2,3,4,5,6,7,8,9};
myclass A(Array);
cout << A.returnArray() << endl; // try to return an array and print it.
myclass* ptr = &A;
cout << *ptr->returnArray2 << endl; // error here
return 0;
}
First of all it is better to write the constructor either like
myclass ( const int ( &temp )[10] ) {
for (size_t i = 0; i < 10; i++) {
Array [i] = temp [i];
}
}
or like
myclass ( int temp[], size_t n ) : Array {} {
if ( n > 10 ) n = 10;
for (size_t i = 0; i < n; i++) {
Array [i] = temp [i];
}
}
Or even you may define the both constructors.
As for the returning value then you may not return an array. You may return either a reference to an array or a pointer to the entire array or a pointer to its first element
For example
int ( &returnArray () )[10] {
return Array;
}
In this case you can write in main
for ( int x : A.returnArray() ) std::cout << x << ' ';
std::cout << std::endl;
As for this statement
cout << *ptr->returnArray2 << endl; // error here
then you forgot to place parentheses after returnArray2. Write
cout << *ptr->returnArray2() << endl;
And the following member function is wrong because the expression in the return statement has type int * while the return type of the function is int
int returnArray () {
return Array; // error here, I'm not sure if it is legal to return an array.
}
So either the function will coincide with the the second member function if you specify its return type like int *. Or you could change the return expression to *Array
int returnArray () {
return Array; // error here, I'm not sure if it is legal to return an array.
}
This is illegal because Array is not of int type. Your returnArray2 is valid, however. As for this line:
cout << *ptr->returnArray2 << endl; // error here
This is illegal because returnArray2 is a function; you must call it to return the int*:
cout << *ptr->returnArray2() << endl; // prints the first value in the array
Other notes:
Your capitalization is backwards; you should call your class MyClass and your member array arr or arr_, or you will confuse a lot of people.
return this->Array; this is redundant, you can simply return Array;
If you haven't heard of std::vector and std::array you should research those, as they are generally superior to C-style arrays.
In general, I would suggest to read a c++ book to get your basics correct as there are lot of issues in the code you posted.
Regarding your main question about exposing C style arrays in class public API, this is not a very robust mechanism. Do it if it is absolutely essential because of existing code but if possible prefer to use std::vector. You will mostly always end up with better code.
Other answers have corrected your coding errors, so i won't repeat that.
One other thing, your code suggests that the array size is fixed. You can pass and return the array by reference as well. Refer to: General rules of passing/returning reference of array (not pointer) to/from a function?