C++ – Rule of Three (Making your class leak free)

I was recently re-learning C++. It’s an amazing journey which I enjoy a lot. Sharing one such experiment here.
Here we are trying to create a robust class which gets used by a driver which does some pretty nasty stuff with dynamic memory allocation. The target here is to make the class as memory-safe as possible. There are two ways of doing it. Following the rule of three (or five) and using the smart pointer. Here we see the first approach. Smart pointers will be covered in the future post.

All potential pitfalls and fixes are commented.

#include <iostream>
#include "resource.h"
using namespace std;

/**
 * A robust class following the "Rule of Three"
 * 
 * Does all the necessary checks for copy, assignment and re-assignment for the
 * dynamic resources. 
 * To verify with valgrind: 
 * valgrind --tool=memcheck --leak-check=full ./a.out
 */
class Owner
{
    string name;
    Resource *resource;

  public:
    Owner()
    {
        cout << "Creating owner!" << endl;
        name = "";
        resource = nullptr;
    }

    //for fixing double deletion of resource copy
    Owner(const Owner &owner) : name(owner.name)
    {
        cout << "Creating owner!" << endl;
        if (owner.getResource())
        {
            this->resource = new Resource(owner.name);
        }
        else
        {
            this->resource = nullptr;
        }
    }

    void setName(const string pName)
    {
        name = pName;
    }

    string getName() const { return name; }
    void addResource()
    {
        //fix for readdition of resouce
        delete resource;

        resource = new Resource(name);
    }

    Resource *getResource() const { return resource; }
    ~Owner()
    {
        cout << "Destroying owner!" << endl;
        delete resource;
    }

    // fix for assignment operator
    void  operator=(const Owner &owner)
    {
        //deleting existing resource on self
        delete resource;
        this->name = owner.getName();
        if (owner.getResource())
        {
            this->resource = new Resource(owner.getName());
        }
        else
        {
            this->resource = nullptr;
        }
    }
};
#include <iostream>
using std::cout;
using std::endl;
using std::string;
class Resource
{
    string name;

  public:
    Resource(string pName)
    {
        cout << "Creating resource!" << endl;
        name = pName;
    }
    string getMessage()
    {
        return "Hello " + name + ". Welcome to the Matrix!";
    }
    ~Resource()
    {
        cout << "Destroying resource!" << endl;
    }
};
#include "owner_shared.h"
using namespace std;
int main(int argc, char const *argv[])
{
    Owner owner1;
    Owner owner3;
    owner3.setName("userC");
    owner3.addResource();
    owner1.setName("userA");
    owner1.addResource();
    Owner owner2 = owner1;
    cout << (owner1.getResource())->getMessage() << endl;
    owner2.setName("user Z");
    owner2.addResource();
        cout << (owner1.getResource())->getMessage() << endl;
    cout << (owner2.getResource())->getMessage() << endl;

    owner1.setName("userB");
    //replace resource
    owner1.addResource();
    owner3 = owner1;
    return 0;
}

 

Leave a Reply