c语言运算符优先级问题讲解(自定义数据类型运算符重载的设计方法)
c语言运算符优先级问题讲解(自定义数据类型运算符重载的设计方法)Ration operator*(const Ration& Lhs const Ration& Rhs) { return Ration(Lhs.GetNumerator() * Rhs.GetNumerator() Lhs.GetDenominator() * Rhs.GetDenominator()); }主函数源码修改如下:那么现在不能设计为成员函数,能否变为友元函数?友元函数可以访问对象的公有、受保护和私有属性和方法。按照面向对象的要求,这样做不符合封装性,我们应该尽可能地将实现细节隐藏。此时就只剩下非成员函数了,接下来我们用非成员函数实现乘法运算符重载函数看看效果如何。Ration::Ration(int NumeratorIn int DenominatorIn) : Numerator(NumeratorIn) Denominator(Denominator
更多知识请关注微信公众号“写代码的牛顿”。
在C 中我们可以自定义丰富的数据类型,而且C 还提供了运算符重载。那么当我们自定义数据类型后想要像内置基本数据类型一样进行算术运算,该如何设计运算符重载函数呢?是声明为成员函数好?还是非成员函数好?亦或是友元函数?
假设现在我们自定义一个有理数数据类型,一个设计良好的自定义有理数,应该满足数学运算法则和平常人的使用习惯。有理数有分子和分母,现在我们自定义一个有理数,实现乘法运算,我们先将乘法运算符重载函数声明为成员函数,源码如下:
class Ration{
public:
Ration(int NumeratorIn = 0 int DenominatorIn = 1);
Ration operator*(const Ration& Rhs);
int GetNumerator() const;
int GetDenominator() const;
private:
int Numerator;
int Denominator;
};
在Ration类的构造函数中,有默认形参,并且是非显示构造函数。这是由于一个有理数和一个整数相乘,整数在运算符的右侧时会调用非显示构造函数创建一个临时Ration类,并且很符合我们在数学上的运算法则和使用习惯。接下来是成员函数实现源码。
Ration::Ration(int NumeratorIn int DenominatorIn) : Numerator(NumeratorIn) Denominator(DenominatorIn)
{}
Ration Ration::operator*(const Ration& Rhs)
{
return Ration(Numerator * Rhs.Numerator Denominator * Rhs.Denominator);
}
int Ration::GetNumerator() const
{
return Numerator;
}
int Ration::GetDenominator() const
{
return Denominator;
}
现在我们来实际运用一下,看看实现的效果。主函数源码如下所示。
int main(int argc char *argv[])
{
std::cout << "Hello world" << std::endl;
Ration R1(10 20);
Ration R2 = R1 * 2; //正确
std::cout << "R2: " << R2.GetNumerator() << " " << R2.GetDenominator() << std::endl;
Ration R3 = 3 * R1; //错误
std::cout << "R3: " << R3.GetNumerator() << " " << R3.GetDenominator() << std::endl;
return 0;
}
编译运行观察效果。
编译出错!这是因为R2 = R1 * 2的实际函数调用是R2 = R1.operator(Ration(2 1))。也就是说此时乘法运算符的左侧必须是一个Ration类型变量。而R3 = 3 * R1左侧是整数3并不是Ration类型变量。所以无法编译通过,但是R3 = 3 * R1按照数学运算法则应该和R3 = R1* 3一样的效果。
那么现在不能设计为成员函数,能否变为友元函数?友元函数可以访问对象的公有、受保护和私有属性和方法。按照面向对象的要求,这样做不符合封装性,我们应该尽可能地将实现细节隐藏。此时就只剩下非成员函数了,接下来我们用非成员函数实现乘法运算符重载函数看看效果如何。
Ration operator*(const Ration& Lhs const Ration& Rhs)
{
return Ration(Lhs.GetNumerator() * Rhs.GetNumerator() Lhs.GetDenominator() * Rhs.GetDenominator());
}
主函数源码修改如下:
int main(int argc char *argv[])
{
std::cout << "Hello world" << std::endl;
Ration R1(10 20);
Ration R2 = R1 * 2;
std::cout << "R2: " << R2.GetNumerator() << " " << R2.GetDenominator() << std::endl;
Ration R3 = 3 * R1;
std::cout << "R3: " << R3.GetNumerator() << " " << R3.GetDenominator() << std::endl;
return 0;
}
编译运行结果如下:
OK,现在两种情况下都能输出正确信息,这是因为R3 = 3 * R1和R2 = R1 * 2可以转化为以下代码。
R3 = operator*(Ration(5) R1);
R2 = operator*(R1 Ration(2));
但是这并不意味着所有的运算符重载函数都要声明为非成员函数,C 还有一种运算符是自增或自减运算符,但是自增和自减运算符不能通过“=”赋值获取最终结果,在调用自增或自减运算符重载函数时即可获取最终结果,所以自增和自减运算符重载函数不能声明为非成员函数。现在我们考虑将自增运算符重载函数声明为成员函数,Ration类修改代码和自增运算符重载函数实现源码如下。
class Ration{
public:
Ration(int NumeratorIn = 0 int DenominatorIn = 1);
Ration operator*(const Ration& Rhs);
void operator =(const Ration& Rhs);
int GetNumerator() const;
int GetDenominator() const;
private:
int Numerator;
int Denominator;
};
void Ration::operator =(const Ration& Rhs)
{
Numerator = Numerator * Rhs.GetDenominator() Rhs.GetNumerator() * Denominator;
Denominator = Denominator * Rhs.GetDenominator();
}
主函数调用如下。
Ration R4(2 1);
R4 = 4; //正确
//4 = R4; //错误
std::cout << "R4: " << R4.GetNumerator() << " " << R4.GetDenominator() << std::endl;
编译运行结果如下。
OK,正确。看到这里你可以测试一下R4 = R3;输出结果。
写作不易,您的点赞是我最大的支持。