Search code examples
c++pointersoperator-overloadingpointer-to-pointer

operator= overloading with double pointers for fraction math


Originally, my lab was passing three argument: addFractionJesseR(*lFrac, *rFrac, **resFrac); but I just found out I can't pass three arguments. I had to change it to **resFrac = addFractionJesseR(*lFrac, *rFrac); and now I'm having problems compiling. I know my pointers and double pointers are out of scope somewhere but I just can't spot where. the debugger points to the second line as the problem:

FractionJesseR& FractionJesseR::operator=(const FractionJesseR& arg) {
  num = arg.num;
  denom = arg.denom;
  return *this;
}

which is called by:

FractionJesseR& addMenu(FractionJesseR* lFrac, FractionJesseR* rFrac) {
  int option;
  FractionJesseR** resFrac = new FractionJesseR*();
......
    case 2:
      cout << "Calling add() --\n\n";
      **resFrac = addFractionJesseR(*lFrac, *rFrac);
      break;
    ......

**resFrac = addFractionJesseR(*lFrac, *rFrac); was originally addFractionJesseR(*lFrac, *rFrac, **resFrac);

which is called by:

void displayMenu() {
  int option;
  FractionJesseR *lFrac = nullptr;
  FractionJesseR *rFrac = nullptr;
  FractionJesseR *resFrac = nullptr;
......
    case 2:
      cout << "  Adding Option --\n\n";
      if (lFrac == nullptr && rFrac == nullptr) {
        cout << "    Not a proper call as no Fractions are available!\n\n";
      }
      else {
        *resFrac = addMenu(lFrac, rFrac);
      }
      break;

*resFrac = addMenu(lFrac, rFrac) was originally addMenu(lFrac, rFrac, &resFrac)

(yes, I did call delete on all my pointers, I'm still new to Stack Overflow and learning to only put up relevant snippets of code) I need help pointing me in the right direction. I think my pointers go out of scope somewhere in addMenu or displayMenu... maybe I'm dereferencing a double pointer wrong?

Any help would be greatly appreciated!

edit:

FractionJesseR& addFractionJesseR(FractionJesseR& lFrac, FractionJesseR& rFrac) {
  int n = 0;
  int d = 0;
  FractionJesseR *resFrac = nullptr;

 // Adding the fractions
 n = (&lFrac)->getNum() * (&rFrac)->getDenom() + (&lFrac)->getDenom() *
    (&rFrac)->getNum();
  d = (&lFrac)->getDenom() * (&rFrac)->getDenom();

  resFrac = new FractionJesseR(n / gcd(n, d), d / gcd(n, d));

  if (d < 0) {
    d = -d;
    n = -n;
  }

  return *resFrac;
}

Solution

  • You're having issues with memory management.

    I assume you are using some version of Visual Studio. The debugger will usually mark the next line to be executed, so you're getting a crash on

    num = arg.num;
    

    This happens because this is null, since

    FractionJesseR** resFrac = new FractionJesseR*();
    

    allocates a pointer on the free store (which is unusual) and initializes it with 0 (because of the brackets). This:

    **resFrac
    

    first dereferences resFrac, giving a null pointer, which you then dereference again. Dereferencing a null pointer is undefined behaviour. In your case, it causes a crash in the assignment operator on the first statement.

    The obvious solution is to stop using pointers and manual memory management. At best, use objects:

    FractionJesseR resFrac
    // ...
    resFrac = addFractionJesseR(*lFrac, *rFrac);
    

    At worst, use smart pointers:

    auto resFrac = std::make_unique<FractionJesseR>();
    // ...
    *resFrac = addFractionJesseR(*lFrac, *rFrac);
    

    What you don't want to do (unless you are forced to because this is an assignment, in which case I'd question the motivation):

    auto resFrac = new FractionJesseR;
    // ...
    *resFrac = addFractionJesseR(*lFrac, *rFrac);
    // ...
    delete resFrac;
    

    addFractionJesseR() is returning a reference to a value that was allocated on the free store. Where are you going to delete it? Every new must be matched with a delete.

    If you really want an example of manual memory management, you should not mix pointers and references:

    FractionJesseR* addFractionJesseR(FractionJesseR* a, FractionJesseR* b)
    {
        auto n = a->getNum() * b->getDenom() + a->getDenom() * b->getNum();
        auto d = a->getDenom() * b->getDenom();
    
        if (d < 0)
        {
            d = -d;
            n = -n;
        }
    
        return new FractionJesseR(n / gcd(n, d), d / gcd(n, d));
    }
    
    FractionJesseR* addMenu(FractionJesseR* a, FractionJesseR* b)
    {
        // ...
        FractionJesseR* resFrac = addFractionJesseR(a, b);
        // ...
        return resFrac;
    }
    
    void displayMenu()
    {
        // ...
        FractionJesseR *resFrac = addMenu(lFrac, rFrac);
        // ...
        delete resFrac;
    }
    

    Notice that the pointer allocated in addFractionJesseR() is returned to addMenu(), which returns it to displayMenu(), which deletes it.