Search code examples
c++classoopoperator-overloadingcompound-assignment

Exception thrown: read access violation. this was nullptr in array of objects


I'm a CS student taking an OOP course and I don't know how to fix this issue. I understand that when the += operator tries to add the first element into the array, 'this' is nullptr and an exception is thrown, but I don't know how to fix it.

Shopping list header looks like this:

#include "Groceries.h"

class ShoppingList{
    Groceries* list;
    int size = 0, capacity = 2;
public:
//methods
     ShoppingList& operator+=( const Groceries& c);

operator+= looks like:

ShoppingList& ShoppingList::operator+=( const Groceries& c) {
    if (size == capacity) {
        Groceries* l1 = new Groceries[capacity * 2];
        l1 = list;
        list = l1;
        capacity *= 2;
    }
    list[size++]=c;//here is the exception
    return *this;
}

Groceries header looks like:

#include <string>
#include <iostream>
class Groceries {
    std::string product;
    int quantity;
public:
    Groceries() : product("empty"), quantity(0) {};
    Groceries(std::string s, int x) : product(s), quantity(x) {};
    Groceries(const Groceries& c);
    ~Groceries() {};
    std::string product();
    int quantity();
    void Print();
};

and main HAS TO look like

int main()
{
    ShoppingList L;
    (L += Groceries("bread", 5)) += Groceries("cheese", 2);
    L.Print();
//...
}

Solution

  • These statements in the body of the operator

    l1 = list;
    list = l1;
    

    do not make sense. After the first assignment statement there is a memory leak because the address of the allocated memory is lost. In fact these two statements are equivalent to this statement

    list = list;
    

    including the side effect of the overwriting the pointer l1.

    The operator can be defined the following way

    ShoppingList& ShoppingList::operator+=( const Groceries& c) {
        if (size == capacity) {
            Groceries* l1 = new Groceries[capacity * 2];
            std::copy( list, list + size, l1 );
            delete [] list;
            list = l1;
            capacity *= 2;
        }
        list[size++]=c;//here is the exception
        return *this;
    }
    

    Pay attention to that you are using the same identifiers product and quantity to declare different entities

    class Groceries {
        std::string product;
        int quantity;
    public:
        //...
        std::string product();
        int quantity();
        //...
    

    Here is a demonstrative program based on your code.

    #include <iostream>
    #include <string>
    #include <iterator>
    #include <algorithm>
    
    class Groceries {
        std::string product;
        int quantity;
    public:
        Groceries() : product("empty"), quantity(0) {};
        Groceries(std::string s, int x) : product(s), quantity(x) {};
        Groceries(const Groceries& c);
        ~Groceries() {};
    //    std::string product();
    //    int quantity();
        void Print();
    
        friend std::ostream & operator <<( std::ostream &os, const Groceries &g )
        {
            return os << g.product << ": " << g.quantity; 
        }
    };
    
    class ShoppingList{
        Groceries* list;
        int size = 0, capacity = 2;
    public:
    //methods
         ShoppingList& operator+=( const Groceries& c);
         ShoppingList() : list( new Groceries[2]() ) {}
         ~ShoppingList() { delete [] list; }
    
         friend std::ostream & operator <<( std::ostream &os, const ShoppingList &sl )
         {
            std::copy( sl.list, sl.list + sl.size, 
                       std::ostream_iterator<Groceries>( os, " " ) );
            return os;
         }
    };
    
    ShoppingList& ShoppingList::operator+=( const Groceries& c) {
        if (size == capacity) {
            Groceries* l1 = new Groceries[capacity * 2];
            std::copy( list, list + size, l1 );
            delete [] list;
            list = l1;
            capacity *= 2;
        }
        list[size++]=c;//here is the exception
        return *this;
    }
    
    
    int main() 
    {
        ShoppingList L;
        (L += Groceries("bread", 5)) += Groceries("cheese", 2);
    
        std::cout << L << '\n';
    
        return 0;
    }
    

    The program output is

    bread: 5 cheese: 2