c++classpointerssmart-pointersmembers

Modify references members on class c++


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.


Solution

  • 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