Chariot
Chariot

Reputation: 335

Reserve dynamic memory with the new operator

I'm learning new operator and I have the next question: I want to reserve new memory when I add a new subject and if I do this way I lose all the previus content of array. So, how can I do this if i have to reserve memory each time that i want to add a new subject? Or in other words, how i reserve memory without lose the previus?

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <string>
using namespace std;

class Subject {
public:
    Subject() { m_name = "";
                m_hours = 0;
    }
    string getName() { return m_name; }
    int getHours() { return m_hours; }
    void setName(string name) { m_name = name; }
    void setHours(int hours) { m_hours = hours; }
private:
    string m_name;
    int m_hours;
};

class Person {
private:
    string m_name;
    int m_age;
    Subject *m_subjects;
    int m_nSubjects;
public:
    Person() {
        m_name = "";
        m_age = 0;
        m_nSubjects = 0;
    }
    ~Person() {

    }
    string getName() { return m_name; }
    int getAge() { return m_age; }
    void setName(string name) {
        m_name = name;
    }
    void setAge(int age) {
        m_age = age;
    }
    void addSubject(string name, int hour);
    void showSubjects();
};

void Person::addSubject(string name, int hours) {
    m_subjects = new Subject[m_nSubjects+1]; *the problem is here, all the previus content is lost*


    m_subjects[m_nSubjects].setName(name);
    m_subjects[m_nSubjects].setHours(hours);
    m_nSubjects++;
}

void Person::showSubjects() {
    for (int i = 0; i < m_nSubjects; i++) {
        cout << m_subjects[i].getName();
        cout << "\n";
        cout << m_subjects[i].getHours();
    }
}


int main() {
    int nSubjects;
    string name;
    int hours;

    Person person1;
    person1.setName("Name 1");
    person1.setAge(30);

    cout << "Subjects to add: ";
    cin >> nSubjects;
    for (int i = 0; i < nSubjects; i++) {
        cout << "Name of subject: " << "\n" << endl;
        cin >> name; 
        cout << "Hours: " << "\n" << endl;
        cin >> hours;
        person1.addSubject(name, hours);
    }

    person1.showSubjects();


    system("pause");
    return 0;
}

I hope you can understand me.

Upvotes: 1

Views: 74

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 598134

You need to copy the existing data to the new array before you then replace the previous array (which you are leaking, BTW), eg:

void Person::addSubject(string name, int hours) {
    Subject *new_subjects = new Subject[m_nSubjects+1];

    for(int i = 0; i < m_nSubjects; ++i) {
        new_subjects[i] = m_subjects[i];
    }

    new_subjects[m_nSubjects].setName(name);
    new_subjects[m_nSubjects].setHours(hours);

    delete[] m_subjects;
    m_subjects = new_subjects;
    m_nSubjects++;
}

You also need to free the current array in your Person destructor to avoid leaking as well:

~Person() {
    delete[] m_subjects;
}

And you also need to add a copy constructor and a copy assignment operator to Person as well, to avoid future problems with multiple Person objects sharing the same array in memory if you assign one Person to another:

Person(const Person &src) {
    m_name = src.m_name;
    m_age = src.m_age;
    m_nSubjects = src.m_nSubjects;
    m_subjects = new Subject[m_nSubjects];
    for (int i = 0; i < m_nSubjects; ++i) {
        m_subjects[i] = src.m_subjects[i];
    }
}

Person& operator=(const Person &rhs) {
    if (&rhs != this) {
        Person copy(rhs);
        std::swap(m_name, copy.m_name);
        std::swap(m_age, copy.m_age);
        std::swap(m_nSubjects, copy.m_nSubjects);
        std::swap(m_subjects, copy.m_subjects);
    }
    return *this;
}

And, if you are using C++11 or later, you should (optionally) also add a move constructor and move assignment operator to Person, too:

Person(Person &&src) {
    m_name = std::move(src.m_name);
    m_age = src.m_age; src.m_age = 0;
    m_nSubjects = src.m_nSubjects; src.m_nSubjects = 0;
    m_subjects = src.m_subjects; src.m_subjects = nullptr;
}

Person& operator=(Person &&rhs) {
    Person movedTo(std::move(rhs));
    std::swap(m_name, movedTo.m_name);
    std::swap(m_age, movedTo.m_age);
    std::swap(m_nSubjects, movedTo.m_nSubjects);
    std::swap(m_subjects, movedTo.m_subjects);
    return *this;
}

See the Rule of 3/5/0 for more details.

A better solution is to use std::vector instead, let the compiler handle all of these details for you:

#include <iostream>
#include <string>
#include <vector>
#include <limits>

class Subject {
public:
    Subject() {
        m_name = "";
        m_hours = 0;
    }

    Subject(std::string name, int hours) {
        m_name = name;
        m_hours = hours;
    }

    std::string getName() const { return m_name; }
    int getHours() const { return m_hours; }
    void setName(std::string name) { m_name = name; }
    void setHours(int hours) { m_hours = hours; }

private:
    std::string m_name;
    int m_hours;
};

class Person {
private:
    std::string m_name;
    int m_age;
    std::vector<Subject> m_subjects;

public:
    Person() {
        m_name = "";
        m_age = 0;
    }

    std::string getName() const { return m_name; }
    int getAge() const { return m_age; }
    void setName(std::string name) { m_name = name; }
    void setAge(int age) { m_age = age; }
    void addSubject(std::string name, int hour);
    void showSubjects() const;
};

void Person::addSubject(string name, int hours) {
    m_subjects.push_back(Subject(name, hours));
}

void Person::showSubjects() const {
    for (std::size_t i = 0; i < m_nSubjects.size(); ++i) {
        cout << m_subjects[i].getName();
        cout << "\n";
        cout << m_subjects[i].getHours();
    }
}

int main() {
    int nSubjects;
    std::string name;
    int hours;

    Person person1;
    person1.setName("Name 1");
    person1.setAge(30);

    std::cout << "Subjects to add: ";
    std::cin >> nSubjects;
    for (int i = 0; i < nSubjects; i++) {
        std::cout << "Name of subject: ";
        std::getline(std::cin, name); 
        std::cout << "Hours: ;
        std::cin >> hours;
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        person1.addSubject(name, hours);
    }

    person1.showSubjects();

    std::system("pause");
    return 0;
}

Upvotes: 2

Related Questions