C++ 如何将对象的所有权传递给函数外部
是否有任何方法可以将在堆栈内存上的函数中创建的对象的所有权传递到函数的外部,而不使用复制构造 通常,编译器会自动调用函数堆栈上的对象的销毁。因此,如果我们想要创建一个类的对象(可能有一些特定的参数),我们如何避免浪费大量的资源从临时对象复制呢? 以下是一个常见的情况:C++ 如何将对象的所有权传递给函数外部,c++,c++11,move,C++,C++11,Move,是否有任何方法可以将在堆栈内存上的函数中创建的对象的所有权传递到函数的外部,而不使用复制构造 通常,编译器会自动调用函数堆栈上的对象的销毁。因此,如果我们想要创建一个类的对象(可能有一些特定的参数),我们如何避免浪费大量的资源从临时对象复制呢? 以下是一个常见的情况: while(...){ vectors.push_back(createObject( parameter )); } 因此,当我们想在迭代中使用一些参数创建对象,并将它们推到向量中时,正常的值传递路径将需要大量时间来复
while(...){
vectors.push_back(createObject( parameter ));
}
因此,当我们想在迭代中使用一些参数创建对象,并将它们推到向量中时,正常的值传递路径将需要大量时间来复制对象我不想在堆内存上使用指针和新建
对象,因为用户可能忘记删除
它们,从而导致内存泄漏
好吧,智能指针也许是一个解决方案。但是,我想,不那么优雅了。啊
有没有任何方法可以应用rvalue reference
和move
语义来解决这个问题?通常,按值返回对象将不会复制对象,因为编译器应该进行(命名的)返回值优化,从而删除副本
通过这种优化,从调用上下文(外部堆栈帧)分配返回对象的空间,并直接在那里构造对象
在您的示例中,编译器将在调用createObject()
的上下文中为对象分配空间。由于此上下文是std::vector.push_back()
成员函数的一个(未命名)参数,因此此上下文用作右值引用,因此push_back()
by值将通过将此对象移动(而不是复制)到向量中来使用它。这是可能的,因为如果生成的对象是可移动的。否则,将出现一个副本
总之,将创建每个对象,然后将其移动(如果可移动)到向量中
下面是一个示例代码,详细说明了这一点:
#include <iostream>
#include <string>
#include <vector>
using Params = std::vector<std::string>;
class Object
{
public:
Object() = default;
Object(const std::string& s) : s_{s}
{
std::cout << "ctor: " << s_ << std::endl;
}
~Object()
{
std::cout << "dtor: " << s_ << std::endl;
}
// Explicitly no copy constructor!
Object(const Object& other) = delete;
Object(Object&& other)
{
std::swap(s_, other.s_);
std::cout << "move: traded '" << s_ << "' for '" << other.s_ << "'" << std::endl;
}
Object& operator=(Object other)
{
std::swap(s_, other.s_);
std::cout << "assign: " << s_ << std::endl;
return *this;
}
private:
std::string s_;
};
using Objects = std::vector<Object>;
Object createObject(const std::string& s)
{
Object o{s};
return o;
}
int main ()
{
Objects v;
v.reserve(4); // avoid moves, if initial capacity is too small
std::cout << "capacity(v): " << v.capacity() << std::endl;
Params ps = { "a", "bb", "ccc", "dddd" };
for (auto p : ps) {
v.push_back(createObject(p));
}
return 0;
}
#包括
#包括
#包括
使用Params=std::vector;
类对象
{
公众:
Object()=默认值;
对象(const std::string&s):s_{s}
{
std::coutMove semantics和copy elison of vectors应该意味着本地std::vector
的元素实际上是从对象传递到本地变量中的
粗略地说,您可以预期std::vector
的move构造函数类似于:
//This is not real code...
vector::vector(vector&& tomove){
elems=tomove.elems; //cheap transfer of elements - no copying of objects.
len=tomove.elems;
cap=tomove.cap;
tomove.elems=nullptr;
tomove.len=0;
tomove.cap=0;
}
执行此代码并注意构造和销毁的对象的最小数量
#include <iostream>
#include <vector>
class Heavy{
public:
Heavy(){std::cout<< "Heavy construction\n";}
Heavy(const Heavy&){std::cout<< "Heavy copy construction\n";}
Heavy(Heavy&&){std::cout<< "Heavy move construction\n";}
~Heavy(){std::cout<< "Heavy destruction\n";}
};
std::vector<Heavy> build(size_t size){
std::vector<Heavy> result;
result.reserve(size);
for(size_t i=0;i<size;++i){
result.emplace_back();
}
return result;
}
int main() {
std::vector<Heavy> local=build(5);
std::cout<<local.size()<<std::endl;
return 0;
}
请注意,在填充向量之前,我在向量中保留了容量,并使用emplace\u back
将对象直接构建到向量中
您不必获得传递给reserve
的值,因为向量将增长以容纳值,但这最终将导致重新分配和移动所有元素,这可能会很昂贵,这取决于您或编译器是否实现了有效的移动构造函数。如果您只是通过val返回对象ue,编译器最有可能使用。是一件事。副本不是问题。您对内存泄漏的恐惧与您的问题无关。内存管理是一个实现细节,由RAII覆盖。如果对象移动成本低,则当前代码可以,否则您可能希望将其封装在智能指针中。好吧,如果我在堆上创建对象并返回它们的指针,这意味着我必须在另一个函数中手动删除它们。当我与一群人合作时,这不是一个安全的工具,因为我认为他们更愿意忘记它。@knivil
Heavy construction
Heavy construction
Heavy construction
Heavy construction
Heavy construction
5
Heavy destruction
Heavy destruction
Heavy destruction
Heavy destruction
Heavy destruction