0

I'm new in c++ and I come from Java. So I have guess about modifying class members through reference variables.

In java to add an element to an existing list(class member) only have to use the "add" method, and that is all, but here I don't understand why I can't modify the vector with my get method.

class Student {
public:
  Student(std::string n):name(n){};
  std::string getName(){return name;};
  void setName(std::string name){this->name = name;};
private:
  std::string name;
};

class Subject {
public:
  std::vector<Student> getStudents(){return students;};
  void setStudents(std::vector<Student> students){this->students = students;};
private:
  std::vector<Student> students;
};

int main() {
  // Students
  Student s1("Abi");
  Student s2("Nat");
  Student s3("Osi");

  // normal case
  Subject math;
  std::vector<Student> students;
  students.push_back(s1);
  math.setStudents(students);
  math.getStudents().push_back(s2);
  // print names
  for(unsigned int i=0; i < math.getStudents().size();i++) {
    std::cout << math.getStudents()[i].getName() << std::endl;
  }

  // pointers
  std::cout << "Ptr------------------" << std::endl;
  Subject *mathPtr;
  mathPtr  = &math;
  // try to add student to the existing vector
  mathPtr->getStudents().push_back(s2); // it doesnt work
  // it works if i add a new vector
  std::vector<Student> studentsPtr;
  studentsPtr = mathPtr->getStudents();
  studentsPtr.push_back(s2);
  mathPtr->setStudents(studentsPtr);
  // print students of original object
  for(unsigned int i=0; i < math.getStudents().size();i++) {
    std::cout << math.getStudents()[i].getName() << std::endl;
  }

  // pointers
  std::cout << "Smart_ptr-------------" << std::endl;
  std::shared_ptr<Subject> mathSmartPtr;
  mathSmartPtr  = std::make_shared<Subject>(math);
  // try to add student to the existing vector
  mathSmartPtr->getStudents().push_back(s3); //it  doesnt work
  std::vector<Student> studentsSmartPtr;
  studentsSmartPtr = mathPtr->getStudents();
  studentsSmartPtr.push_back(s3);
  mathSmartPtr->setStudents(studentsSmartPtr);// it doesnt work too
  // print students of original object
  for(unsigned int i=0; i < math.getStudents().size();i++) {
    std::cout << math.getStudents()[i].getName() << std::endl;
  }
}

I don't understand why the smart pointer doesn't work. It suppose to be as a normal pointer + autodelete, no?

Greetings and thanks.

Alejandro
  • 15
  • 6

3 Answers3

1

The problem in your code is that you are making a lot of copies of the student vector, without realizing it. A common problem when you come from Java.

Specifically, in your Subject class, the method getStudents does not do what you probably think it does:

std::vector<Student> getStudents(){return students;};

It makes a copy of the vector called "students", and returns the copy to you, not the original. The same is also happening elsewhere in your code, but probably with less harmful effects.

You could easily fix this by including the reference operator:

std::vector<Student>& getStudents(){return students;};

will work just as it would in Java. There is a danger, though: if your Subject object is destroyed, the vector will go with it - even if you stored a reference to it elsewhere. For instance, this may crash your program:

Subject* mathPtr = new Subject;
// try to add student to the existing vector
std::vector<Student>& s = mathPtr->getStudents();
delete mathPtr;
s.push_back(s2); // accesses already-freed memory
Kevin Keane
  • 1,506
  • 12
  • 24
  • So, What do you recommend? – Alejandro Mar 06 '15 at 06:48
  • Personally, I prefer to work with pointers rather than references, and use new and delete to create and destroy long-living objects. With references, so much is happening behind the scenes that you can easily lose track of whether you are working with the original or a copy of an object. Pointers are more up front. – Kevin Keane Mar 06 '15 at 08:33
  • Ok, one more thing,when i use smart pointers to modify the original object why it doesnt works? It supose the smart pointer has to work like a pointer, no? – Alejandro Mar 08 '15 at 18:49
  • The problem actually has nothing to do with how you reference the Subject object. It is about how you access the vector of students that is nested within the Subject object. The problem thus occurs regardless of how you get to the Subject object. – Kevin Keane Mar 09 '15 at 20:24
1

You have declared getStudents to return a copy of the students vector.

std::vector<Student> getStudents(){return students;};

So when you do this:

getStudents().push_back(...)

it only adds to a copy of the vector, which gets destroyed right away because you're not storing it anywhere. Thus the original students vector doesn't get modified.

If you want to push_back to the original vector, you will need to return a reference to it:

std::vector<Student>& getStudents(){return students;}; 
                    ^
Emil Laine
  • 41,598
  • 9
  • 101
  • 157
-1

This might help

 class Student {
    public:
        Student(std::string n) :name(n){};
        std::string getName(){ return name; };
        void setName(std::string name){ this->name = name; };
    private:
        std::string name;
    };

    class Subject {
    public:
        Student * getStudent(std::string name);
        void setStudents(Student* student);
        void PrintStudents();
    private:
        std::vector<Student*> students;
        typedef std::vector<Student*>::iterator iter;
    };

    Student* Subject::getStudent(std::string name)
    {
        Student* stud = NULL;
        for (iter i = students.begin(); i != students.end(); ++i)
        {
            if ((*i)->getName() == name)
                stud = (*i);
        }
        return stud;
    }

    void Subject::setStudents(Student* student)
    {
        students.push_back(student);
    }

    void Subject::PrintStudents()
    {
        for (iter i = students.begin(); i != students.end(); ++i)
        {
                std::cout << (*i)->getName() << std::endl;
        }
    }

    int main()
    {
        Student* s1;
        s1 = new Student("Kavinda");
        Student s2("Liza");

        Subject math;
        math.setStudents(s1);
        math.setStudents(&s2);

        Student *s3 = math.getStudent("Kavinda");
        if (s3)
            std::cout << s3->getName() << std::endl;
        else
            std::cout << "No Student Exists having that name\n" << std::endl;

        math.PrintStudents();

        getchar();
        return 0;
    }