thinkdeep
thinkdeep

Reputation: 1023

C++ destructor for class object within another class

I am trying to implementing a SegmentTree class to be used in another Solution class.

In class SegmentTree, the destructor is implemented as follows:

~SegmentTree() {
    destruct(root);
};

void destruct(Node* root) {
    if (!root) return;
    
    destruct(root->left);
    destruct(root->right);
    delete root;
    root = NULL;
}

Then, in class Solution, we have

class Solution {
private:
    SegmentTree* tree;

public:
    Solution(vector<int>& nums) {
        tree = new SegmentTree(nums);
    }

    //Question: how shall ~Solution() be implemented to free memory of the pointer tree?
};

The question:

  1. Do we need to implement destructor to delete tree pointer in Solution class?
  2. If yes, how shall that be implemented?

Upvotes: 2

Views: 937

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 595305

Do we need to implement destructor to delete tree pointer in Solution class?

If you use new to create an object, you need to delete the object when you are done using it. So yes, Solution needs a destructor to delete the tree that it new's (just as SegmentTree needs a destructor to delete the Node's that it new's).

If yes, how shall that be implemented?

Like this:

class Solution {
private:
    SegmentTree* tree;

public:
    Solution(vector<int>& nums) :
        tree(new SegmentTree(nums))
    {
    }

    ~Solution() {
        delete tree;
    }
};

Which you can automate by using std::unique_ptr, eg:

class Solution {
private:
    std::unique_ptr<SegmentTree> tree;

public:
    Solution(vector<int>& nums) :
        tree(std::make_unique<SegmentTree>(nums))
        //tree(new SegmentTree(nums))
    {
    }
};

Either way, do be aware of the Rule of 3:

If a class requires a user-defined destructor, a user-defined copy constructor, or a user-defined copy assignment operator, it almost certainly requires all three.

So, you should also implement (or disable) the copy constructor and copy-assignment operator, eg:

class Solution {
private:
    SegmentTree* tree;

public:
    Solution(vector<int>& nums) :
        tree(new SegmentTree(nums))
    {
    }

    Solution(const Solution &src) :
        tree(new SegmentTree(*(src.tree)))
    {
    }

    ~Solution() {
        delete tree;
    }

    Solution& operator=(const Solution &rhs) {
        if (&rhs != this) {
            Solution tmp(rhs);
            std::swap(tree, tmp.tree);
        }
        return *this;
    }
};

Though, you might consider simply getting rid of new to begin with and just let the compiler manage the SegmentTree object for you:

class Solution {
private:
    SegmentTree tree;

public:
    Solution(vector<int>& nums) :
        tree(nums)
    {
    }
};

Upvotes: 5

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122133

There must be exactly one delete for every new or there will be a memory leak. You can write a destructor like this:

class Solution {
private:
    SegmentTree* tree;

public:
    Solution(vector<int>& nums) {
        tree = new SegmentTree(nums);
    }
    ~Solution() { 
        delete tree;
    }
};

However, you need to read about the rule of 3/5 (What is The Rule of Three?), because when a class manages a resource a constructor and destructor are not sufficient. For example copying the above Solution will cause problems (because the compiler generated assignment and copy constructor aren't doing the right thing). When possible you should strive to follow the rule of 0, for example by not using a pointer in the first place:

class Solution {
private:
    SegmentTree tree;

public:
    Solution(vector<int>& nums) : tree(nums) {}
};

If the member must be dynamically allocated you should use a std::unique_ptr (and again rule of 0) but never a raw pointer.

Upvotes: 4

Related Questions