Smart Pointers

C++ 11 standard solved a major safety problem with the language: Managing memory

  • Creating memory
  • Deleting memory
  • Managing who owns memory and how many owners are there?

Example

Problematic code

Memory becomes difficult to manage when you give the pointers or references away. Here is an example.

// Interface for an internet socket API
class socket_i {
public:
    virtual void send() = 0;
    virtual void recv() = 0;
    virtual ~socket_i() {}
};

// Child class that implements an interface
class socket : public socket_i {
public:
    void send() override {
    }
  
    void recv() override {
    }
};

// An HTTP library that uses a socket interface to communicate
class http {
public:
    http(socket_i *sock) : m_socket(sock) {
    }

private:
    socket_i *m_socket;
};

// Multiple problems with this code
http* create_http() {
  socket s;
  http http_obj(&s);
  return http_obj;
}

int main(void) {
  http* http_obj = create_http();
}

One of the problems can be simplified to this:

int* get() {
  int x; 
  return &x;
}

void problem() {
  int* pointer = get();
  *pointer = 123;
}

Fixed Version 1

The partially corrected version creates a new memory reference, and it leaves it up to the consumer of the code to actually delete the object. This is just one of the issues: memory management problem. Because you are relying on the user of the code to delete your memory, it creates traps in your code.

// Let us partially fix our code
http* create_http() {
  socket s;

  // Let us create new memory which will not go out of scope
  // even when this function exits
  // BUT:
  // We have now created memory management problem:
  // ie: 
  //  - Who deletes this memory?
  //  - When do they do it?
  //  - Can you ensure that they delete it?
  http * http_obj = new http(&s);

  return http_obj;
}

int main(void) {
  http* http_obj = create_http();
  
  // We better delete our object or else it is memory leak
  delete http_obj;
}

std::unique_ptr

Let us introduce the concept of using a unique pointer such that we do not have to worry about deleting it.

// Let us partially fix our code
std::unique_ptr<http> create_http() {
  socket s;
  std::unique_ptr<http> http_obj = std::make_unique<http>(&s);
  return http_obj;
}

int main(void) {
  std::unique_ptr<http> http_obj = create_http();

  // No need to worry about deleting our allocated resource
  //delete http_obj
}

The unique_ptr is also special because it will force you to maintain "one owner".

std::shared_ptr

If we continue in our example, we will notice that there is still a fundamental flaw which is that the socket that was created would go out of scope, and the http class would then end up using a reference to memory that no longer is alive (no longer in scope).

// Interface for an internet socket API
class socket_i {
public:
    virtual void send() = 0;
    virtual void recv() = 0;
    virtual ~socket_i() {}
};

// Child class that implements an interface
class socket : public socket_i {
public:
    void send() override {
    }
  
    void recv() override {
    }
};

// An HTTP library that uses a socket interface to communicate
class http {
public:
    http(std::shared_ptr<socket_i> sock) : m_socket(sock) {
    }

private:
    std::shared_ptr<socket_i> m_socket;
};

 Cleaned up code

#include <iostream>
#include <memory>

// Interface for an internet socket API
class socket_i {
public:
    virtual bool open(const std::string& hostname) = 0;
    virtual void send(const std::string& data_to_transit) = 0;
    virtual std::string recv() = 0;
    virtual ~socket_i() {}
};

// An HTTP library that uses a socket interface to communicate
class http {
public:
    http(std::shared_ptr<socket_i> sock) : m_socket(sock) {
    }
    ~http() {
      std::cout << "Destructor of http class has been called" << std::endl;
    }

    void send_request(const std::string& host, const std::string& resource) {
      std::string request = "GET " + resource + " HTTP/1.1\r\n";
      request += "Host: " + host + "\r\n";
      request += "Connection: close\r\n\r\n";

      m_socket->send(request);

      std::string response = m_socket->recv();
      std::cout << "Response:\n" << response << std::endl;
    }

private:
    std::shared_ptr<socket_i> m_socket;
};

class linux_socket : public socket_i {
public:
    bool open(const std::string& hostname) override {
      return false;
    }
    void send(const std::string& data_to_transit) override {
    }
  
    std::string recv() override {
      return std::string{};
    }
};

// Let us partially fix our code
std::unique_ptr<http> create_http() {
  auto socket = std::make_shared<linux_socket>();

  // References to the shared pointer:
  std::cout << "reference count before: " << socket.use_count() << std::endl;

  // Let us create new memory which will not go out of scope
  // even when this function exits
  // BUT:
  // We have now created memory management problem:
  // ie: 
  //  - Who deletes this memory?
  //  - When do they do it?
  //  - Can you ensure that they delete it?
  std::unique_ptr<http> http_obj = std::make_unique<http>(socket);

  std::cout << "reference count after: " << socket.use_count() << std::endl;

  return http_obj;
}

int main(void) {
  auto http_obj = create_http();

  // This uses "socket" which was created at the first line inside create_http()
  http_obj->send_request("google.com", "index.html");

  // We better delete our object or else it is memory leak
  //delete http_obj;

  std::cout << "End of main()" << std::endl;
}

 

 

Back to top