C++智能指针混乱
C++ smart pointers confusion
我知道在C++领域提倡使用智能指针。我有一个简单的程序如下。
/* 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;
}
当我用unique_ptr行编译时收到一条错误消息:
error C2027: use of undefined type 'sqlite3'
error C2338: can't delete an incomplete type
当我用shared_ptr行编译时,它是成功的。从几个问题和答案中,我的理解是应该首选unique_ptr,因为我不打算让对象共享资源。在这种情况下,最好的解决方案是什么?使用shared_ptr还是回到裸指针的旧方法(新建/删除(?
一般方法是在@SomeProgrammerDudes的答案中(接受它(。但为了解决您的担忧,我发布了这个。
您不应该返回到原始新建并删除。既不是因为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>;
sqlite3
是一个不透明的结构(很像CFILE
(。你所拥有的只是它的声明,而不是它的定义。这意味着如果没有自定义删除器,您将无法直接在std::unique_ptr
中使用它。
#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();
}
shared_ptr
的解决方案
我正在学习C++和SQLite,所以我也有这个问题。读完这篇文章后,我尝试了一些答案。结果是一个工作示例和一个小分析。
- 首先为智能指针创建自定义删除器。
- 然后,创建一个包含自定义删除程序的空share_ptr
- 然后,为 DB 处理程序创建一个空指针 (
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 使用智能指针?
使用智能指针的主要原因是自动进行内存管理并避免内存泄漏。因此,如果我们考虑在免费商店上分配内存,就会发生这种情况, 使用new
和delete
.
但是我在免费商店中分配数据库处理程序的所有尝试都失败了。
失败 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 对象(。那么为什么要使用智能指针呢?即使您在堆栈上分配数据库处理程序,智能指针也会使用更多内存和更多代码行。
使用智能指针的另一个原因是管理所有权。但是,在 sqlite3 中,工作流非常重复: 在例程中:
- 创建数据库处理程序。
- 打开数据库,执行SQL语句等。
- 完成声明
- 完成数据库连接。
所以我不明白我们为什么要在这个工作流之外传递一个数据库处理程序。
我的建议是继续使用原始指针并用sqlite3_close(sqlite3 * ptr)
销毁它们。
- 1d 智能指针不适用于语法 (*)++
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 为什么使用 "this" 指针调用派生成员函数?
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用指针从C++中的数组中获取最大值
- 助记符和指向成员语法的指针
- 嵌入方指针压缩已禁用
- 常量指针上的混乱
- 数组和指针使算术混乱
- C++智能指针混乱
- 指针混乱char* and int*
- 链接列表插入,指针混乱
- 指针地址和参考混乱
- 链表中的指针算术混乱
- 交换功能 - 指针 - 混乱
- C ,向量,指针和对象混乱
- 指针,矢量列表=大混乱
- 指向数组混乱的指针
- 使用 std::sort 对指针向量进行排序,导致地址混乱
- 我有一些混乱的指针删除和分配指针到类