Trying to pass a ** array but keep getting c2664 error - c++

Okay, so I have a class called student and another called faculty that are both derived classes from a person class. I need to make two teams consisting of both student and faculty. I have an array of students and an array of faculty. I created two more arrays for the teams that are both person**. when I try to pass the array I get the compiler error c2664 with a message of cannot convert parameter3 from 'Person**[12] to Person*[]'
Student sArr[12];
Faculty fArr[12];
Person** t1Arr[12];
Person** t2Arr[12];
generateTeams(fArr,sArr,t1Arr,t2Arr);
}
void generateTeams(Faculty fArr[],Student sArr[],Person** t1Arr[], Person** t2Arr[]){....}
I was also getting erros while attempting to assign the student/faculty to the person array so I had to reinterpret_cast as follows:
for(int i=0;i<12;i++){
if(sArr[i].getTeam()==1){
t1Arr[t1Count]=reinterpret_cast<Person**> (&sArr[i]);
t1Count++;
}
else if(sArr[i].getTeam()==2){
t2Arr[t2Count]=reinterpret_cast<Person**> (&sArr[i]);
t2Count++;
}
else if(fArr[i].getTeam()==1){
t1Arr[t1Count]=reinterpret_cast<Person**> (&fArr[i]);
t1Count++;
}
else if(fArr[i].getTeam()==2){
t2Arr[t2Count]=reinterpret_cast<Person**> (&fArr[i]);
t2Count++;
}
}
I can provide mor of the code if needed, but I think what I have shown should suffice as that is where the problem lies.

Student sArr[12] is an array of Students.
If I understand your code, you're trying to assign your Students and Faculty to teams by keeping separate arrays of pointers to Person objects presumably Student and Faculty both inherit from Person).
If this is correct, then you want your teams to be made of of arrays of pointers.
Person* t1Arr[12]
What you have declared instead of are arrays of pointers-to-pointers, which has it's uses, but those are pretty rare.

Assuming that the error message quote
” cannot convert parameter3 from 'Person**[12] to Person*[]
is correct, then it does not match the alleged function declaration
void generateTeams(Faculty fArr[],Student sArr[],Person** t1Arr[], Person** t2Arr[]){....}
i.e. this is not the real code.
Alternatively it's not the real error message.
But basically, assuming that the error message is correctly quoted, it tells you what's wrong: you have declared a function that expects a Person*[] as third argument, but you're passing a Person**[].
At a guess, you have forward-declared the function, and missed a *.
Forward-declaration is a generally Bad Practice™: it's more work for no gain and does sometimes foul up things, as I suspect it has here.
A basic and probably the best solution is to ditch your current raw-array-and-pointers-and-casting code, and use std::vector.

Your problem appears to be in how you create an array of pointers to objects, and how you use those objects from the array. Here's a simplified example of how to do that:
#include <cstdio>
using namespace std;
class person {
const char * _name;
public:
person(const char * n) : _name(n) {}
const char * getname() const { return _name; }
};
person * pa1[12];
int main( int argc, char ** argv )
{
const char * names[12] = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l" };
// construct objects
for( int i = 0; i < 12; ++i) {
pa1[i] = new person(names[i]);
}
// use objects
for( int i = 0; i < 12; ++i) {
printf("person %d is %s\n", i, pa1[i]->getname());
}
// destroy objects
for( int i = 0; i < 12; ++i) {
delete pa1[i];
}
return 0;
}
Here I've created a simple class, person, and I've constructed an array of pointers to person like this:
person * pa1[12];
I then used a simple for loop to construct objects with the new operator:
for( int i = 0; i < 12; ++i) {
pa1[i] = new person(names[i]);
}
The new operator allocates space for each object, and calls its constructor.
I can then use those objects by dereferencing the pointer with the -> operator:
for( int i = 0; i < 12; ++i) {
printf("person %d is %s\n", i, pa1[i]->getname());
}
Because I used new to construct the objects, I need to destroy them with delete:
for( int i = 0; i < 12; ++i) {
delete pa1[i];
}
Also note that it's always a good idea to reduce a problem to a short self-contained example in order to best understand it.

Related

Class member value overwritten by previous member

