C++ primer 练习题:
确保资源被释放的一种简单方法是使用智能指针。
假设我们正在使用 C 和 C++ 都使用的网络库。使用这个库的程序可能包含如下代码:
struct connection
{
string ip;
int port;
connection(string i, int p) :ip(i), port(p) {};
}; // represents what we are connecting to
struct destination
{
string ip;
int port;
destination(string i, int p) :ip(i), port(p) {};
}; // information needed to use the connection
void disconnect(connection c)
{
cout << "Scoket(" << c.port << "," << c.port << ") disconnect" << endl;
} //close the given connection
connection connect(destination* pDest)
{
shared_ptr<connection> pConn(new connection(pDest->ip, pDest->port), disconnect );
cout << "Scoket(" << pConn->ip << "," << pConn->port << ") connect" << endl;
return *pConn;
} // open the connection
void f(destination& d)
{
connection c = connect(&d);
}
编译错误:
error C2661: 'std::shared_ptr<connection>::shared_ptr': no overloaded function takes 2 arguments.
代码有什么问题?
来自cppreference,确实有一个需要两个参数的重载,但它并没有按照你想要的方式工作。您正在查看过载 (4)。
template< class Y, class Deleter > shared_ptr( Y* ptr, Deleter d );
使用指定的删除器
作为删除器。 表达式d
必须格式正确, 具有明确定义的行为并且不会抛出任何异常。d(ptr)
和从中复制的存储删除器的构造不得抛出异常。d
(强调我的)
请注意,删除器必须是一个可调用对象,它接受
Y*
类型的参数,即指向 std::shared_ptr
中数据的指针。所以你的 disconnect
函数,如果你打算将它用作 std::shared_ptr
的删除器,应该有签名
void disconnect(connection* c)
带有指针参数。
另请注意,正如所写,您的共享指针已经很可能表现出未定义的行为。
connection connect(destination* pDest) { shared_ptr<connection> pConn(new connection(pDest->ip, pDest->port), disconnect ); cout << "Scoket(" << pConn->ip << "," << pConn->port << ") connect" << endl; return *pConn; }
让我们分解一下你在这里做了什么。首先,您创建一个指向新连接对象的共享指针。该连接对象由
std::shared_ptr
拥有和管理。当不再有 std::shared_ptr
对象指向该内存时,它将被释放,并且您的删除器将运行。然后返回底层连接(的副本)。 std::shared_ptr
是一个局部变量,理所当然地被释放,这是指向您分配的 connection*
的唯一共享指针。因此,应调用disconnect
。 connect
返回后,它会返回已关闭的连接的副本。如果有人试图使用 connection
对象,它将失败,或者更糟的是,表现出未定义的行为。
如果你打算使用智能指针,你必须坚持下去。您的
connect
函数必须返回一个 std::shared_ptr<connection>
,然后由调用者管理。如果你返回的只是一个原始指针,那么构建一个共享指针是没有用的。
你给你的
std::shared_ptr
的删除器需要接受一个与shared_ptr
管理的相同类型的指针。所以对于一个std::shared_ptr<connection>
,删除者需要接受一个connection*
,但这不是disconnect
接受的。签名不匹配,程序编译失败。
值得注意的是,您的示例中还有其他几个问题。
disconnect
没有delete
传递给它的对象,所以pConn
管理的对象会泄漏。此外,connect
返回 pConn
指向的对象的副本,因此根本没有理由使用 std::shared_ptr
或动态分配。