C++ 如何将对象移动到 nullptr
C++ how to move object to a nullptr
我在想一个奇怪的用例,我想将一个对象移动到 nullptr。也许我应该给出一个代码片段:
class Objpair {
public:
Objpair(Obj&& a, Obj&&b) : first(&a), second(&b) { }
private:
Obj* first;
Obj* second;
};
问题是当a和b超出范围时,第一个和第二个指针将悬空。如果我可以将对象 a 移动到第一个指针上,那么应该没有双重释放和范围问题。如果成员 first 被声明为 Obj 而不是 Obj* 指针,那么直接 first(std::move(a)) 就可以完成这项工作。我一定是在这里做错了什么。我正在考虑移动而不是复制,因为我正在尝试将控制权从另一个对象转移到当前对象并提高性能。
使用指针版本是因为我正在考虑成员对象的多态行为。
您不能将对象移动到指针(nullptr 或其他)上。您也不能通过让指针指向对象来延长对象的生命周期 - C++ 根本无法那样工作。
由于您需要多态行为,您不能只是复制构造或分配一个 Obj
指针。正如这个问题中所讨论的,您可能需要 class Obj
的 clone()
方法之类的东西:
Copying derived entities using only base class pointers, (without exhaustive testing!) - C++
了解更多信息。不过请记住——您无法延长右值引用的生命周期,因此您必须以某种方式构造一个新对象。还要记住避免内存泄漏。所以如果你的 clone()
产生一个 Obj*
,你需要写这样的东西:
class Objpair {
public:
Objpair(Obj&& a, Obj&&b) :
first(a.clone()),
second(b.clone())
{ }
private:
std::unique_ptr<Obj> first;
std::unique_ptr<Obj> second;
};
也许 std::shared_ptr
's instead of the unique_ptr
's (see this answer 关于在两者之间进行选择)。
如果您需要 "polymorphic" 行为,但您在编译时知道 确切 类型的 a 和 b,并且您不介意有不同的 ObjPair
classes,你也可以尝试这样的事情:
template <typename T1, typename T2>
class Objpair {
public:
Objpair(T1&& a, T2&&b) : first(a), second(b)
{ }
private:
T1 first;
T2 second;
};
在这种情况下不涉及指针 (!)
在评论中,您说,
first and second can point to BaseClass, Derived1, Derived2,
在这种情况下,您最好的选择是在 Obj
中提供一个 virtual
成员函数来克隆一个对象。
class Obj
{
public:
virtual Obj* clone() const = 0;
...
}
并用它来初始化first
和second
。
class Objpair {
public:
Objpair(Obj&& a, Obj&&b) : first(a.clone()), second(b.clone()) { }
private:
Obj* first;
Obj* second;
};
并确保在析构函数中处理 first
和 second
指向的内存的释放。有关详细信息,请参阅 The Rule of Three。
您可以使用智能指针来消除 class 中用于管理对象生命周期的代码的需要。使用 std::unique_ptr
还是 std::shared_ptr
取决于您希望如何使用 Objpair
.
您可以做的是从您的参数中移动构造 对象,如下所示:
class Objpair {
public:
Objpair(Obj&& a, Obj&& b)
: first(new Obj(std::move(a)))
, second(new Obj(std::move(b)))
{}
private:
Obj* first;
Obj* second;
};
我推荐你使用std::unique_ptr
:
class Objpair {
public:
Objpair(Obj&& a, Obj&& b)
: first(std::make_unique<Obj>(std::move(a)))
, second(std::make_unique<Obj>(std::move(b)))
{}
private:
std::unique_ptr<Obj> first;
std::unique_ptr<Obj> second;
};
@Galik 接受的答案很好。虽然它要求 Obj
至少是可移动的,这与 Clone()
解决方案的其他答案类似 - 他们都对 Obj
及其后代提出了一些要求。要删除此限制,我们可以更改 Objpair
的构造函数以接受 std::unique_ptr<Obj>
:
#include <memory>
class Obj
{
public:
Obj() {}
// non-copyable/non-movable
Obj(const Obj&) = delete;
Obj& operator=(const Obj&) = delete;
};
class Objpair {
public:
Objpair(std::unique_ptr<Obj>&& a, std::unique_ptr<Obj>&& b)
: first(std::move(a))
, second(std::move(b))
{}
private:
std::unique_ptr<Obj> first;
std::unique_ptr<Obj> second;
};
int main()
{
Objpair p{std::make_unique<Obj>(), std::make_unique<Obj>()};
return 0;
}
可能的奖金是:
- 用户处理内存分配,因此可以更好地控制它。
- 用户可以在将
a
和 b
参数传递给 Objpair
之前使用多态的实例。
我在想一个奇怪的用例,我想将一个对象移动到 nullptr。也许我应该给出一个代码片段:
class Objpair {
public:
Objpair(Obj&& a, Obj&&b) : first(&a), second(&b) { }
private:
Obj* first;
Obj* second;
};
问题是当a和b超出范围时,第一个和第二个指针将悬空。如果我可以将对象 a 移动到第一个指针上,那么应该没有双重释放和范围问题。如果成员 first 被声明为 Obj 而不是 Obj* 指针,那么直接 first(std::move(a)) 就可以完成这项工作。我一定是在这里做错了什么。我正在考虑移动而不是复制,因为我正在尝试将控制权从另一个对象转移到当前对象并提高性能。
使用指针版本是因为我正在考虑成员对象的多态行为。
您不能将对象移动到指针(nullptr 或其他)上。您也不能通过让指针指向对象来延长对象的生命周期 - C++ 根本无法那样工作。
由于您需要多态行为,您不能只是复制构造或分配一个 Obj
指针。正如这个问题中所讨论的,您可能需要 class Obj
的 clone()
方法之类的东西:
Copying derived entities using only base class pointers, (without exhaustive testing!) - C++
了解更多信息。不过请记住——您无法延长右值引用的生命周期,因此您必须以某种方式构造一个新对象。还要记住避免内存泄漏。所以如果你的 clone()
产生一个 Obj*
,你需要写这样的东西:
class Objpair {
public:
Objpair(Obj&& a, Obj&&b) :
first(a.clone()),
second(b.clone())
{ }
private:
std::unique_ptr<Obj> first;
std::unique_ptr<Obj> second;
};
也许 std::shared_ptr
's instead of the unique_ptr
's (see this answer 关于在两者之间进行选择)。
如果您需要 "polymorphic" 行为,但您在编译时知道 确切 类型的 a 和 b,并且您不介意有不同的 ObjPair
classes,你也可以尝试这样的事情:
template <typename T1, typename T2>
class Objpair {
public:
Objpair(T1&& a, T2&&b) : first(a), second(b)
{ }
private:
T1 first;
T2 second;
};
在这种情况下不涉及指针 (!)
在评论中,您说,
first and second can point to BaseClass, Derived1, Derived2,
在这种情况下,您最好的选择是在 Obj
中提供一个 virtual
成员函数来克隆一个对象。
class Obj
{
public:
virtual Obj* clone() const = 0;
...
}
并用它来初始化first
和second
。
class Objpair {
public:
Objpair(Obj&& a, Obj&&b) : first(a.clone()), second(b.clone()) { }
private:
Obj* first;
Obj* second;
};
并确保在析构函数中处理 first
和 second
指向的内存的释放。有关详细信息,请参阅 The Rule of Three。
您可以使用智能指针来消除 class 中用于管理对象生命周期的代码的需要。使用 std::unique_ptr
还是 std::shared_ptr
取决于您希望如何使用 Objpair
.
您可以做的是从您的参数中移动构造 对象,如下所示:
class Objpair {
public:
Objpair(Obj&& a, Obj&& b)
: first(new Obj(std::move(a)))
, second(new Obj(std::move(b)))
{}
private:
Obj* first;
Obj* second;
};
我推荐你使用std::unique_ptr
:
class Objpair {
public:
Objpair(Obj&& a, Obj&& b)
: first(std::make_unique<Obj>(std::move(a)))
, second(std::make_unique<Obj>(std::move(b)))
{}
private:
std::unique_ptr<Obj> first;
std::unique_ptr<Obj> second;
};
@Galik 接受的答案很好。虽然它要求 Obj
至少是可移动的,这与 Clone()
解决方案的其他答案类似 - 他们都对 Obj
及其后代提出了一些要求。要删除此限制,我们可以更改 Objpair
的构造函数以接受 std::unique_ptr<Obj>
:
#include <memory>
class Obj
{
public:
Obj() {}
// non-copyable/non-movable
Obj(const Obj&) = delete;
Obj& operator=(const Obj&) = delete;
};
class Objpair {
public:
Objpair(std::unique_ptr<Obj>&& a, std::unique_ptr<Obj>&& b)
: first(std::move(a))
, second(std::move(b))
{}
private:
std::unique_ptr<Obj> first;
std::unique_ptr<Obj> second;
};
int main()
{
Objpair p{std::make_unique<Obj>(), std::make_unique<Obj>()};
return 0;
}
可能的奖金是:
- 用户处理内存分配,因此可以更好地控制它。
- 用户可以在将
a
和b
参数传递给Objpair
之前使用多态的实例。