If I create a two class members of char* type and is array. And if I resize first member of the class it overwrite the second member with same values.
Why it happens?
Is it some memory management issues?
#include <iostream>
using namespace std;
class myclass
{
private:
const char *arr1[0];
const char *arr2[4] {
"one",
"two",
"three",
"four"
};
public:
void run() {
int f = 0;
*this->arr1 = new char[4];
for(f = 0; f < 4; f++) {
this->arr1[f] = "foo";
}
for(f = 0; f < 4; f++) {
cout << this->arr2[f] << endl;
}
}
};
int main()
{
myclass *my = new myclass();
my->run();
return 0;
}
Output
foo
foo
foo
foo
const char *arr1[0];
Zero-sized arrays are not allowed in standard C++. Your compiler is allowing it as an extension to the language.
Even if your compiler has this extension, dereferencing an array of size 0 causes undefined behavior and you are doing this here:
*this->arr1 = new char[4];
I don't know what your intention here is, either you want
const char *arr1[4];
in which case *this->arr1 = new char[4]; is unnecessary or you want
const char **arr1;
in which case it should be this->arr1 = new const char*[4];.
You should not use char* to manage strings, use std::string instead, which does the memory management for you. Similarly, for multiple strings use std::vector<std::string> instead of char**.
There doesn't seem any reason to use dynamic memory allocation in main either. The same way as you should use std::vector to manage dynamically-sized arrays of objects instead of using new[]/delete[], don't use dynamic memory allocation to create single objects if you don't have a good reason for it and if you have to, use std::unique_ptr instead of raw new/delete.
int main()
{
myclass my;
my.run();
return 0;
}
This does the same without dynamic allocation.

Problems with default value of pointer to pointer in constructor

