Bloom filter implementation in C++
Thanks. I have been trying to learn about bloom filters. Had to do some research before the review :-)

Code Review

Hash Function

This is an old C style typedef.

typedef unsigned int (*HashFunction)(std::string);


Though not wrong I prefer the C++ style as they are easier to read.

typedef std::function&lt;unsigned int(std::string)&gt;   HashFunction;


Or using the newer C++11 using directive (I am beginning to use more of this myself).

using HashFunction = std::function&lt;unsigned int(std::string)&gt;;


While we are on the hash function. I note that you are passing std::string as a value and thus inducing a copy of the string. To avoid the copy pass by const reference.

using HashFunction = std::function&lt;unsigned int(std::string const&amp;)&gt;;


Members

Looking at the data members of BloomFilter.

    // This member is not used anywhere.
    // but should also be the same value as hashFunctions.size()
    // So I think we can remove this.
    unsigned int numberOfFunctions;

    // Here we have a pointer data member. This is usually a bad
    // idea. We want to encourage the rule of zero. So we want to 
    // use classes that do their own memory management.
    bool* cell;
    // More about this in notes on the destructor.


So I would use:

    std::vector&lt;bool&gt;   cell;


Constructor

Just like normal variable declarations, prefer to initialize members one per line. There are no prizes for using the least number of rows possible.

Also like the hash function you are passing the parameters by value (and thus inducing a copy). Here we can pass funcs by const reference.

    BloomFilter(unsigned int numbCells, std::vector&lt;HashFunction&gt; funcs) : numberOfCells(numbCells), hashFunctions(funcs) {
        cell = (bool*)calloc(numbCells, sizeof(bool));
    }

    // Can be

    BloomFilter(unsigned int numbCells, std::vector&lt;HashFunction&gt; const&amp; funcs)
        : numberOfCells(numbCells)
        , hashFunctions(funcs)
        , cell(numbCells)          // assuming we change cell to vector
    {}


As a side note:

        // Please don't use C-Casts.
        // These are completely unchecked. You are basically
        // telling the compiler to ignore all errors.
        cell = (bool*)calloc(numbCells, sizeof(bool));
        // Also there is no check to see if calloc() failed.


It should look more like this:

        cell = static_cast&lt;bool*&gt;(calloc(number, sizeof(bool));
        if (cell == null) {
            throw bad_alloc;  // maybe another exception.
        }

        // or better yet use new/delete
        cell = new bool[number]();      // need a call to delete [] cell; in destructor: The braces force value initialization that is false.

        // or better yet use a type like vector that does memory management correctly.
           // No action needed as you did it all in the initializer list.


addElement

Passing parameter by value again. Prefer to pass by const reference.

    void addElement(std::string str) {
    // So prefer
    void addElement(std::string const&amp; str) {


When using iterators prefer to use the pre-increment ++iter rather than the post increment iter++. There is slight performance environment.

So you use the standard for loop from C++03

        for (std::vector&lt;HashFunction&gt;::iterator iter = hashFunctions.begin(); iter != hashFunctions.end(); iter++) {
            cell[(*iter)(str) % numberOfCells] = true;
        }


We can improve this in C++11 using auto.

        for (auto iter = hashFunctions.begin(); iter != hashFunctions.end(); ++iter) {
            cell[(*iter)(str) % numberOfCells] = true;
        }


In C++14 we now have the range based for loop.

        for (auto const&amp; item: hashFunctions) {
            cell[item(str) % numberOfCells] = true;
        }


searchElement

Passing parameter by value again. Prefer to pass by const reference.

    bool searchElement(std::string str) {
    // should be
    bool searchElement(std::string const &amp; str) {


The first note I want to make is the name. A searchX() function usually return a reference to something. I would name it checkX().

In this case rather than using a for loop you can use one of the standard function. In this case we can use find(). You are looking for a failure (but that's a thing).

    auto f = std::find(std::begin(hashFunctions), std::end(hashFunctions),
                       [&amp;cell, &amp;str](HashFunction hash){
                           return !cell[hash(str)];
                       });
    return f != std::end(hashFunctions);


Destructor

We should note that in C++11 we moved to using nullptr rather than NULL.  It is type safe.

    ~BloomFilter() {
        free(cell);
        cell = NULL;  // This is actually a bad idea.
                      // It hides errors that can be detected by the
                      // debug system.
    }


One of the issues you can get into with doing your own memory management is called the double free. Usually debug version of the standard library detects double free so you can catch it during testing. But by setting the value to nullptr in the destructor prevents that check.

The other thing you need to note is that by default the compiler will generate four methods for a class if you don't define them.

  BloomFilter();                             // Constructor.
  BloomFilter(BloomFilter const&amp;);           // Copy constructor.
  BloomFilter&amp; operator=(BloomFilter const&amp;);// Assignment operator
  ~BloomFilter();                            // Destrutor.


You define the Constructor and Destructor but the other two you don't define. So the compiler will generate the other two for you. Usually these work fine. BUT if your class contains an owned RAW pointer then the default compiler generated versions don't work as you would expect.

So you should look up the rule of three.
If you have to define one of Copy Constructor/Copy Assignment or destructor you probably need to do all three. If your class only uses objects that obey the rule of three already then your class will obey the rule of zero automatically (look them up). 

Design Review

Asking user to provide a large set of hash functions is unreasonable. Hash functions are hard to define in the first place and even harder to get a good distribution with over a set of them. You need to define some way that in most cases the hash is provided (though being able to provide your own is a nice touch).