What is the best smart pointer return type for a factory function?

Malvineous picture Malvineous · Feb 3, 2015 · Viewed 14k times · Source

With respect to smart pointers and new C++11/14 features, I am wondering what the best-practice return values and function parameter types would be for classes that have these facilities:

  1. A factory function (outside of the class) that creates objects and returns them to users of the class. (For example opening a document and returning an object that can be used to access the content.)

  2. Utility functions that accept objects from the factory functions, use them, but do not take ownership. (For example a function that counts the number of words in the document.)

  3. Functions that keep a reference to the object after they return (like a UI component that takes a copy of the object so it can draw the content on the screen as needed.)

What would the best return type be for the factory function?

  • If it's a raw pointer the user will have to delete it correctly which is problematic.
  • If it returns a unique_ptr<> then the user can't share it if they want to.
  • If it's a shared_ptr<> then will I have to pass around shared_ptr<> types everywhere? This is what I'm doing now and it's causing problems as I'm getting cyclic references, preventing objects from being destroyed automatically.

What is the best parameter type for the utility function?

  • I imagine passing by reference will avoid incrementing a smart pointer reference count unnecessarily, but are there any drawbacks of this? The main one that comes to mind is that it prevents me from passing derived classes to functions taking parameters of the base-class type.
  • Is there some way that I can make it clear to the caller that it will NOT copy the object? (Ideally so that the code will not compile if the function body does try to copy the object.)
  • Is there a way to make it independent of the type of smart pointer in use? (Maybe taking a raw pointer?)
  • Is it possible to have a const parameter to make it clear the function will not modify the object, without breaking smart pointer compatibility?

What is the best parameter type for the function that keeps a reference to the object?

  • I'm guessing shared_ptr<> is the only option here, which probably means the factory class must return a shared_ptr<> also, right?

Here is some code that compiles and hopefully illustrates the main points.

#include <iostream>
#include <memory>

struct Document {
    std::string content;
};

struct UI {
    std::shared_ptr<Document> doc;

    // This function is not copying the object, but holding a
    // reference to it to make sure it doesn't get destroyed.
    void setDocument(std::shared_ptr<Document> newDoc) {
        this->doc = newDoc;
    }
    void redraw() {
        // do something with this->doc
    }
};

// This function does not need to take a copy of the Document, so it
// should access it as efficiently as possible.  At the moment it
// creates a whole new shared_ptr object which I feel is inefficient,
// but passing by reference does not work.
// It should also take a const parameter as it isn't modifying the
// object.
int charCount(std::shared_ptr<Document> doc)
{
    // I realise this should be a member function inside Document, but
    // this is for illustrative purposes.
    return doc->content.length();
}

// This function is the same as charCount() but it does modify the
// object.
void appendText(std::shared_ptr<Document> doc)
{
    doc->content.append("hello");
    return;
}

// Create a derived type that the code above does not know about.
struct TextDocument: public Document {};

std::shared_ptr<TextDocument> createTextDocument()
{
    return std::shared_ptr<TextDocument>(new TextDocument());
}

int main(void)
{
    UI display;

    // Use the factory function to create an instance.  As a user of
    // this class I don't want to have to worry about deleting the
    // instance, but I don't really care what type it is, as long as
    // it doesn't stop me from using it the way I need to.
    auto doc = createTextDocument();

    // Share the instance with the UI, which takes a copy of it for
    // later use.
    display.setDocument(doc);

    // Use a free function which modifies the object.
    appendText(doc);

    // Use a free function which doesn't modify the object.
    std::cout << "Your document has " << charCount(doc)
        << " characters.\n";

    return 0;
}

Answer

Mike Seymour picture Mike Seymour · Feb 3, 2015

What would the best return type be for the factory function?

unique_ptr would be best. It prevents accidental leaks, and the user can release ownership from the pointer, or transfer ownership to a shared_ptr (which has a constructor for that very purpose), if they want to use a different ownership scheme.

What is the best parameter type for the utility function?

A reference, unless the program flow is so convoluted that the object might be destroyed during the function call, in which case shared_ptr or weak_ptr. (In either case, it can refer to a base class, and add const qualifiers, if you want that.)

What is the best parameter type for the function that keeps a reference to the object?

shared_ptr or unique_ptr, if you want it to take responsibility for the object's lifetime and not otherwise worry about it. A raw pointer or reference, if you can (simply and reliably) arrange for the object to outlive everything that uses it.