A few times I've stumbled across the scenario where I have a container of pointers that needs to be copied.
Let's say we have the following class hierarchy:
Student (base class)
StudentService
The StudentService class has a std::vector<Student*> students
field and the following constructor:
StudentService::StudentService(std::vector<Student*> students) {
// code
}
It won't be correct to just use the std::vector::operator=
operator and write this->students = students
, because that will only copy the pointer addresses and so if someone from the outside deletes the objects pointed to by those pointers, then the StudentService class would suffer as a result.
The solution is to loop through each pointer in the students
parameter and create a new dynamic object, something like this:
for(int i = 0; i < students.size(); i++) {
this->students.at(i) = new Student(*students.at(i));
}
But even that is not proper due to the fact that it will create ONLY Student objects. And we know that a Student can be a Freshman, Sophmore, Junior or Senior. So here is my question: what's the best solution to this problem?
I guess one way would be to place a private enum field inside each Student class and have 4 if-else statements checking what type of Student it is and then creating a new dynamic object based on that like so:
for(int i = 0; i < students.size(); i++) {
if(students.at(i).getType() == FRESHMAN) {
this->students.at(i) = new Freshman(*students.at(i));
} else if(students.at(i).getType() == SOPHMORE) {
this->students.at(i) = new Sophmore(*students.at(i));
} else if {
// and so on...
}
}
But this still seems quite cumbersome, so what would you suggest?
You can store pointers in a vector just like you would anything else. Declare a vector of pointers like this: vector<MyClass*> vec; The important thing to remember is that a vector stores values without regard for what those values represent.
Having vector of objects is much slower than a vector of pointers. The results are because algorithms such as sorting need to move elements inside the container.
Passing by references ensures an actual object is passed to the copy constructor, whilst a pointer can have NULL value, and make the constructor fail.
You're looking for the Clone pattern. Add a clone() virtual function to Student, which is overridden in each descendant and creates the appropriate copy. Then write deep copy of containers as you've correctly specified.
Edit: my work assumption is that your Freshman, etc. classes are descending from Student. If not, use a variant<> and apply a copy visitor.
Resolving ownership issues
If you deem to shared the Student
-s between your modules, then you are facing an ownership issue, and I would recommend using vector of std::shared_ptr<Student>
-s to solve it.
If you have a std::vector<std::shared_ptr<Student>>
, you can pass it to anyone you want. The receiver can copy the vector
using an assignment operator and later on any objects he adds/removes won't affect the original container, just like you seem to desire.
Resolving cloning issues
If you are interested in each module having its own copy of a Student
-s vector, you are facing a cloning issue.
You could resolve it by adding the following method to your classes:
class Student {
[..]
virtual Student * clone() const = 0; // Assuming Student is abstract, otherwise implement as well
};
class Freshman : public Student {
[..]
virtual Freshman * clone() const { return new Freshman(*this); }
};
// Same for other derived classes...
And then using std::transform
to copy the vector:
// students is the original std::vector<Student *>
std::vector<Student *> copy(students.size());
std::transform(students.begin(), students.end(), copy.begin(), [](Student * s) -> Student * { return s->clone(); });
BTW, it's Sophomore, not Sophmore...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With