Search code examples
c++crashcopy-constructordynamic-memory-allocationassignment-operator

Constructors and operator= with dynamic memory allocation


noob here. I'm doing an exercise from a book and the compiler reports no errors, but the program crashes when I try to run it.

I'm trying to run a little program exercising the different methods of the Cow class. It explicitly has: a constructor, a default constructor, a copy constructor, a destructor, an overloaded assignment operator, and a method to show its contents.

I'll put the whole project:

Class specification:

//cow.h -- For project Exercise 12.1.cbp

class Cow
{
    char name[20]; // memory is allocated in the stack
    char *hobby;
    double weight;
public:
    Cow();
    Cow(const char *nm, const char *ho, double wt);
    Cow(const Cow &c);
    ~Cow();
    Cow & operator=(const Cow &c);
    void ShowCow() const; // display all cow data
};

Methods implementation:

// cow.cpp -- Cow class methods implementation (compile with main.cpp)

#include <cstring>
#include <iostream>
#include "cow.h"

Cow::Cow() // default destructor
{
    strcpy(name, "empty");
    hobby = new char[6]; // makes it compatible with delete[]
    strcpy(hobby, "empty");
    weight = 0.0;
}

Cow::Cow(const char *nm, const char *ho, double wt)
{
    strcpy(name, nm); // name = nm; is wrong, it copies the address of the argument pointer (swallow copying)
    /*if (name[20] != '\0') // if it's not a string, make it a string (in case nm is larger than 20)
        name[20] = '\0';*/
    hobby = new char[strlen(ho) + 1]; // allocates the needed memory to hold the argument string
    strcpy(hobby, ho); // copies the pointed-to data from the argument pointer to the class pointer
    weight = wt;
}

Cow::Cow(const Cow &c) // copy constructor
{
    strcpy(name, c.name); // copies the value to the desired address
    char *temp = hobby; // stores the address of the memory previously allocated with new
    hobby = new char[strlen(c.hobby) + 1];
    strcpy(hobby, c.hobby); // copies the value to the new address
    delete[] temp; // deletes the previously new allocated memory
    weight = c.weight;
}

Cow::~Cow()
{
    delete[] hobby;
}

Cow & Cow::operator=(const Cow &c) // overloaded assignment operator
{
    strcpy(name, c.name);
    char *temp = hobby;
    hobby = new char[strlen(c.hobby) + 1];
    strcpy(hobby, c.hobby);
    delete[] temp;
    weight = c.weight;
    return *this;
}

void Cow::ShowCow() const
{
    std::cout << "Name: " << name << '\n';
    std::cout << "Hobby: " << hobby << '\n';
    std::cout << "Weight: " << weight << "\n\n";
}

Client:

// main.cpp -- Exercising the Cow class (compile with cow.cpp)

#include "cow.h"
#include <iostream>

int main()
{
    using std::cout;
    using std::cin;

    Cow subject1; // default constructor
    Cow subject2("Maria", "Reading", 120); // non-default constructor
    Cow subject3("Lula", "Cinema", 135);
    subject1 = subject3; // overloaded assignment operator
    Cow subject4 = subject2; // copy constructor
    subject1.ShowCow();
    subject2.ShowCow();
    subject3.ShowCow();
    subject4.ShowCow();

    cin.get();
    return 0;
}

I was hiding some parts of the code to locate the possible problem and it seems the progam doesn't like this two lines:

subject1 = subject3;
Cow subject4 = subject2

And in particular, in the overloaded assignment operator and the copy constructor, if I hide the delete[] temp line, the program doesn't crash.

I'm total noob and probably is something stupid but I can't see what I'm doing wrong in these definitions.

Any help?


Solution

  • Cow::Cow(const Cow &c) // copy constructor
    {
        strcpy(name, c.name); // copies the value to the desired address
        char *temp = hobby; // stores the address of the memory previously allocated with new
        hobby = new char[strlen(c.hobby) + 1];
        strcpy(hobby, c.hobby); // copies the value to the new address
        delete[] temp; // deletes the previously new allocated memory
        weight = c.weight;
    }
    

    It's the copy c-tor. There is no previously allocated members. this->hobby points to random garbage. So when you delete[] temp (i.e. this->hobby before the new assignment), you get UB.

    Cow & Cow::operator=(const Cow &c) // overloaded assignment operator
    {
        strcpy(name, c.name);
        char *temp = hobby;
        hobby = new char[strlen(c.hobby) + 1];
        strcpy(hobby, c.hobby);
        delete[] temp;
        hobby = name;
        weight = c.weight;
        return *this;
    }
    

    hobby = name is incorrect, as it causes a memory leak + destructor will try to delete an object that was not allocated with operator new[].