0

Im trying to pass an arrays of objects to a function so that it can be filled with the contents of a text file.

Update: Removed _menu[] from the function parameters and also from main. The code compiles just fine, but now it crashes right after openMenu asks for the cin to the filename.

code:

class Dish {


    public:

        int _itemNo;
        string _category;
        string _description;
        double _price;


        Dish();

        Dish(int itemNo, string category, string description,
            double price); 
        }

class DishDb{


    private:

        int _nElems;

    public:
        Dish _menu[20];

        void openMenu(ifstream &fromFile);
};

void DishDb::openMenu(ifstream &fromFile){

    string fileName;

    int itemNo;
    double price;
    string description;
    string category;

    int numOfDishes = 0;

    cout << "Enter file name: ";

    cin >> fileName;

    ifstream inFile(fileName);

    do{

        inFile >> itemNo;
        _menu[numOfDishes]._itemNo = itemNo;

        getline(inFile, category, ':');
        _menu[numOfDishes]._category = category;

        getline(inFile, description, ':');
        _menu[numOfDishes]._description = description;

        inFile >> price;
        _menu[numOfDishes]._price = price;

        numOfDishes++;

    }while(!inFile.eof());

    inFile.close();

    cout << endl << "Menu was loaded.";

}

int main(){

    string filename;

    cout << "Enter today's transaction file name: ";

    cin >> filename;

    DishDb DDb; 

    ifstream inFile;
    Dish _menu[20];

    DDb.openMenu(inFile);

    DDb.display();

    return 0;
}

No errors for some reason

  • You already have a member `_menu` in `DishDb`. Do you really want a parameter with the same name? In `DishDb::openMenu` only the parameter and not the member is used, so it seems like a design issue. – churill Apr 07 '20 at 17:31
  • Note that in `DishDb::openMenu`, the parameter `_menu` isn't the same as the data member `_menu` – Caleth Apr 07 '20 at 17:31
  • Aside: if you don't know how many `Dish`es, use `std::vector` instead of `Dish[20]`. Otherwise your program has undefined behaviour if it tries to read 21 or more dishes – Caleth Apr 07 '20 at 17:33
  • Well as already said, you don't need to have this parameter because the array is already available in your class (which is kind of the whole point of classes). But if you did want to do it for some strange reason then just drop the `[]`. `DDb.openMenu(inFile, _menu);` – john Apr 07 '20 at 17:38
  • _"No errors for some reason Errors: expected primary-expression before ']' token"_ Which one is it? You still have a missing semicolon at the end of the definition of `Dish`. – churill Apr 07 '20 at 18:10

3 Answers3

1

By default, arguments in C++ are passed by value.

By the phrasing of your question it seems like you are trying emulate pass-by-reference which is default in many other languages.

What you want to do in that case is have the function accept either a pointer (Dish* dishArr) or reference (Dish& dishArr) to the array. In either case, you can then use the pointer/reference to access the memory where the object resides from inside the function.

Note you will likely want to also pass in the size of the array so that you don't go out of the bounds of the array.

Pointer Example

void mutateDishArray(Dish* dishPtr, int numDishes) {
    for(int i = 0; i < numDishes; ++i) {
        dishPtr[i] = Dish(); // dereferencing the pointer using the array syntax
        // this is equivalent to writing *(dishPtr+i) = Dish(); using the dereference operator '*'
    }
}

int main() {
    Dish dishArray[10]; // an array with memory for 10 dishes on the stack

    mutateDishArray(dishArray, 10); // pass a pointer to the array (an array reference will decay into a pointer so we don't need the address-of operator '&')
}

That answers your question, but to fit your class layout, you may want to alter your code as follows.

You have a member definition for Dish _menu[20] array in the DishDb class, but you never initialize it with a constructor. Instead, you create a Dish _menu[20] array in main(). That array is outside the scope of the method DishDb::openMenu and not at all related to the Dish _menu[20] array defined in the Dish class.

class DishDb{
    private:
        int _nElems;
        int _menuSize;
        Dish* _menu;

    public:
        DishDb();
        void openMenu(ifstream &fromFile);
};

DishDb::DishDb(Dish* _menu, int _menuSize)
    : _nElems(0) // this is a 'member initializer-list'
    , _menuSize(_menuSize)
    , _menu(_menu)
{
}

Now, the DishDb constructor will accept a pointer to the array you had already made in main() and its member methods will have access to it through that pointer.

int main(){

    string filename;

    cout << "Enter today's transaction file name: ";

    cin >> filename;

    Dish _menu[20];

    DishDb DDb(_menu, 20); // The DishDb is now constructed with a pointer to the _menu array on the stack

    ifstream inFile;

    DDb.openMenu(inFile);

    DDb.display();
}

More on member initializer lists: https://en.cppreference.com/w/cpp/language/initializer_list

Ben Boyle
  • 140
  • 9
  • 1
    *"non-pointer arguments in C++ are passed by value."* Pointers too are passed by value. – HolyBlackCat Apr 07 '20 at 20:12
  • Yes, although one could argue that a pointer is a reference by definition; so is passing the value of a reference (or pointer) pass-by-value, or pass-by-reference? It can be debated, but you are correct in saying that. – Ben Boyle Apr 07 '20 at 20:18
0

Here is a trivial example of using an array as a parameter for a function, for reference:

#include <iostream>

void func(int intArr[], unsigned int arrLength) {
  for(unsigned int i = 0; i < arrLength; i++) {
    std::cout << intArr[i] << '\n';
  }
}

int main(int argc, char* argv[]) {
  const unsigned int SIZE = 10;
  int myInts[SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  func(myInts, SIZE);
  return 0;
}

Behind the scenes, arrays are treated as pointers when passed into a function. The parameter int intArr[] could have been int *intArr with no change in effect. Incidentally, I see that you made your array of Dish objects a public variable, but you should really make it private because half the reason we encapsulate data within classes is to make sure that other code (that has no business altering our data) cannot change it directly. Instead, you should add a method that can update the array after performing its own checks and another method that can pass out a pointer to the array.


A couple other things to possibly revisit:

  1. Class definitions must end with a semicolon, but you are missing one at the end of your class Dish definition.
  2. !inFile.eof() is actually a really bad test to use to see if you have reached the end of a file. This is because inFile.eof() will only return true after you have attempted to read past the end of the file, but if you try to read past the end of a file you will get a segfault. Rather, you will want a loop condition that will guarantee that the next read from the file you perform is a valid read. You can accomplish this with a loop similar to:
while(inFile >> itemNo && getline(inFile, category, ':') && getline(inFile, description, ':') && inFile >> price){

    _menu[numOfDishes]._itemNo = itemNo;

    _menu[numOfDishes]._category = category;

    _menu[numOfDishes]._description = description;

    _menu[numOfDishes]._price = price;

    numOfDishes++;

}

I would highly recommend reading this post on Stack Overflow for a better explanation about why this works better.

  1. It looks like you're probably using using namespace std; somewhere outside of what you showed us, but this is considered bad practice. You won't always run into problems when using it, but as you go on to make larger projects the danger level goes up pretty quickly. This other post on Stack Overflow gives a good explanation of why.
Shaavin
  • 116
  • 6
-1

Try adding a semicolon to the class definition for Dish and make sure you are inside the std namespace.

ThatGuy
  • 35
  • 5