【问题标题】:Raw pointer or std::shared_ptr in std::stack for a db connection pool数据库连接池的 std::stack 中的原始指针或 std::shared_ptr
【发布时间】:2016-04-14 03:47:09
【问题描述】:

我搜索了 google 和 stackoverflow,但没有找到适合我特定用途的答案。关于原始指针和 std::shared_ptr 的优点有很多很好的建议,但与 std::stack 无关。

我在 youtube 上观看了 Herb Sutters 关于编写优秀 C++14 代码 (https://www.youtube.com/watch?v=hEx5DNLWGgA) 的演讲,其中一个主题是为什么唯一和共享指针很有用。

所以我改写了从原始指针到 std::shared_ptr 的简单数据库连接池。在我 top()/pop() 堆栈进行连接并与数据库交互后,我 push() 将其返回。

在构造函数中使用原始指针

Database::Database(const unsigned int connections) {
    for (int i = 0; i < connections; ++i) {
        try {
            auto* dbconn = new pqxx::connection(connectionString);
            dbpool.push(dbconn);
        } catch (const std::exception& e) {
            std::cerr << e.what() << std::endl;
        }
    }
}

后来

auto *D = dbpool.top();
dbpool.pop();

std::string query = "select * from races order by racestart_at desc";
pqxx::nontransaction N(*D);
pqxx::result R(N.exec(query));

dbpool.push(D);

在构造函数中使用 shared_ptr

auto* dbconn = new pqxx::connection(connectionString);
std::shared_ptr<pqxx::connection> s_dbconn(dbconn);
dbpool.emplace(s_dbconn);

在程序中

auto D = dbpool.top();
dbpool.pop();
<query db etc.>
dbpool.push(D);

在这种情况下,原始指针和共享指针一样好吗?我不认为原始指针会误入歧途。

【问题讨论】:

  • This 应该可以帮到你。
  • 谢谢。我读过类似的线程,其中也提到了生命周期管理,并且在生命周期比程序短的范围内使用它们时很容易体会到好处。我可以想象的用例是如果我忘记将连接 push() 回池。但是他们无论如何我将在几个周期后无法连接到数据库。

标签: c++ pointers c++11 stack shared-ptr


【解决方案1】:

原始指针可能会误入歧途,因为它们不会被自动删除,而且即使您认为自己处理了所有情况,它们也可能以多种方式泄漏。

【讨论】:

    【解决方案2】:

    一切都与所有权有关。使用共享指针,多段代码(对象)可以同时拥有指针。

    对于您使用池的示例,最好只创建一个连接对象数组(std::array 或 std::vector),并拥有另一个指针数组(或堆栈) (或索引)到第一个数组中的对象。然后,当从池中请求连接对象时,您会返回一个作为原始指针的对象,然后您需要一个函数将连接“返回”回池,以便可以重用它。

    通过这种方式,只有池实际拥有连接对象,因此它应该是唯一删除它们的对象。其他代码只是使用该对象,但不应删除它,而是应在使用完毕后将其返回到池中。

    增强这一点的一种方法是从池中返回一个特殊的对象/类,而不是原始指针。这个对象就像一个管理连接对象借出的小 RAII 包装器。因此,一旦它被销毁,它将连接对象返回到池中。我相信您可以为此使用带有自定义删除器的 unique_ptr。

    【讨论】:

    • 谢谢。使用双向量/堆栈来管理连接听起来是个好主意。
    【解决方案3】:

    由于您实际上是在有人请求资源时从池中删除了指针,所以智能指针肯定是您应该做的。原因是您无法删除析构函数中的所有资源,因为您并非始终拥有所有可用的指针。

    如果你想使用原始指针,你应该一直保留类中的所有指针,以便在数据库被破坏时删除它们。这将需要重新设计,以将各个资源标记为已占用/空闲。

    也就是说 - 请记住,使用池与动态内存分配相同。

    如果有人忘记将资源还给池,您的程序迟早会崩溃。

    如果有人忘记删除动态分配的内存,你的程序迟早会崩溃。

    同样的故事……

    换句话说 - 池与动态内存分配一样危险。

    【讨论】:

      猜你喜欢
      • 2012-09-07
      • 2013-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多