操作员功能的操作符重载我遇到了麻烦

时间:2011-12-15 03:15:05

标签: c++ operator-overloading

编译器在尝试减去两个Point3D对象时给出了错误。我收到这个错误:

Invalid operands to binary expression ('Point3D' and 'Point3D')

这就是我在Vector3D.h中所拥有的:

#include "Point3D.h"
using namespace std;
class Vector3D
{
    friend const Point3D operator+(const Point3D& a, const Vector3D& b);
    friend const Vector3D operator-(const Point3D& a, const Point3D& b);

public:

    Vector3D() {}
    Vector3D(float x, float y, float z);
    Vector3D(Point3D const& originPoint, float theta, float distance);
    float getX() const {return x;}
    float getY() const {return y;}
    float getZ() const {return z;}
    static Vector3D minus(Point3D const& destination, Point3D const& origin);
    Vector3D operator-(Vector3D const& other) const;
    float dot(Vector3D const& other) const;
    static float angleBetweenTwoVectorsZeroToPi(Vector3D const& a, Vector3D const& b);
    static float angleBetweenTwoVectorsZeroToTwoPi(Vector3D const& a, Vector3D const& b);
    Vector3D normalize() const;
    float length() const;
    //const float * const getArray() const {return &x;}
    Vector3D multiply(float scalar) const;
    bool operator==(Vector3D const& v) const;
    float operator[] (int i) const;
private:
    float x;
    float y;
    float z;
};

定义二元运算符的Vector3D.cpp文件:

#include "Vector3D.h"
#include "Math3D.h"
#include <math.h>
#include "MathConstants.h"

Vector3D::Vector3D(float x, float y, float z):
x(x), y(y), z(z)
{}

Vector3D::Vector3D(Point3D const& originPoint, float theta, float distance) 
{
    Point3D endPoint = Math3D::calcaultePoint3D(originPoint, theta, distance);
    Vector3D result = minus(endPoint, originPoint);
    this->x = result.x;
    this->y = result.y;
    this->z = result.z;

}

Vector3D Vector3D::minus(Point3D const& destination, Point3D const& origin)
{
    return Vector3D(destination.getX() - origin.getX(), 
                    destination.getY() - origin.getY(), 
                    destination.getZ() - origin.getZ());
}

Vector3D Vector3D::operator-(Vector3D const& other) const {
    return Vector3D(x-other.x, y-other.y, z-other.z);
}

float Vector3D::dot(const Vector3D &other) const
{
    return x * other.x + y * other.y + z * other.z;
}



float Vector3D::length() const
{
    return sqrtf(dot(*this));
}

Vector3D Vector3D::normalize() const
{
    float len = length();
    return Vector3D(getX()/len, getY()/len, getZ()/len);
}

Vector3D Vector3D::multiply(float scalar) const {
    return Vector3D(x * scalar, y * scalar, z * scalar);
}

float Vector3D::angleBetweenTwoVectorsZeroToPi(const Vector3D &a, const Vector3D &b)
{
    /*
     *  The result is between 0 and PI
     */
    Vector3D unitA = a.normalize();
    Vector3D unitB = b.normalize();
    return acos(unitA.dot(unitB));

}

bool Vector3D::operator==(const Vector3D &v) const {
    return (x == v.x) && (y == v.y) && (z == v.z);
}

float Vector3D::operator[](int i) const {
    return (&x)[i];
}

float Vector3D::angleBetweenTwoVectorsZeroToTwoPi(const Vector3D &a, const Vector3D &b)
{
    /*
     *  The result is between 0 and 2PI
     *
     *  "Assuming a = [x1,y1] and b = [x2,y2] are two vectors with their bases at the 
     *  origin, the non-negative angle between them measured counterclockwise 
     *  from a to b is given by
     *
     *  angle = mod(atan2(x1*y2-x2*y1,x1*x2+y1*y2),2*pi);
     *
     *  As you can see, this bears a close relationship to the three-dimensional 
     *  formula I wrote last July 10. The quantities, x1*y2-x2*y1 and x1*x2+y1*y2 
     *  are, respectively, the sine and cosine of the counterclockwise angle from 
     *  vector a to vector b, multiplied by the product of their norms - that is, their 
     *  cross product and the dot product restricted to two dimensions. The 'atan2' 
     *  function then gives the angle between them ranging from -pi to +pi, and the 
     *  'mod' operation changes this so as to range from 0 to 2*pi, as you requested."
     *
     *  Roger Stafford
     *  http://www.mathworks.com/matlabcentral/newsreader/view_thread/151925
     */
    float resultNegPiToPosPi = atan2f(a.x*b.y-b.x*a.y, a.x*b.x+a.y*b.y);
    if (resultNegPiToPosPi < 0.0f)
    {
        resultNegPiToPosPi = resultNegPiToPosPi + 2*MathConstants::PI;
    }
    return resultNegPiToPosPi;
}


const Point3D operator+(const Point3D& a, const Vector3D& b) {return Point3D(a.getX()+b.getX(), a.getY()+b.getY(), a.getZ()+b.getZ());}


const Vector3D operator-(const Point3D& a, const Point3D& b) {return Vector3D(a.getX()-b.getX(), a.getY()-b.getY(), a.getZ()-b.getZ());}

这是我尝试从另一个中减去Point3D的地方:

void AnimationService::handlePlayerMovement(double lastTime, double currentTime, Vector3D vector) {

    Point3D a;
    Point3D b;
    Vector3D result = a - b; // this is the problem line
}

奇怪的是,二进制operator+确实有效,但由于某种原因operator-给了我错误。谁能告诉我我做错了什么?

2 个答案:

答案 0 :(得分:4)

要快速解决问题,请将operator-设为免费功能,而不是Vector3D的朋友。只有当您想要访问private成员Point3D时才需要友谊(您使用公共获取者),而您对Vector3D尤其不做构造函数从值中直接构造它。

operator+也是如此,这里不需要友谊。

现在问你代码失败的原因:

如果在类体中找到friend声明 first ,并且在类声明后未在封闭命名空间中声明它,那么该函数只能通过参数找到依赖查找,意味着如果所有参数都不属于它的朋友,则不会找到它。你可以通过在不需要友谊时使它们成为自由函数,或者通过在标题中再次明确地将它们声明为Vector3D类的主体来解决这个问题,例如:

class Vector3D{
  friend const Point3D operator+(const Point3D& a, const Vector3D& b);
  friend const Vector3D operator-(const Point3D& a, const Point3D& b);
public:
  // bla bla, yadda yadda
};

// also *declare* them here
const Point3D operator+(const Point3D& a, const Vector3D& b);
const Vector3D operator+(const Point3D& a, const Point3D& b);

对于那些语言律师而言,这里是相关的标准段落:

§7.3.1.2 [namespace.memdef] p3

  

[...]如果非本地类中的friend声明首先声明一个类或函数,那么友元类或函数是最内层封闭命名空间的成员。 在该命名空间范围内提供匹配声明(在授予友谊的类定义之前或之后),通过非限定查找或限定查找找不到朋友的姓名。 [...]

答案 1 :(得分:3)

当函数的唯一声明是friend声明时,我相信只有在其中一个参数是声明它的类型时才能找到该函数。由于operator +采用Vector3D,编译器在查找Vector3D时会查看它。由于仅运算符采用Point3D,编译器在Point3D内部而不是Vector3D,因此找不到运算符。

正如Xeo所说,将函数声明移到类之外,它应该可以工作。