1

我正在为一个项目编写一个 C++ 矢量类,我很难决定如何最好地编写一些方法。在我开始之前,我会说这个类有一个很好的复制构造函数和赋值运算符(这将在几秒钟内相关)。该类有一组静态方法,当我想确保两个参数都没有被更改时,它们返回向量,它们具有如下签名:

Vector* Vector::subtract(const Vector* v, const Vector* u)
{
    double outX = v->myX - u->myX;
    double outY = v->myY - u->myY;
    double outZ = v->myZ - u->myZ;

    return new Vector(outX, outY, outZ);
}

我遇到的问题是,如果我能提供帮助,我不想返回指针。所以相反我做了一些测试并意识到如果我只是说

return Vector(outX, outY, outZ)

然后分配结果

Vector foo = Vector::subtract(bar, temp)

它将创建一个副本并正常工作。这就是我的问题所在:我刚刚调用了构造函数两次(基本上)有没有办法解决这个问题?其次,如果我将此方法用作另一种方法的参数,例如

foo.multiply(&Vector::subtract(foo, bar), 5)

它还会创建一个副本,还是我只是传递了 Vector::subtract 方法中超出范围的指针?

更一般地说,最好的(或至少有更好的)方法是什么?

4

4 回答 4

2

将指针/引用返回到动态分配的对象绝不是要走的路。一个明显的例子是,如果您的函数被递归调用,那么谁负责取消分配该内存?

我从一本书中读到(Scott Meyers,它是 Effective C++ Item #21),唯一安全的方法是返回对象本身。您可以做的是,IIRC,促进编译器消除这些本质上是临时的对象的工作(从一个函数返回以提供给另一个函数,而不会将每个函数都分配给另一个函数),其中一种方法是使其匿名(又名返回值优化,感谢 In silico 提醒我)。像这样

return Vector(outX, outY, outZ);

相对于:

Vector v(outX, outY, outZ);
return v;

因此,我为您的减法方法提出的签名归结为:

Vector Vector::subtract(const Vector& v, const Vector& u)
于 2012-09-20T03:12:06.700 回答
2

我刚刚调用了构造函数两次(基本上)有没有办法解决这个问题?

你听说过返回值优化吗?您无需做任何事情。编译器很可能会为您消除副本。现在,如果您使用 C++11 并且Vector该类管理资源,您还可以声明一个移动构造函数,以便移动返回的值,以防编译器确定它无法执行 RVO。但是,看起来该类仅包含 3 个值,如果是这样,复制将与移动一样有效。

&Vector::subtract(foo, bar)

&为了什么?那是一个错误吗?此外,成员函数未声明static,因此语法错误。无论如何,假设subtract返回一个副本,它将返回一个副本并将其multiply作为参数传递给函数。

另外,另一方面,Vector* Vector::subtract(const Vector* v, const Vector* u) 会更好,因为Vector* Vector::subtract(const Vector& v, const Vector& u)当您将参数传递给减法等时,这会使语法更清晰。

所以改变你的代码,它看起来像下面这样:

Vector Vector::subtract(const Vector& v, const Vector& u)
{
    return Vector(v.myX - u.myX, v.myY - u.myY, v.myZ - u.myZ);
}
于 2012-09-20T03:15:05.460 回答
1

您应该尽可能避免使用指针,不仅在返回类型中,而且在函数的参数中。尽可能首选引用,必要时使用指针。

如果您更改函数以通过引用获取参数并按值返回。当在域中有意义时考虑重载运算符(我相信这里就是这种情况):

class Vector { ... };
Vector operator+( Vector const& lhs, Vector const& rhs ) {
   return Vector( lhs.x+rhs.x, lhs.y+rhs.y, lhs.z+rhs.y );
}
Vector operator*( Vector const& lhs, Vector const& rhs ) { ... }

然后就可以随意连锁操作了:

Vector a(...), b(...);
Vector c = (a + b) * c;
于 2012-09-20T03:30:41.570 回答
1

如今,大多数编译器都将实现所谓的返回值优化 (RVO) 来解决这个问题。你是对的,除非真的有必要,否则你不应该返回指针。话虽如此,您可能应该使用引用。总而言之,如果我要编写这个方法,我会这样写:

Vector Vector::subtract(const Vector& v, const Vector& u)
{
    return Vector(v.myX - u.myX, v.myY - u.myY, v.myZ - u.myZ);
}
于 2012-09-20T03:14:58.850 回答