문제

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?

도움이 되었습니까?

해결책

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 Movieuse a collection by-value (std::vector<Movie>). (In C++11 the returned vector will be moved anyway.)

다른 팁

db = ConnectionFactory.get_connection(db_name) # (1) Create database connection

Who owns db? When it is destroyed?

  1. This stack owns it and should be destroyed in the end => use std::unique_ptr
  2. This is saved in a class(eg MovieManager) member and the class will live as long as all other objects => use std::unique_ptr and pass raw pointers to other objects
  3. db may outlive this stack and the class and is shared with other objects => std::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

  1. if db was std::unique_ptr and you want MoviewModel to take ownership from now on, std::move(db)
  2. Model will be destroyed in this stack, pass db.get()
  3. Model needs shared ownership as any of the models might be destroyed last, pass as shared_ptr
star_wars = Movie("Star Wars", ...) 
model.store(star_wars)    # (3) Save object

Two options:

  1. Make store overloaded for const Movie& and Movie&&
  2. Make store take Movie by value

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>

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.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top