Reputation: 19236
Let's suppose I'm developing a small movie database. In dynamic language like python I'd write:
db = ConnectionFactory.get_connection(db_name) # (1) Create database connection
model = MovieModel(db) # (2) object-to-relational mapper
star_wars = Movie("Star Wars", ...)
model.store(star_wars) # (3) Save object
et = model.fetch("E.T.") # (4) Retrieve one object
movies = model.fetch_all() # (5) Retrieve all objects
When translating this to C++, problem arises how to pass objects around: by value, by reference, by plain-old-pointers, or by one of a few smart pointers.
Line 1 incorporates an Abstract Factory, which returns subclass of some base class, so returning a pointer is necessary here. Question is, what kind of pointer should be returned? std::unique_ptr
seems a good fit.
In line 2 MovieModel
is constructed, which takes a database connection as argument and stores it for future use. As one connection could be shared between one or more Models, database connection should be passed and stored as std::shared_ptr
, I presume. But then, should I convert std::unique_ptr
created by ConnectionFactory
to shared_ptr
or modify ConnectionFactory
to return shared_ptr
?
In line 3, an object being stored in database. As Movie
is simple "data" class, it should be passed by value, or better, by const reference. That's simple, but in line 4 another object is retrieved. Since Movie
has "value" semantics, returning by value seems natural. But what happens when particular movie is not found? Is it better to throw an exception, or change return type to smart pointer and return a nullptr
in that case?
Last, a collection of objects is returned. Is it better to return a container (e.g, std::vector
) of objects, or container of (smart)pointers. In latter case, which pointers?
Upvotes: 3
Views: 1141
Reputation: 38961
Case 1+2: I think it would make sense to already initially return by shared_ptr
, as a DB connection seems to be inherently "sharable" in this design. That way, Case2 solves itself.
Case 3: Pass Movie
by const reference. If Movie
is a value-like class (and doesn't have any expensive-to-copy handle like stuff), a simple single version of store
with a const-ref parameter should do it and keep the code simple.
If you need to model "not found" in a fetch
method, use boost::optional<Movie>
(or something similar) as return value.
Case Last: for Movie
use a collection by-value (std::vector<Movie>
). (In C++11 the returned vector will be moved anyway.)
Upvotes: 3
Reputation: 27684
db = ConnectionFactory.get_connection(db_name) # (1) Create database connection
Who owns db? When it is destroyed?
std::unique_ptr
std::unique_ptr
and pass raw pointers to other objectsstd::shared_ptr
Note in all the above cases, factory should just return a unique_ptr
unless it returns an existing shared connection which case it is a shared_ptr
. unique_ptr
can automatically be upgraded to shared_ptr
model = MovieModel(db) # (2) object-to-relational mapper
std::move(db)
db.get()
star_wars = Movie("Star Wars", ...) model.store(star_wars) # (3) Save object
Two options:
const Movie&
and Movie&&
If you don't want to use star_wars again in this function, call std::move(star_wars)
et = model.fetch("E.T.") # (4) Retrieve one object
As object with exception or optional<Movie>
movies = model.fetch_all() # (5) Retrieve all objects
std::vector<Movie>
Upvotes: 2
Reputation: 1404
edit: I said some not so clever things here, completely ignoring the C++11 in the title.
Some personal opinions on 3 and 5:
3) I can not think of any reason to use value here instead of const reference. At least as long as the storing does not happen in some very unusual way.
Last point: I would always prefer a container of pointers to minimize random copying around of memory. In C++11 you can probably use either unique_ptr or shared_ptr in them, depending on what they are used for later.
Upvotes: 1