unique_ptr 的移动构造函数的实现问题
Problems with implementation of unique_ptr's move constructor
我正在尝试编写 unique_ptr 实现。我正在努力编写移动构造函数。这是我的问题:
- 当我将移动构造函数标记为
default
时,我的资源被删除了两次,当我移动分配指针时(下面的 auto foo2 = std::move(foo);
)- 为什么?
- 当我尝试像这样
*rhs = nullptr
那样在移动构造函数中分配底层指针时(参见下面的实现),编译器说 *rhs
是一个右值,我不能将任何东西分配给它。
- 终于,
rhs.m_ptr = nullptr
成功了。为什么它有效,而 *rhs = nullptr
无效?
我的代码:
#include <iostream>
namespace my
{
template <class T>
class unique_ptr
{
public:
unique_ptr()
{
m_ptr = new T;
}
unique_ptr(const unique_ptr&) = delete;
// move constructor
unique_ptr(unique_ptr&& rhs) // = default deletes m_ptr twice
{
m_ptr = *rhs;
rhs.m_ptr = nullptr; // *rhs = nullptr doesn't work (*rhs is an rvalue)
}
~unique_ptr()
{
delete m_ptr;
}
T* operator->()
{
return m_ptr;
}
T* operator*()
{
return m_ptr;
}
unique_ptr& operator=(const unique_ptr&) = delete;
// no move assignment yet
private:
T* m_ptr;
};
} // namespace my
struct Foo
{
Foo()
{
std::cout << "Foo" << std::endl;
}
~Foo()
{
std::cout << "~Foo" << std::endl;
}
void printHello()
{
std::cout << "Hello" << std::endl;
}
};
int main()
{
my::unique_ptr<Foo> foo;
foo->printHello();
auto foo2 = std::move(foo);
return 0;
}
附带说明一下,显然我可以将不带任何模板参数的 unique_ptr 传递给 unique_ptr class 模板内的方法。编译器是否只是假设它是 T?
请丢弃与所述问题无关的任何其他实施错误。正在进行中。
1) 默认移动构造函数不知道您的 class 的语义。所以它移动了指针rhs
,但它不会重置另一个指针,它也会在另一个析构函数中被删除。
2) *rhs
调用operator*
和return一个temporary/rvalueT*
,内部指针的拷贝,和通常的不一致operator*
应该 return 一个 T&
或一个 const T&
.
3) 请参阅 2。您正在 return 创建一个临时对象。
所以最后,你应该拥有:
unique_ptr(unique_ptr&& rhs) // = default deletes m_ptr twice
: m_ptr(rhs.m_ptr)
{
rhs.m_ptr = nullptr; // *rhs = nullptr doesn't work (*rhs is an rvalue)
}
T& operator*() {return *m_ptr;}
const T& operator*() const {return *m_ptr;}
以此类推
你太努力了。您不必通过外部接口。只需赋值:
m_ptr = rhs.m_ptr;
rhs.m_ptr = nullptr;
此外,operator*()
应该return一个T&
,而不是T*
。
我正在尝试编写 unique_ptr 实现。我正在努力编写移动构造函数。这是我的问题:
- 当我将移动构造函数标记为
default
时,我的资源被删除了两次,当我移动分配指针时(下面的auto foo2 = std::move(foo);
)- 为什么? - 当我尝试像这样
*rhs = nullptr
那样在移动构造函数中分配底层指针时(参见下面的实现),编译器说*rhs
是一个右值,我不能将任何东西分配给它。 - 终于,
rhs.m_ptr = nullptr
成功了。为什么它有效,而*rhs = nullptr
无效?
我的代码:
#include <iostream>
namespace my
{
template <class T>
class unique_ptr
{
public:
unique_ptr()
{
m_ptr = new T;
}
unique_ptr(const unique_ptr&) = delete;
// move constructor
unique_ptr(unique_ptr&& rhs) // = default deletes m_ptr twice
{
m_ptr = *rhs;
rhs.m_ptr = nullptr; // *rhs = nullptr doesn't work (*rhs is an rvalue)
}
~unique_ptr()
{
delete m_ptr;
}
T* operator->()
{
return m_ptr;
}
T* operator*()
{
return m_ptr;
}
unique_ptr& operator=(const unique_ptr&) = delete;
// no move assignment yet
private:
T* m_ptr;
};
} // namespace my
struct Foo
{
Foo()
{
std::cout << "Foo" << std::endl;
}
~Foo()
{
std::cout << "~Foo" << std::endl;
}
void printHello()
{
std::cout << "Hello" << std::endl;
}
};
int main()
{
my::unique_ptr<Foo> foo;
foo->printHello();
auto foo2 = std::move(foo);
return 0;
}
附带说明一下,显然我可以将不带任何模板参数的 unique_ptr 传递给 unique_ptr class 模板内的方法。编译器是否只是假设它是 T?
请丢弃与所述问题无关的任何其他实施错误。正在进行中。
1) 默认移动构造函数不知道您的 class 的语义。所以它移动了指针rhs
,但它不会重置另一个指针,它也会在另一个析构函数中被删除。
2) *rhs
调用operator*
和return一个temporary/rvalueT*
,内部指针的拷贝,和通常的不一致operator*
应该 return 一个 T&
或一个 const T&
.
3) 请参阅 2。您正在 return 创建一个临时对象。
所以最后,你应该拥有:
unique_ptr(unique_ptr&& rhs) // = default deletes m_ptr twice
: m_ptr(rhs.m_ptr)
{
rhs.m_ptr = nullptr; // *rhs = nullptr doesn't work (*rhs is an rvalue)
}
T& operator*() {return *m_ptr;}
const T& operator*() const {return *m_ptr;}
以此类推
你太努力了。您不必通过外部接口。只需赋值:
m_ptr = rhs.m_ptr;
rhs.m_ptr = nullptr;
此外,operator*()
应该return一个T&
,而不是T*
。