I am learning more about smart pointers in C++14.
Consider the following MWC:
#include <iostream>
#include <string>
#include <memory>
class House {
public:
House &operator=(const House &house) = default;
House(const House &house) = default;
House(): id_habitants_(nullptr), num_habitants_() {}
explicit House(size_t num_habitants) {
if (num_habitants > 0) {
num_habitants_ = num_habitants;
id_habitants_ = new int[num_habitants_];
if (id_habitants_ != nullptr) {
for (size_t id = 0; id < num_habitants_; ++id) {
id_habitants_[id] = 1;
}
}
}
}
void Print() {
if (id_habitants_ != nullptr) {
for (size_t id = 0; id < num_habitants_; ++id) {
std::cout << id_habitants_[id] << ' ';
}
std::cout << std::endl;
} else {
std::cout << "<empty>" << std::endl;
}
}
~House() {
if (id_habitants_ != nullptr) {
delete [] id_habitants_;
}
num_habitants_ = 0;
}
private:
int *id_habitants_;
size_t num_habitants_;
};
int main() {
std::cout << "Testing unique_ptr.\n" << std::endl;
std::cout << "Using a dumb House class..." << std::endl;
std::cout << "Creating House h1 with 3 habitants..." << std::endl;
House h1(3);
std::cout << "IDs of h1's 3 habitants:" << std::endl;
h1.Print();
std::cout << "Creating House h2 with 0 habitants..." << std::endl;
House h2;
std::cout << "IDs of h2's 0 habitants:" << std::endl;
h2.Print();
std::cout << "Default-assigning h1 to h2..." << std::endl;
h2 = h1;
std::cout << "IDs of h2's new 3 habitants:" << std::endl;
h2.Print();
std::cout << "Destroying h1..." << std::endl;
h1.~House();
std::cout << "IDs of h2's new 3 habitants:" << std::endl;
h2.Print();
}
Without modifying the default copy constructor and the default assignment operator for the class House, how can I ensure correct pointer behavior during assignment via smart pointers?
On a first try it seems like using std::unique_ptr would be the way to go. I could create a new class:
class SmartHouse {
public:
SmartHouse &operator=(const SmartHouse &shouse) = default;
SmartHouse(const SmartHouse &shouse) = default;
SmartHouse(): id_habitants_(nullptr), num_habitants_() {}
explicit SmartHouse(size_t num_habitants) {
if (num_habitants > 0) {
num_habitants_ = num_habitants;
id_habitants_ = std::unique_ptr<int[]>(new int[num_habitants_]);
if (id_habitants_) {
for (size_t id = 0; id < num_habitants_; ++id) {
id_habitants_[id] = 1;
}
}
}
}
void Print() {
if (id_habitants_) {
for (size_t id = 0; id < num_habitants_; ++id) {
std::cout << id_habitants_[id] << ' ';
}
std::cout << std::endl;
} else {
std::cout << "<empty>" << std::endl;
}
}
~SmartHouse() {
num_habitants_ = 0;
}
private:
std::unique_ptr<int[]> id_habitants_;
size_t num_habitants_;
};
According to this, I can't really copy one unique pointer to another. Makes sense, right? It sort of defeats the purpose of it being unique. I.e. this would not compile:
SmartHouse sh1(3);
SmartHouse sh2;
sh2 = sh1;
But I could specify a move assignment operator and have the unique_ptr<int[]> member be moved upon assignment thus transferring ownership of the pointed data to the left object upon assignment:
class SmartHouse {
SmartHouse &operator=(SmartHouse &&SmartHouse) = default;
}
...
SmartHouse sh1(3);
SmartHouse sh2;
sh2 = std::move(sh1);
sh1.~SmartHouse();
sh2.Print();
Core question: Does this make sense at all? Are there better ways to enhance assignment of pointer member variables?
id_habitants_. The "smart pointer" that does this is calledstd::vector<int>. As a bonus, it also makesnum_habitants_unnecessary, as it keeps track of its own size.SmartHouse(size_t num_habitants)doesn't do anything for the case of 0 habitants. 2)id_habitants_ = std::unique_ptr<int[]>(p)can be spelledid_habitants_.reset(p)See std::unique_ptr::resetnew int[num_habitants_]) throw exceptions on failure, they don't return a null pointer, except the special no exception versions (new (std::nothrow) some_type). Seestd::nothrowh1.~House();as you will exit the block with no liveh1object. You need to recreate the object before you exit the block. (You just don't call the destructor of an automatic object.) 5) What are you trying to accomplish with that destructor?~SmartHouse() { num_habitants_ = 0; }When an object is destroy, its subobjects are destroyed too.