[英]C++11 constructors
新的move-constructor / move-operator允许我们转移对象的所有权,这样就避免了使用(昂贵的)复制构造函数调用。 但是有可能避免构造临时对象(不使用返回参数)吗?
示例:在下面的代码中,构造函数被调用4次 - 但理想情况下,我想要做的是避免在交叉方法中构造任何对象。 使用返回参数(例如void cross(const Vec3 &b, Vec3& out)
是可能的,但读起来很难看。我有兴趣更新现有变量。
#include <iostream>
using namespace std;
class Vec3{
public:
Vec3(){
static int count = 0;
id = count++;
p = new float[3];
cout << "Constructor call "<<id <<" "<<p<< " "<<this<< endl;
}
~Vec3(){
cout << "Deconstructor call "<<id << " "<<p<<" "<<this<< endl;
delete[] p;
}
Vec3(Vec3&& other)
: p(nullptr) {
cout << "Move constructor call "<<id << " "<<p<<" "<<this<< endl;
p = other.p;
other.p = nullptr;
}
Vec3& operator=(Vec3&& other) {
cout << "Move assignment operator call from "<<other.id<<" to "<<id << " "<<p<<" "<<this<< endl;
if (this != &other) {
p = other.p;
other.p = nullptr;
}
return *this;
}
Vec3 cross(const Vec3 &b){
float ax = p[0], ay = p[1], az = p[2],
bx = b.p[0], by = b.p[1], bz = b.p[2];
Vec3 res;
res.p[0] = ay * bz - az * by;
res.p[1] = az * bx - ax * bz;
res.p[2] = ax * by - ay * bx;
return res;
}
float *p;
int id;
};
int main(int argc, const char * argv[])
{
Vec3 a,b,c;
a = b.cross(c);
return 0;
}
另一种解决方案是从a.cross(b)
返回一个“表达式对象”,对计算进行处理,直到将这样的对象分配给c
,然后在operator=
实际进行计算:
struct Vec3
{
CrossProduct cross(const Vec3& b);
Vec3& operator=(CrossProduct cp)
{
do calculation here putting result in `*this`
}
}
并添加类似的施工机制等。
这涉及更多,但许多C ++数学库使用这种设计模式。
如果直接分配新值:
Vec3 a = b.cross(c);
然后RVO可能会生效,并且没有临时构造和稍后移动。 确保使用优化进行编译。 返回的值将就地构造成一个。
在堆上分配3个浮点数组似乎是一个性能杀手。 使用类似C的数组float p[3]
或std::array<float, 3>
应该表现得更好。
要更新现有变量,可以使用out参数:
// out parameter version
void cross(const Vec3 &b, Vec3& res){
float ax = p[0], ay = p[1], az = p[2],
bx = b.p[0], by = b.p[1], bz = b.p[2];
res.p[0] = ay * bz - az * by;
res.p[1] = az * bx - ax * bz;
res.p[2] = ax * by - ay * bx;
return res;
}
当返回值版本用作初始化程序时( 但在分配给现有对象时 ),RVO将忽略构造函数:
// return value version (RVO)
Vec3 cross(const Vec3& b)
{
Vec3 t; cross(b, t); return t;
}
你也可以提供结果对象的mutator:
// assignment version
void set_cross(const Vec3& a, const Vec3& b)
{
a.cross(b,*this);
}
如图所示,所有三个成员函数可以有效地共存并重用彼此的代码。
这不是你问题的直接答案,我只能做出很少贡献,因为目前的答案涵盖了重点,但我想引导你注意你所选择的基于堆的设计的缺点。
这当然是权衡取决于您必须进行的移动次数以及您对向量执行的操作次数。
如果你只使用一些单一的向量并做很多复制/移动的东西,你可以坚持你的堆基设计。 但是如果你有几个向量(例如在向量或数组中)并且你想对它们进行操作,那么如果你担心性能,我建议你不要进行堆分配。
std::vector<Vec3> a(20);
class con_Vec3 { double x, y, z; };
std::vector<con_Vec3> b(20);
向量a
将保持一个连续的指针块浮动,其中浮点值依次驻留在内存中的其他位置,这意味着您的值实际上分散在内存中。 相比之下,矢量b
将包含一个60个double
s的连续块,所有这些都在一个地方。
移动这样一个std::vector
的成本对于两种情况都是相同的(准交换每种情况)但是如果你复制它们,基于堆的解决方案将会更慢,因为将发生21次分配和20次拷贝,而在非堆解决方案有一个分配和20个副本覆盖整个向量。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.