Smart pointer class that can share the pointer with sibling objects

A simple smart pointer that uses reference counting to manage a resource:

// Keeps track of the reference count for a shared object.
struct RefCount
{
  int ref_count_;
  RefCount () : ref_count_ (0) { }
  RefCount (int i) : ref_count_ (i) { }
};

// Smart pointer class that can share the pointer with
// sibling objects.
template 
class SharedPtr
{
public:
  SharedPtr () : pointer_ (NULL), ref_count_ (NULL) { }
  SharedPtr (T* t) : pointer_ (t), ref_count_ (new RefCount ()) { }
  SharedPtr (const SharedPtr& sp)
  {
    pointer_ = sp.pointer_;
    ref_count_ = sp.ref_count_;
    ++ref_count_->ref_count_;
  }
  ~SharedPtr ()
  {
    if (ref_count_->ref_count_ == 0)
      {
        delete pointer_;
        delete ref_count_;
      }
    else
      --ref_count_->ref_count_;
  }
  T& operator* () const
  {
    return *pointer_;
  }
  T* operator-> () const
  {
   return pointer_;
  }
  SharedPtr* operator= (const SharedPtr& sp)
  {
    pointer_ = sp.pointer_;
    ref_count_ = sp.ref_count_;
    ++ref_count_->ref_count_;
  }
private:
  T* pointer_;
  RefCount* ref_count_;
};

Example usage:

class Test
{
public:
  Test (int id) : id_ (id) { }
  int id () const { return id_; }
  ~Test () { std::cout << "deleted" << std::endl; }
private:
  int id_;
};

static SharedPtr<Test>* SP = new SharedPtr<Test> ();

static void
test1 (SharedPtr<Test> sp)
{
  SharedPtr<Test> sp1 (new Test (1001));
  std::cout << sp1->id () << '\n'; // => 1001
  sp = sp1;
  *SP = sp1;
  std::cout << "leaving test1 " << std::endl;
}

int
main ()
{
  SharedPtr<Test> sp;
  test1 (sp);
  std::cout << (*sp).id () << '\n'; // => 1001
  std::cout << "leaving main " << std::endl;
  delete SP;
  return 0;
}

When to declare a destructor virtual

It is generally said that C++ base classes should have virtual destructors. It is more accurate to say that “all polymorphic base classes should have virtual destructors”. In other words, a virtual destructor is needed only if we plan to create objects of derived classes on the heap and access them from a base class pointer. (I.e, we use a factory function or something like that). Base classes are often used to inherit behavior. They can also be used to inherit traits. An often sited example is that of the concept of a non-copyable object:

class NotCopyable
{
public:
  NotCopyable () { }
private:
  NotCopyable (const NotCopyable&) { }
  NotCopyable& operator= (const NotCopyable&) { }
};

// A Fingerprint object is unique. It cannot be
// created from an existing Fingerprint and can't
// be copied to another.
class Fingerprint : private NotCopyable
{
public:
  Fingerprint (char* data) : data_ (data) { }
private:
  char* data_;
};

int main ()
{
    Fingerprint fp01 ("000111100101010000100100101110");
    Fingerprint fp02 (fp01); // => COMPILER ERROR, 
                             // Copy Constructor is private.
    Fingerprint fp03 ("00000000");
    fp03 = fp01; // => COMPILER ERROR, 
                 // Copy assignment operator is private.
    return 0;
}

As we can see from the above code, the Fingerprint class only inherits the trait of being non-copyable. (Hence the private inheritance). As we never deal directly with pointers of NotCopyable, it need not have a virtual destructor. But does it really matter whether the destructor is virtual or not? Yes. If we care about having compact objects in memory. The declaration of a virtual member will add overhead to each new object in the form of a pointer to the “virtual table”.

There is another situation where virtual destructors are useful – declaring abstract classes. A class can be rendered abstract by making one or more of its virtual functions pure. Sometimes you may not want pure virtual functions, but still like to prevent objects of the class from being initialized. A pure virtual destructor will help you here:

class Abstract
{
public:
    virtual ~Abstract () = 0;
};

There is a subtle difference between a pure virtual destructor and a pure virtual function. A pure virtual destructor should have a definition! This is because the destructor of a derived class will call the destructor of all its base classes, in order. The definition of a pure virtual destructor can be empty:

Abstract::~Abstract () { }

That will satisfy the complaining linker.

C for scripting

Ever imagined of using C as a scripting language? Install TCC. Write a simple C program:

// add.c
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv)
{
   /* No error checking. */
   int a = atoi(argv[1]);
   int b = atoi(argv[2]);
   printf("%d\n", (a + b));
   return 0;
}

