C++11智能指针用例

C++11 smart pointers use cases

本文关键字:指针 智能 C++11      更新时间:2023-10-16

假设我正在开发一个小型电影数据库。在像python这样的动态语言中,我会写:

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

当将其转换为C++时,会出现如何传递对象的问题:通过值、通过引用、通过普通的旧指针或通过少数智能指针之一。

第1行包含了一个抽象工厂,它返回某个基类的子类,所以在这里返回指针是必要的。问题是,应该返回什么样的指针?std::unique_ptr似乎很合适。

在第2行中,构造了MovieModel,它将数据库连接作为参数并存储它以备将来使用。由于一个连接可以在一个或多个模型之间共享,所以我认为数据库连接应该传递并存储为std::shared_ptr。但是,我应该将ConnectionFactory创建的std::unique_ptr转换为shared_ptr还是修改ConnectionFactory以返回shared_ptr

在第3行中,一个存储在数据库中的对象。由于Movie是一个简单的"数据"类,所以它应该通过值传递,或者更好地通过const引用传递。这很简单,但在第4行中检索到了另一个对象。由于Movie具有"值"语义,按值返回似乎很自然。但当找不到特定的电影时会发生什么呢?在这种情况下,是抛出异常更好,还是将返回类型更改为智能指针并返回nullptr更好?

最后,返回一个对象集合。返回对象的容器(例如std::vector)或(智能)指针的容器更好吗。在后一种情况下,哪些指针?

案例1+2:我认为最初由shared_ptr返回是有意义的,因为在这种设计中,DB连接似乎天生就"可共享"。这样,案例2就能自行解决。

情况3:通过常量引用传递Movie。如果Movie是一个类似值的类(并且没有任何昂贵的复制句柄的东西),那么一个简单的带有const-ref参数的store单一版本应该可以做到这一点,并保持代码简单。

如果您需要在fetch方法中对"未找到"进行建模,请使用boost::optional<Movie>(或类似的东西)作为返回值。

Case Last:对于Movie,按值使用集合(std::vector<Movie>)。(在C++11中,返回的矢量无论如何都会被移动。)

db=ConnectionFactory.get_connection(db_name)#(1)创建数据库连接

谁拥有数据库?什么时候被摧毁?

  1. 这个堆栈拥有它,最终应该销毁=>使用std::unique_ptr
  2. 这保存在类(例如MovieManager)成员中,只要所有其他对象=>使用std::unique_ptr并将原始指针传递给其他对象,该类就会一直存在
  3. db可能比该堆栈和类更长寿,并与其他对象共享=>std::shared_ptr

请注意,在上述所有情况下,工厂应该只返回一个unique_ptr,除非它返回一个现有的共享连接,在这种情况下它是shared_ptrunique_ptr可自动升级为shared_ptr

model=影片model(db)#(2)对象到关系映射器

  1. 如果db是std::unique_ptr,并且您希望MoviewModel从现在起拥有所有权,则std::move(db)
  2. 模型将在此堆栈中销毁,通过db.get()
  3. 模型需要共享所有权,因为任何模型都可能最后被销毁,作为shared_ptr传递
star_wars = Movie("Star Wars", ...) 
model.store(star_wars)    # (3) Save object

两种选择:

  1. 使const Movie&Movie&&的存储过载
  2. 使商店按值获取电影

如果您不想在此函数中再次使用star_wars,请调用std::move(star_wars)

et=model.fetch("E.T.")#(4)检索一个目标

作为异常对象或optional<Movie>

movies=model.fetch_all()#(5)检索所有对象

std::vector<Movie>

edit:我在这里说了一些不太聪明的事情,完全忽略了标题中的C++11。

关于3和5的一些个人意见:

3) 我想不出有什么理由在这里使用value而不是const引用。至少只要存储不会以某种非常不寻常的方式发生。

最后一点:我总是喜欢一个指针容器,以尽量减少内存的随机复制。在C++11中,您可能可以在其中使用unique_ptr或shared_ptr,这取决于它们以后的用途。