Search code examples
c++new-operatorraw-pointer

How should I manage an intermediary vector after moving/swapping it in the constructor?


I am learning about memory management and not quite sure how how to manage the delete and/or destructor policy.

Suppose I have the following template:

template <typename X>
class DBContainer {
private:
  vector<vector<X>> db;
  size_t dbSize;

public:
  DBContainer(size_t n = 100) {
    dbSize = n;
    <vector<vector<X>> *tmp = new vector<vector<X>>(dbSize);
    db = std::move(*tmp);
    // db = std::swap(tmp);
    delete tmp;
  }

  ~DBContainer() { /* how should I delete `db`? */ }
  // ~DBContainer() = default;
};

How should I set the destructor policy for db? I understand that for a raw pointer, std::move only copies the intermediary vector of vectors to db; therefore, I still need to delete the intermediary dynamically allocated vector. I could be wrong but I believe it is the same for std::swap.

But then, at the end of the program, how should I manage the destructor policy for db? Do I simply set it to default?


Solution

  • Anything you construct dynamically with new needs to be delete'd when you are done using it. std::unique_ptr can help with that by calling delete for you when it is itself destructed, eg:

    template <typename X>
    class DBContainer {
    private:
      vector<vector<X>> db;
      size_t dbSize;
    
    public:
      DBContainer(size_t n = 100) {
        dbSize = n;
        std::unique_ptr<vector<vector<X>>> tmp(new vector<vector<X>>(dbSize));
        // or: auto tmp = std::make_shared<vector<vector<X>>>(dbSize);
        db = std::move(*tmp);
      } // <-- tmp is destroyed here, delete'ing the temp vector
    
      //~DBContainer() = default;
    };
    

    It doesn't matter that the content of tmp is moved to db, tmp itself still needs to be delete'd.

    However, since db is not being dynamically constructed via new, there is no need to delete it. It will be destructed automatically when its owning DBContainer object is destructed. Only your temporary vector that is being constructed via new needs to be delete'd. But even that can be avoided, by simply not constructing the temporary vector dynamically at all, eg:

    template <typename X>
    class DBContainer {
    private:
      vector<vector<X>> db;
      size_t dbSize;
    
    public:
      DBContainer(size_t n = 100) {
        dbSize = n;
        vector<vector<X>> tmp(dbSize);
        db = std::move(tmp);
      } // <-- tmp is destroyed here
    
      //~DBContainer() = default;
    };
    

    Alternatively:

    template <typename X>
    class DBContainer {
    private:
      vector<vector<X>> db;
      size_t dbSize;
    
    public:
      DBContainer(size_t n = 100) {
        dbSize = n;
        db = vector<vector<X>>(dbSize); // <-- temp destroyed after operator= exits
      }
    
      //~DBContainer() = default;
    };
    

    That being said, you don't actually need the temporary vector at all. You can (and should) initialize db directly, in the constructor's member initialization list, eg:

    template <typename X>
    class DBContainer {
    private:
      vector<vector<X>> db;
      size_t dbSize;
    
    public:
      DBContainer(size_t n = 100) : db(n), dbSize(n) { }
    
      //~DBContainer() = default;
    };
    

    And technically, you can get rid of dbSize too, just use db.size() whenever needed:

    class DBContainer {
    private:
      vector<vector<X>> db;
    
    public:
      DBContainer(size_t n = 100) : db(n) { }
    
      //~DBContainer() = default;
    };