编写运算符时需要了解一些事项,这些事项不常与其他函数一起使用。
例如,分配运算符将return *this
因为您更改向量的值:
class v {
public:
double x_, y_;
v& operator += (const v& rhs)
{
_x += rhs._x;
_y += rhs._y;
return *this;
}
};
另一个有趣的是,pre++
和 post++
的不同只是因为一个未使用的参数:
class v {
public:
double x_, y_;
v& operator ++ (); // ++v
v& operator ++ (int); // v++
};
当您使用指针时,“相等”(赋值)是另一个棘手的问题。对于一个向量,一般不会有问题,但是如果你定义一个向量 V 并将它赋值给它自己,你必须小心:
class v {
public:
double x_, y_;
v& operator = (const v& rhs)
{
if(this != &rhs)
{
x_ = rhs.x_;
y_ = rhs.y_;
}
return *this;
}
};
在你的情况下,if()
肯定不会有用,但考虑做这样的事情:
delete p_;
p_ = new foo;
p_->x_ = rhs.p_->x_;
如果&rhs == this
,则delete p_
删除rhs
指针!这意味着在第 3 行访问它是一个错误。
其余的应该很容易使用。比较运算符返回bool
并且是const
:
class v {
public:
double x_, y_;
bool operator == (const v& rhs) const
{
return x_ == rhs.x_ && y_ == rhs.y_;
}
};
虽然,从 C++20 开始,您应该只声明三路比较运算符<=>
,它允许编译器为您实现所有其他比较运算符。这个返回一个负数(较小:a < b)、0(等于:a == b)或正数(较大:a > b)。
我不确定是什么使向量变大或变小,在此示例中我使用了 (0, 0) 的长度:
class v {
public:
double x_, y_;
int operator <=> (const v& rhs) const
{
if(x_ == rhs.x_ && y_ == rhs.y_)
{
return 0;
}
return length() > rhs.length() ? 1 : -1;
}
};
运营商除外[]
。该版本有两个版本:
class v {
public:
// I would imagine you'd use an array but as a simple example...
double x_, y_;
double operator [] (int idx) const
{
return idx == 0 ? x_ : y_;
}
v_ref operator [] (int idx)
{
v_ref v(this, idx);
return v;
}
};
如您所见,非常量版本的 [] 运算符返回一个引用。这是必要的,因此您可以编写如下内容:
r[3] = 7.3;
r[3]
返回该引用,然后将引用的赋值7.3
作为参数调用。(请注意,如果您使用 3 作为索引,而您只有 2 个值:0 和 1,我们可能会抛出错误:此处未显示)
class v_ref
{
public:
v *p_;
int i_;
v_ref(v *p, int i)
: p_(p), i_(i)
{
}
operator = (double q)
{
// again, I suppose you'd use an array instead!
if(i_ == 0)
{
p_->x_ = q;
}
else
{
p_->y_ = q;
}
}
};
假设您想要一些安全性,向量指针可以使用引用计数器,以便您知道主向量对象是否在其所有引用对象之前被删除......
另一个注意事项:我想您的构造函数将分配一个 double 数组(或使用std::vector<double>
类型......)如果您使用 new,请记住在析构函数中删除,if()
此时赋值运算符非常重要。