0

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?

Full MWE.

4
  • You seem to want a deep copy of id_habitants_. The "smart pointer" that does this is called std::vector<int>. As a bonus, it also makes num_habitants_ unnecessary, as it keeps track of its own size. Commented Jun 24, 2018 at 17:04
  • 1) 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 spelled id_habitants_.reset(p) See std::unique_ptr::reset Commented Jun 26, 2018 at 19:18
  • 3) You need to realize that, unlike C or the primitive C++ of the early 90ties, in modern C++ allocation functions (like new 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). See std::nothrow Commented Jun 26, 2018 at 19:31
  • 4) You cannot just do h1.~House(); as you will exit the block with no live h1 object. 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. Commented Jun 26, 2018 at 20:11

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.