When we are using a "double" pointer to class, what are we writing in the constructor with arguments? Are we using one pointer for the allocated memory ?
Here is the code. It doesn't compile and I don't understand why. Thanks for the help.
#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;
class Article{
private:
char title[100];
char author[50];
char *content;
bool publish;
public:
Article(char *title="", char *author="", char *content="", bool publish=0){
strcpy(this->title, title);
strcpy(this->author, author);
this->content=new char[strlen(content)+1];
strcpy(this->content, content);
this->publish=publish;
}
void show(){
cout<<title<<endl;
cout<<author<<endl;
cout<<content<endl;
}
~Article(){
delete [] content;
}
};
class Newspaper{
private:
char name[100];
Article **p;
int articles;
Article first;
public:
Newspaper(char *name="", Article **p=Article(), int articles=0, Article first=Article()){
strcpy(this->name, name);
}
};
int main() {
char title[100], author[50], content[100];
int n;
cin >> n;
char name[100];
cin.getline(name, 100);
cin.getline(name, 100);
Article first("VAZNO","OOP","Vezba:OOP",true);
Newspaper v(name,first);
Article **s = new Article*[n];
for(int i = 0; i < n; ++i) {
cin.getline(title, 100);
cin.getline(author, 50);
cin.getline(content, 100);
v.addArticle(Article(title, author, content, true)); //se koristi copy konstruktor
}
v.showFirst();
v.showLongest();
cout << v.totalofauthor(author) << endl;
for(int i = 0; i < n; ++i) {
delete s[i];
}
delete [] s;
return 0;
}
Solution to your question:
From your code it seems that a Newspaper uses a pointer to a pointer (what you call a double pointer) for keeping track of the Articles:
Newspaper(char *name, Article **p , // This would be ok
int articles = 0, Article first = Article())
but that you've trouble to define a default argument for it:
Newspaper(char *name="", Article **p=Article(), // oops doesn't compile
int articles = 0, Article first = Article())
The reason is that a "double" pointer is not the same as the object itself. If you want a default argument here, you must provide a double pointer as well.
This code does exactly this:
Newspaper(char *name = "", Article **p = new Article*, // this is ok !
int articles = 0, Article first = Article()){
So this is the solution to your question.
But what's your problem ?
But what's the purpose or providing a dummy pointer, pointing to nowhere as default argument ?
Later you try to create a Newspaper based on an article again:
Newspaper v(name, first); // won't work, because first is not a pointer either
So the problem is not the constructor, but the whole principle. It seems that you really want to create a Newspaper based on an Article, and that you use defaults to cover the case where someone wants to create a Newspaper without Article.
Apparently your design also foresees that Articles may be added dynamically:
v.addArticle(Article(title, author, content, true));
Finally it seems that you really have a problem with pointers: when in the constructor you write, you didn't initialise the poitner to a memory region large enough to hold the name:
Newspaper(char *name = "", ...) {
strcpy(this->name, name); // Ouch !! pointer name is not initalized !
}
So once you'll get your code compiled, your programme won't work ! As this->name is a pointner that is never initialized, your strcpy() will cause memory corruption and undefined behaviour (crash!?).
Recommendations
First, get a book or a tutorial to understand and master pointers. If you do'nt you'll quickly be completely lost in your C++ class.
In the meantime, get rid of your char* and strcpy() and alike, and use std::string instead.
Last, consider the use of std::vector to manage the dynamic Article container. A dynamic array of poitners implemented with Article** would need extra logic, such as maintaining the size, reallocation of memory once the number of article grows, not speaking of the ownership (allocation/deallocation) of the Articles you put in the array.
Based on these recommendations, your Newspaper would look like this:
class Newspaper{
private:
string name;
vector<Article> p; // p.size() will provide the number of articles
public:
Newspaper(string name = "") : name(name) { } // empty newspaper
Newspaper(string name, Article& a) : Newspaper(name) { // combo
addArticle(a);
}
void addArticle(Article &a) {
p.push_back(a); // add and article to the vector
}
... // rest of the code, here
};
And here a little more

Invalid conversion from 'int' to 'const char*' issue C++

The problem occurs in my ToDoList.cpp class file.
ToDoList.cpp:
ToDoList::ToDoList() {
arraySize = 3;
arrayData = 0;
array = new string(arraySize); //error here
}
ToDoList::ToDoList() {
array = new string(todolist.arraySize); //and error here
arraySize = todolist.arraySize;
arrayData = todolist.arraySize;
}
ToDoList.h:
class ToDoList {
public:
ToDoList();
ToDoList(const ToDoList&);
~ToDoList();
void AddItem(string item);
void ListItems();
private:
string* array;
int arraySize;
int arrayData;
};
If you want an array of strings, use
array = new string[arraySize];
You should use
array = new string[arraySize];
and
array = new string[todolist.arraySize];
In your example, you are trying to create an object of std::string class(string(arrraySize)) which is not valid. Compiler is giving the appropriate error to understand it.
EDIT
Your class may be written using std::vector which is efficient correct and easy to understand code.
class ToDoList {
public:
ToDoList() {};
~ToDoList() {};
void AddItem(std::string& item);
void ListItems();
private:
std::vector<std::string> array;
};
void ToDoList::AddItem(std::string& item) {
array.push_back(item);
}
void ToDoList::ListItems() {
for(size_t i = 0; i<array.size(); i++) {
std::cout<<array[i]<<std::endl;
}
}
There's no std::string constructor with just a length. If you want to initialise a string with something in it, you need to say what characters to use. You could use new string(arraySize, fillCharacter), but given this usage, maybe std::vector may be more appropriate.
Edit: the extra details shows you are trying to do something different. So see the other answers. However it looks like you are trying to re-invent a vector<string> , so you may find it easier to use a std::vector instead of manually allocating your array of strings.

C++ how to display multidimentional array in OO design

Hi I'm writing a simple version of Pacman with OO design in C++. I have problem displaying the content of a 2D array. The array constains a bunch of symbols, which represent the wall of the map/maze. Here is the sample code that I wrote:
Game.h
class Game
{
private:
char map;
public:
Game();
void displayMap();
};
Game.cpp
char map[10][20] = {...};
void Game::displayMap()
{
for(int i = 0; i < 10; i++)
{
for(int j = 0; j < 20; j++)
{
cout << map[i][j] << " ";
}
cout << endl;
}
}
The compiler will pop out an error at [i] saying "expression must have pointer-to-object type".
But if I define the size of the array in the header file and assign its value when defining the constructor
Game.h
class Game
{
private:
char map[10][20];
};
Game.cpp
Game::Game()
{
char map[10][20] = {...};
}
It will compile but when I try to display the content of the array (using the same code of displayMap()), I found out it's filled with junk. I think it's because that assignment is not an assignment. It's actually an initialization of another array on the stack, so that array is destroyed after the constructor finishes, and the displayMap() at that time display the original unassigned array.
I could be wrong, so feel free to correct me. I need a recommendation on how should I structure my game to avoid this problem
Game::Game() {
char map[10][20] = {...};
}
Here you redeclare a local variable with the same name of the instance variable, hence you hide the outer one. In addition you are trying to assign to an array which has been declared before, that's not possible in C++. You can just initialise an array when you declare it, not afterwards.
You can do this (if you have C++11 support):
class Game {
char map[W][H] = { ... };
}
or you can do this:
class Game {
char map[W][H];
Game() {
char tmp[W][H] = { ... };
memcpy(map, tmp, sizeof(char)*W*H);
}
}
Even better, you can use std::copy. Or even even better just use an std::array and forget normal arrays.
I commented your question, but I think it would be good to make it an answer, so here it is.
The second option should work fine.
Garbage values are normal in C/C++.
What you have to do is to initialize your values inside your constructor ( Game::Game() ). Constructors are meant to be used in these cases.
The behavior of C/C++ is to not assign a default value, it just "takes what's in place in RAM". Other languages do initialize the RAM cells though. It all depends of what programming language you are using.
Including this code inside your constructor (before accessing map[][] for another thing) should work:
for (int ix = 0; ix < 10; ix++)
for (int jx = 0; jx < 20; jx++)
map[ix][jx] = "x"; // x will be your default value

How to manage objects in a fixed array?

This if for my homework.
I have a class called Student that takes 3 parameters (id, name, class) and I want to store each student in an array called Roster (which can only have 7 students).
The user will provides input to add or remove students. Thus, I have to manage the array by creating or deleting students. So if the user specify the student ID, I have to remove him for the array.
I tried to use a fixed array, but I'm struggling to make it works. Is there a better way to implement this?
I must not use a vector or any STL container.
student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <iostream>
#include <string>
static const int SIZE = 7;
class Student {
private:
int student_id;
std::string name;
std::string classification;
public:
Student(int, std::string, std::string); // constructor; initialize the list to be empty
~Student();
void print();
};
#endif
student.cpp
#include <iostream>
#include <string>
#include "student.h"
#define PROMPT "class> "
using namespace std;
Student::Student(int a, string b, string c){
student_id = a;
name = b;
classification = c;
}
Student::~Student(){
//delete Student
}
void Student::print(){
cout<<"Enrolled:"<<endl;
cout<<student_id<<"-"<<name<<"-"<<classification<<endl;
}
main.cpp
#include <iostream>
#include <string>
//#include <sstream>
#include "student.h"
#define PROMPT "class> "
using namespace std;
//**** Implement Error Handling ****\\
enum errorType {
UNKNOWN_ERROR,
INPUT_ERROR,
HANDLER,
NUM_ERRORS
};
// error messages
string errorMessage[NUM_ERRORS] = {
"Unknown Error\n",
"Input Error\n",
};
// error handler
void handleError(errorType err) {
if(err > 0 && err < NUM_ERRORS)
cout<< "Error: "<< errorMessage[err];
else cout<< "Error: "<< errorMessage[UNKNOWN_ERROR];
}
//**** END Error Handling ****\\
void enroll(Student newStudent){
cout<<"test";
Student roster[SIZE];
for(int i=0;i<SIZE;i++){
newStudent->roster[i];
}
}
void handleInput() {
int id; string n, c;
cin>>id>>n>>c;
Student newStudent(id,n,c);
newStudent.print();
enroll(newStudent);
//cout<<"hello3"<<endl;
return;
}
int main() {
//Student newStudent; /* <-- why doesn't this work?!*/
string input = "";
bool finished = false;
cout<<PROMPT; // prompt the user
while(!finished) {
if(input!="") cout<<PROMPT;
cin>>input;
if(input=="enroll") {
cout<<PROMPT<<"Enroll student:"<<endl;
handleInput();
}
else if(input=="drop") {
cout<<PROMPT<<"Enter ID:"<<endl;
}
else if(input=="roster") {
cout<<"This will print formatted list of students"<<endl;
}
else if(input=="quit") {
finished=true;
}
else handleError(errorType(1));
}
}
Since it is a homework, I'd like to point out some mistakes you did because it is important to understand what you are doing in the first place.
You must not program by coincidence, but by trying to understand exactly what's going on. By doing that you will become better and better and the answers should fall in place.
What you've done
So, from what you are describing, the array is fixed. Thus it is a good idea to use a constant as you did (SIZE).
However, as we can see below you a declaring an array of size SIZE in the function. By doing that, your array is like a temporary variable, because its scope is inside the function. Each time you call this function, the array will be declared again and then deleted at the exit. So it should be declared outside.
void enroll(Student newStudent)
{
cout<<"test";
Student roster[SIZE]; // Here 'roster' will be available only inside the function.
for(int i=0;i<SIZE;i++)
{
newStudent->roster[i]; // Here there is few mistakes see my explanation below*
}
}
If we look at this part:
newStudent->roster[i];
First of all, the arrow '->' is used with pointers. The dot '.' is used with objects. In both case, it does the same thing, access to public members of Student.
Since you passed
void enroll(Student newStudent)
you should use '.' instead.
newStudent.SomeOfYourMembers;
If the parameter was a pointer to a Student
void enroll(Student *newStudent)
Then, you'd have to use the arrow '->' like you did.
Back to the original statement:
newStudent->roster[i];
This means, you want to access to 'roster' array at position 'i' inside your Student object (newStudent). As you can see in your code, roster is not declared inside Student (and should not be since you want an array of Students), so that won't work.
Guidelines
As I mentionned, your array should be outside the function, so at a higher scope.
Then, if you need an array of student, basically, 'roster[i]' will give you access to the student 'i'. Thus, if you want to print the student, you would do something like that:
roster[i].print();
This would be valid because 'print()' is defined as public.
In order to store a student inside the array, you can do something like:
roster[i] = new Student(0 /* id*/, "name", "classification");
But don't forget, each time you use new, you have to balance it with a delete. And if you are creating the student like this in a loop, you will have to clean them the same way:
for(int i = 0; i < SIZE; ++i)
{
delete roster[i];
}
Good luck!
Don't hesitate if there is there anything that I could clarify. I hope this helps!
Edit: In reply to your first comment.
Concerning the roster array
No, it is not mandatory to create a class roster you could declare roster in the main.cpp.
The key concept is that by defining
Student roster[SIZE];
the array will contains objects of type Student.
What roster[i].print() means is that you are printing one of the Student of that array, in fact the one at position 'i'.
Concerning the print() function
What is powerfull with Object Oriented language, each object will have the same print() function. So, you do not need to convert the array to string.
However, if you want a string to be printed out (or returned) you can write the code inside the print() function that will do this job.
The advantage of this, is that if further on you need to change your array in some ways, your print() function will always work.
Concerning the Delete
When you are doing something like this on an array that contains objects:
delete roster[i];
It will delete the object at the position 'i'. Thus, the destructor of that Student 'i' will be called. If your object Student would contains other object, you would have to delete them in the destructor.
Further notices
Since ID is an input that you are storing into a string, you will have to convert the ID to the same type of the student_id, which is a int. Then you can always write a loop for each student and check their ID to delete the proper one.
Concerning the container, a fixed array might not be the best to achieve this job. You might want to look the LinkedList concept.
It doesn't make much sense for enroll to be a member function, so
I'd wrap the roster into a class to get automatic clean up of my
pointers.
#include <cstddef>
struct Student {};
class Roster
{
private:
static const size_t size = 7;
// non-copyable
Roster(const Roster&);
Roster& operator=(const Roster&);
public:
Roster() {
for(unsigned i = 0; i < size; ++i) {
roster_[i] = NULL;
}
}
~Roster() {
for(unsigned i = 0; i < size; ++i) {
delete roster_[i];
}
}
// enroll by copy
bool enroll(const Student& s) {
for(unsigned i = 0; i < size; ++i) {
if(roster_[i] == NULL) {
roster_[i] = new Student(s);
return true;
}
}
// out of space
return false;
}
// enroll by taking ownership
bool enroll(Student* s) {
for(unsigned i = 0; i < size; ++i) {
if(roster_[i] == NULL) {
roster_[i] = s;
return true;
}
}
// out of space
return false;
}
private:
// data
Student* roster_[size];
};
int main()
{
Roster r;
Student s;
r.enroll(s);
Student* sp = new Student();
r.enroll(sp);
return 0;
}
What about this?
Student * roster[2];
roster[0] = new Student(5,"first","2A");
roster[1] = new Student(2,"Second","5B");
Ps:
Enrol and Size shouldn't be members of the student class.
Print should ideally be externalized and a ToString function should be added instead.
You should use the inline constructor initialization instead:
Student(int a,string b,string c):id(a),name(b),class(c){}
You've used the keyword class as a variable name of type string. You shouldn't do that. Does it even compile like that?
enroll should have two arguments: void enroll( Student enrollee, Student Roster[]). You should probably change the name of Roster to roster because it's not a class and typically class names are capitalized.
If your array will only ever have 7 students then you could use some sentinel value to mark that the current student as an invalid student. Perhaps the id will be -1 to mark this. It means basically that you need some way to keep track of which spots in the array you can still use. If you don't do this then declaring an array of Students will get you an array of students with garbage member variables. You wouldn't be able to tell which students are real ones and which are just place holders for when someone new enrolls in the class. I would create a default constructor of Student and initialize its member variables like this:
id=-1;
name="";
name_of_class="";
I changed the name of your string class to avoid confusion.
After all that, enroll would look something like this:
void Student::enroll( Student enrolee, Student roster[]){
//search through roster to check for the first student with an
//id of -1
//if there are no students with id of -1, produce an error message
//that the class is full
//overwrite the student with id of -1 with the id, name, and
//name_of_class of enrollee
}
Although I'm not sure what exactly string class is there for. Does it store what class the Student is in? Is it their year in school like Freshman, Sophomore?
If you're suppose to use dynamic allocation of roster, though, it's a different story, but you said it will only ever have seven students.