如何将对象的所有权传递给函数的外部
How to pass the ownership of an object to the outside of the function
是否有任何方法可以在不使用复制构造的情况下将堆栈内存上函数中创建的对象的所有权传递给函数外部?
通常,编译器会自动对函数堆栈上的对象调用销毁。因此,如果我们想创建一个类的对象(可能带有一些特定的参数(,我们如何避免浪费大量资源从临时对象复制?
下面是一个常见情况:
while(...){
vectors.push_back(createObject( parameter ));
}
所以当我们想在迭代中创建一些参数的对象,并将它们推送到一个vector
时,正常值传递的方式会花费大量的时间复制对象。我不想在堆内存上使用指针和new
对象,因为用户可能会忘记delete
它们,从而导致内存泄漏。
好吧,智能指针也许是一个解决方案。但。。我认为不那么优雅。呵
有没有办法应用rvalue reference
和move
语义来解决这个问题?
通常,按值返回对象不会复制该对象,因为编译器应该执行(命名的(返回值优化,从而省略副本。
通过此优化,返回对象的空间从调用上下文(外部堆栈帧(分配,并且对象直接在那里构造。
在您的示例中,编译器将在调用createObject()
的上下文中为对象分配空间。由于此上下文是std::vector<T>.push_back()
成员函数的(未命名(参数,因此它用作右值引用,因此push_back()
by-value 将通过移动(而不是复制(此对象到向量中来使用此对象。这是可能的,因为如果生成的对象是可移动的。否则,将发生复制。
总之,每个对象将被创建,然后移动到(如果可移动(到向量中。
下面是一个示例代码,更详细地展示了这一点:
#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;
}
请注意,class Object
明确禁止复制。但要使其正常工作,移动构造函数必须可用。
有关何时可以(或将(发生复制的详细摘要,请点击此处。
移动语义和复制向量应该意味着局部std::vector
的元素实际上从对象传递到局部变量中。
粗略地说,您可以期望std::vector
的移动构造函数如下所示:
//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 constructionn";}
Heavy(const Heavy&){std::cout<< "Heavy copy constructionn";}
Heavy(Heavy&&){std::cout<< "Heavy move constructionn";}
~Heavy(){std::cout<< "Heavy destructionn";}
};
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;
}
移动语义和复制 elison 倾向于在 11 C++
开始解决这个问题。预期产出:
Heavy construction
Heavy construction
Heavy construction
Heavy construction
Heavy construction
5
Heavy destruction
Heavy destruction
Heavy destruction
Heavy destruction
Heavy destruction
请注意,我在填充向量之前在向量中保留了容量,并使用emplace_back
将对象直接构造到向量中。
您不必将值传递给reserve
完全正确,因为向量将增长以适应值,但它最终将导致所有元素的重新分配和移动,这可能会很昂贵,具体取决于您或编译器是否实现了有效的移动构造函数。
- 在函数内创建的对象的范围 - 如果在函数外部存储和访问引用,它们是否有效?
- C++指针无法在函数外部传递值和分段错误(核心转储)错误
- C++:函数外部的超时功能
- 如何在函数外部访问函数中局部变量的值?
- 在成员函数外部封闭类的定义中需要默认成员初始值设定项
- 以延长构造函数外部 QT 对象的生存期
- 全局静态变量不"staying defined"函数外部
- C++ 函数外部丢失的指针对象
- 向量大小在构造函数外部恢复为 0
- 删除函数外部的实例化对象
- 在函数外部编写代码
- 为什么我不能添加到方法/函数外部的 ostringstream
- 用于在方法或函数外部声明的变量的内存
- SDL2 在函数外部加载字体会引发错误
- 如何在具有多态性的函数外部创建对象
- 在函数外部声明对象,将其作为引用传递,然后在函数内部创建它
- 向量未在函数C++外部更新
- 访问函数外部的堆栈对象
- 在c++中访问函数外部的数组
- 如何更新函数外部的数组(指向指针)