是否可以避免复制传递给保存指向它的指针的函数的临时 class 实例?
Is it possible to avoid copying a temporary class instance passed to a function that saves a pointer to it?
我有一个 class Holder
,其中包含一个向量,该向量包含从相同的抽象基础 class 派生的不同 classes(例如 Derived
) ](Base
)。我将它构造为指向基 class 的指针的向量以支持多态性。通过名为 add
的方法将新实例添加到向量中。新的 classes 是在调用 add
时在括号内构造的,并且没有命名(此机制已提供给我,无法更改)。
我不太明白这个构造的class的生命周期是多少,如何防止它在退出add
后立即被销毁。它必须被构建,那么有没有办法让它保持活动状态并持有指向它的有效指针?还是我必须创建一个副本并让通过的临时文件消失?
我正在使用 C++17,我的 classes 中的 none 手动分配内存,仅使用 STL 容器和原始数据类型。我一直在查看 std::move()
但我没有找到我需要的东西。示例代码:
class Base
{
public:
std::vector<int> vec;
virtual void foo() = 0;
}
class Derived : public Base
{
public:
Derived(int a, int b)
{
vec.push_back(a);
vec.push_back(b);
}
void foo() override;
}
class Holder
{
public:
std::vector<Base*> ptrs;
void add(Base& arg) // What should be the type of arg? (Does not have to be Base&)
{
ptrs.push_back(&arg); // How should I add a pointer to arg into ptrs so that
// arg stays alive after returning from this method?
}
}
int main()
{
Holder h;
h.add(Derived(1, 2)); // this is how add() gets called, I cannot change it
...
}
编辑:为了澄清,我可以更改 add
的签名和正文以及向量 ptrs
使用的数据类型。我无法更改 add
的调用方式 (h.add(Derived(1, 2));
).
在main
传递的参数是在调用 add
returns 之后临时销毁的。您可以存储此值、地址,但绝不能取消引用它。
如果您想确保对象在 Holder
的生命周期内保持有效,您唯一的选择是接管现有对象的所有权或制作您拥有所有权的副本。
获取现有对象的所有权
为此,最好使用 std::unique_ptr
来明确所有权。
class Holder
{
...
void add(std::unique_ptr<Base>&& arg)
{
//preferrably make ptrs a std::vector<std::unique_ptr<Base>> and use ptrs.emplace_back(std::move(arg)) or
ptrs.push_back(args.get());
args.release(); // make sure the object gets deleted before or during destruction of the Holder object
}
};
复制
在此处使用移动构造函数来作废副本
class Holder
{
...
template<class T>
void add(T&& arg) // What should be the type of arg?
{
ptrs.push_back(new T(std::forward<T>(arg));
// Note: make sure the object created here gets destroyed
}
};
编辑
根据更新的答案,这是 Holder
的实现,我会选择:
class Holder
{
public:
std::vector<std::unique_ptr<Base>> ptrs; // make this private?
template<class T>
void add(T&& arg)
{
// better compiler error on incorrect use
static_assert(std::is_base_of_v<Base, T>,
"parameter passed must be an object of a class derived from Base");
ptrs.reserve(ptrs.size() + 1); // if an std::bad_alloc happens, this gives you a strong exception guarantee
ptrs.emplace_back(std::make_unique<T>(std::forward<T>(args)));
}
};
注意:这仅适用于 Base
的虚拟析构函数;如果这不是一个选项,您将需要保存调用对象旁边的正确析构函数的逻辑。您不会阻止临时 Derived
对象的创建,但至少可以使用 Derived
的隐式创建的移动构造函数来防止不必要地复制 Base::vec
[=22 的元素=]
我有一个 class Holder
,其中包含一个向量,该向量包含从相同的抽象基础 class 派生的不同 classes(例如 Derived
) ](Base
)。我将它构造为指向基 class 的指针的向量以支持多态性。通过名为 add
的方法将新实例添加到向量中。新的 classes 是在调用 add
时在括号内构造的,并且没有命名(此机制已提供给我,无法更改)。
我不太明白这个构造的class的生命周期是多少,如何防止它在退出add
后立即被销毁。它必须被构建,那么有没有办法让它保持活动状态并持有指向它的有效指针?还是我必须创建一个副本并让通过的临时文件消失?
我正在使用 C++17,我的 classes 中的 none 手动分配内存,仅使用 STL 容器和原始数据类型。我一直在查看 std::move()
但我没有找到我需要的东西。示例代码:
class Base
{
public:
std::vector<int> vec;
virtual void foo() = 0;
}
class Derived : public Base
{
public:
Derived(int a, int b)
{
vec.push_back(a);
vec.push_back(b);
}
void foo() override;
}
class Holder
{
public:
std::vector<Base*> ptrs;
void add(Base& arg) // What should be the type of arg? (Does not have to be Base&)
{
ptrs.push_back(&arg); // How should I add a pointer to arg into ptrs so that
// arg stays alive after returning from this method?
}
}
int main()
{
Holder h;
h.add(Derived(1, 2)); // this is how add() gets called, I cannot change it
...
}
编辑:为了澄清,我可以更改 add
的签名和正文以及向量 ptrs
使用的数据类型。我无法更改 add
的调用方式 (h.add(Derived(1, 2));
).
在main
传递的参数是在调用 add
returns 之后临时销毁的。您可以存储此值、地址,但绝不能取消引用它。
如果您想确保对象在 Holder
的生命周期内保持有效,您唯一的选择是接管现有对象的所有权或制作您拥有所有权的副本。
获取现有对象的所有权
为此,最好使用 std::unique_ptr
来明确所有权。
class Holder
{
...
void add(std::unique_ptr<Base>&& arg)
{
//preferrably make ptrs a std::vector<std::unique_ptr<Base>> and use ptrs.emplace_back(std::move(arg)) or
ptrs.push_back(args.get());
args.release(); // make sure the object gets deleted before or during destruction of the Holder object
}
};
复制
在此处使用移动构造函数来作废副本
class Holder
{
...
template<class T>
void add(T&& arg) // What should be the type of arg?
{
ptrs.push_back(new T(std::forward<T>(arg));
// Note: make sure the object created here gets destroyed
}
};
编辑
根据更新的答案,这是 Holder
的实现,我会选择:
class Holder
{
public:
std::vector<std::unique_ptr<Base>> ptrs; // make this private?
template<class T>
void add(T&& arg)
{
// better compiler error on incorrect use
static_assert(std::is_base_of_v<Base, T>,
"parameter passed must be an object of a class derived from Base");
ptrs.reserve(ptrs.size() + 1); // if an std::bad_alloc happens, this gives you a strong exception guarantee
ptrs.emplace_back(std::make_unique<T>(std::forward<T>(args)));
}
};
注意:这仅适用于 Base
的虚拟析构函数;如果这不是一个选项,您将需要保存调用对象旁边的正确析构函数的逻辑。您不会阻止临时 Derived
对象的创建,但至少可以使用 Derived
的隐式创建的移动构造函数来防止不必要地复制 Base::vec
[=22 的元素=]