Add this line to the top of the file:

#!tcc -run

Mark the file as executable:

$ chmod +x add.c

Run it:

$ ./add.c 10 20
30

The TCC executable is only 100Kb in size, but it can compile the Linux kernel! An ideal use for TCC is as an embedded compiler to dynamically generate executables.

Functions that return null

It is a bad idea to have functions return null. This is especially true for C/C++ libraries. Think of a hypothetical CharBuffer API:

#include <iostream>
#include <cstring>

class CharBuffer
{
public:
CharBuffer (const char* s) : buffer_ (NULL)
{
    size_t len = 0;
    if (s != NULL && (len = strlen (s)) > 0)
    {
        buffer_ = new char [len + 1];
        strcpy (buffer_, s);
    }
 }
 ~CharBuffer () { delete[] buffer_; }
 const char* GetBuffer ()
 {
    return buffer_;
 }
 private:
 char* buffer_;
};

This is a valid usage of the library, but it will result in the abnormal exit of the program:

int main ()
 {
    CharBuffer b ("");
    std::cout << b.GetBuffer () << '\n';
    return 0;
 }

There are two ways to cleanup the implementation of CharBuffer::GetBuffer():

  1. Throw an exception if buffer_ is NULL.
  2. Return a ‘default’ buffer object.

I prefer the second option as C++ do not have checked exceptions. Continuing with the above sample, we add a new static variable to the CharBuffer class:

static const char* DEFAULT_BUFFER;

and intilialize it like this:

const char* CharBuffer::DEFAULT_BUFFER = "";

The GetBuffer() function should return DEFAULT_BUFFER if buffer_ is NULL:

const char* GetBuffer ()
{
   if (buffer_ == NULL)
      return DEFAULT_BUFFER;
   return buffer_;
}

This gives the user to take appropriate action if the retuned value is the ‘default’ object. Even if he ignore to check it, the program will not behave badly. If the value to be defined is a complex type, and you need to return and non-const pointer, you can clone the DEFAULT_OBJECT and return the new copy.

This is a simple idiom, but can have important consequence on software quality as most of the errors caused by null pointers manifest at production sites. BTW, I was just wondering, do we really need NULL at all?

Value semantics

“Value semantics” is used to refer to classes whose objects when copied gives two independent copies, with the same value. C++ has value semantics for both built-in and user-defined types. This is in accordance with one of C++’s core design principles – “support user-defined and built-in types equally well”. For example:

int a = 10;
int b = a; // Now we have two ints with the same value.
++b; // Changes the value of `b' to 11, but `a' still remains 10.

// A user-defined type that represents a 64-bit integer.
class Int64
{
     // Supports value semantics by overloading the
     //  copy constructor and the = operator. 
     Int64(const Int64& val);
     Int64& operator=(const Int64& val);
};

Int64 a = 10;
Int64 b = a; // Two objects of Int64 with the same value.
++b; // Only b changes, a remains the same.

Java has value semantics for primitive types like int and char. But user-defined types have “reference semantics”:

class Int64 {
    // ...
}

Int64 a = new Int64(10);
Int64 b = a; // a and b points to the same object.
b.increment(); // a and b is changed.

This is often a source of confusion to newcomers.

typedefs are good

The designers of Java omitted typedefs on the ground that it introduces “too much context”. This is true to some extent. I mostly depend on typedefs for declaring generic containers. This saves a lot of keystrokes and screen space, especially when declaring functions that consume or return such containers:

typedef map<string, int> NameAgeMap;

static void InitSample(NameAgeMap& sample)
{
   sample["joe"] = 20;
   sample["mark"] = 22;
}

// ....

NameAgeMap sample;
InitSample(sample);
NameAgeMap::const_iterator iter = sample.begin();
while (iter != sample.end())
{
   std::cout << iter->first << ':' << iter->second << '\n';
   ++iter;
}

If the new type is properly named, we need not look at the typedef to infer what it really is. An IDE might even show the real type when the mouse is hovered over the variable. Contrast this with Java. I have to type the lengthy container declaration where ever I need it:

public void InitSample(HashMap<String, Integer> sample) { 
    sample.put("joe", 20);
    sample.put("mark", 22);
}

// ...

HashMap<String, Integer>sample = new HashMap<String, Integer>();
InitSample(sample);
Set<String>keys = sample.keySet();
for (String name : keys) {
     System.out.println(name + ':' + sample.get(name));
}

I suppose that the argument against typedef was made before Java had generics. As the view on enum was later compromised, Java may eventually introduce typedef.