C ++智能指针混乱

时间:2017-07-20 12:06:52

标签: c++ c++11 sqlite smart-pointers

据我所知,在C ++领域,它主张使用智能指针。我有一个简单的程序如下。

**startsWith()**

当我使用unique_ptr行编译时收到错误消息:

/* main.cpp */
#include <iostream>
#include <memory>
using namespace std;

/* SQLite */
#include "sqlite3.h"

int main(int argc, char** argv)
{
    // unique_ptr<sqlite3> db = nullptr; // Got error with this
    shared_ptr<sqlite3> db = nullptr;

    cout << "Database" << endl;
    return 0;
}

当我使用shared_ptr行编译时,它成功了。从几个问题和答案我的理解是unique_ptr应该是首选,因为我不打算让对象共享资源。在这种情况下,最佳解决方案是什么?使用shared_ptr还是回到裸指针的旧方法(new / delete)?

4 个答案:

答案 0 :(得分:5)

一般方法是@ SomeProgrammerDudes的答案(接受它)。但是为了解决你的问题,我发布了这个。

你不应该回到raw new并删除。也不是因为sqlite3是一个不透明的类型,也不是因为std::shared_ptr的开销。作为指定的其他答案,您使用std::unique_tr

唯一的区别是您如何设置自定义删除器。对于std::unique_ptr,它是类型定义的一部分,而不是运行时参数。所以你需要做这样的事情:

struct sqlite3_deleter {
  void operator()(sqlite3* sql) {
    sqlite3_close_v2(sql);
  }
};

using unique_sqlite3 = std::unique_ptr<sqlite3, sqlite3_deleter>;

答案 1 :(得分:4)

sqlite3不透明结构(非常类似于C中的FILE)。你所拥有的只是它的声明,而不是它的定义。这意味着如果没有自定义删除工具,您无法直接在std::unique_ptr中使用它。

答案 2 :(得分:1)

#include <memory>
#include <stdexcept>

/* sqlite 3 interface */
struct sqlite3 {};
extern void sqlite3_close(sqlite3*);
extern int sqlite3_open(sqlite3**);

/* our boilerplate */
struct closer
{
    void operator()(sqlite3* p) const
    {
        sqlite3_close(p);
    }
};

using sqlite3_ptr = std::unique_ptr<sqlite3, closer>;

/* handy maker function */
sqlite3_ptr make_sqlite()
{
    sqlite3* buffer = nullptr;
    int err = sqlite3_open(&buffer);
    if (err) {
        throw std::runtime_error("failed to open sqlite");
    }
    return sqlite3_ptr(buffer);
}

int main()
{
    auto mysqlite = make_sqlite();
}

答案 3 :(得分:1)

使用shared_ptr

的解决方案

我正在学习C ++和SQLite,所以我也有这个问题。阅读这篇文章后,我尝试了一些答案。结果是一个可行的示例和一个简短的分析。

  • 首先为智能指针创建一个自定义删除器。
  • 然后,创建一个包含自定义删除器的空share_ptr
  • 然后,为数据库处理程序(sqlite3 * DB;)创建一个空指针
  • 然后,打开/创建数据库。
  • 将原始指针链接到共享指针。
  • shared_ptr超出范围后,它也会删除原始指针。

这效率不高(请参见结论),但这是我设法在sqlite3中使用智能指针的唯一方法,因此我决定将其发布为答案。

#include <iostream>
#include<sqlite3.h>
#include<memory>

//Custom deleter
auto del_sqlite3 = [](sqlite3* pSqlite)
{
    std::cout << "Calling custom deleter." << std::endl;
    sqlite3_close_v2(pSqlite);
};

int main()
{
//Uncomment to run
//const char* dir = "C:\\test\\db_dir\\test.db"
openOrCreateDB(dir);
return 0;
}


int openOrCreateDB(const char* dirName)
{
    std::shared_ptr<sqlite3> DB(nullptr, del_sqlite3);//custom deleter
    auto pDB = DB.get();
    {
        int exit = sqlite3_open(dirName, &pDB);
        DB.reset(pDB);// Replace nullptr with pDB and link
     }
    return 0;
}

为什么要使用sqlite3智能指针?

使用智能指针的主要原因是自动执行内存管理并避免内存泄漏。因此,如果我们正在考虑使用newdelete在免费存储区上分配内存,则会发生这种情况。

但是我在免费存储区中分配数据库处理程序的所有尝试都失败了。

失败1:使用sqlite3* DB = new sqlite3;

int openOrCreateDB(const char* dirName)
{
    sqlite3* DB = new sqlite3;//E0070: Incomplete type not allowed
    int exit = sqlite3_open(dirName, &DB);
    sqlite3_close(DB);
    return 0;
}

失败2:使用share_ptr

static int openOrCreateDB(const char* dirName)
{
   
    std::shared_ptr<sqlite3> DB(new sqlite3, del_sqlite3);// Incomplete type not allowed
    auto pDB = DB.get();
    {
        int exit = sqlite3_open(dirName, &pDB);
        DB.reset(pDB);
     }
    
    return 0;
}

失败3:使用make_shared

我什至没有尝试。在 Meyers的有效现代C ++ 中,第21项清楚地表明,您不能使用make_shared在具有自定义删除器的堆上构造智能指针。

结论

也许我做错了,但是SQLite似乎不喜欢在堆上分配数据库处理程序(sqlite3对象)。那么为什么还要使用智能指针呢?即使在堆栈上分配db处理程序,智能指针也会占用更多内存和更多代码行。

使用智能指针的另一个原因是管理所有权。但是,在sqlite3中,工作流程非常重复:在例行程序中:

  • 创建数据库处理程序。
  • 打开数据库,执行SQL语句等。
  • 完成声明
  • 完成数据库连接。

所以我看不到为什么要在此工作流程之外传递一个数据库处理程序。

我的建议是继续使用原始指针,并使用sqlite3_close(sqlite3 * ptr)破坏它们。