专业编程基础技术教程

网站首页 > 基础教程 正文

C++|由成员函数到运算符重载(类内、类外、友元方式重载)

ccvgpt 2024-10-12 13:51:46 基础教程 11 ℃

高级程序设计语言的一种突出的能力就是能够让用户自定义抽象数据类型。同时,还允许用户在自定义类型上定义运算,其中包括赋予语言内建运算新的含义。这扩展了高级程序设计语言的功能,使其能处理更加复杂的问题。

然而一个横在程序员面前的难题是,如何使用常规运算符去处理新类型的运算。常规运算符表达简单的操作,很直观,例如,表达式

C++|由成员函数到运算符重载(类内、类外、友元方式重载)

a+b

就比

add(a,b)

要直观得多。

但问题是,类似于+、*这样的运算符只能对于语言内建的类型(的数据)进行操作。而对于用户自定义类型的数据,编译器将不知道程序员要做什么。举个例子来说,设有如下代码,试图用输出流运算符直接作用在矩形类对象之上:

Rectangle rect1(10, 20), rect2(40, 50), rect3;
rect3 = rect1 + rect2;

如果没有特别编码,那么编译器将“不明白”+运算符作用在类对象上有什么含义。

以复数为例来说,它不是C++的内建类型,因此程序员往往会编写一个类来描述复数类型,同时编写一个全局友元函数add()来完成两个复数的加法运算,另一个全局友元函数assign()来完成两个复数对象的赋值。

一个对象的私有数据只能通过成员函数进行访问,这是一堵不透明的墙。这种限制性给这样的一种情况造成困扰:类的某些成员原则上应该是私有的,但却需要在外部频繁地访问它们。常用的做法是为类设计一个返回这些成员引用的仅有函数,这无疑带来了额外的开销。因此,仅仅出于效率(而非技术上必须)的考虑,C++提供了一种辅助手段中,允许类外面的类或函数去访问一个类的私有数据,这就是友元(friend)机制。一个类的友元可以是一个外部函数,也可以是一个类。它们虽然不是该类的成员,但却能直接访问该类的任何成员,这显然提高了访问效率,但带来的问题是,这在一定程序上违背了数据封装原则。

代码如下:

#include <iostream>
using namespace std;
class complex
{
private:
	double real, imag;
public:
	complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
	friend complex add(const complex &c1, const complex &c2);
	friend complex& assign(complex &c1, const complex &c2);
	friend void print(const complex& c);
	//other operations
};
complex add(const complex &c1, const complex &c2)
{
	complex temp(c1.real + c2.real, c1.imag + c2.imag);
	return temp;
}
complex& assign(complex &c1, const complex &c2)
{
	c1.real = c2.real; 
	c1.imag = c2.imag;
	return c1;
}
void print(const complex& c)
{
	cout<<"("<<c.real<<","<<c.imag<<")"<<endl;
}
int main()
{
	complex a(1,2), b(3,4), c, d;
	c = add(a,b);
	print(c);
	d = a;
	print(d);
	cin.get();
	return 0;
}


运算结果:

(4,6)
(1,2)

这样,两个复数相加就会用如下代码完成:

c = add(a,b);

显然,这种函数式的运算多少显得有些别扭,明显不如赋值语句

c=a+b

直观清晰。然而问题是,如果这样做,那么编译器会因为complex不是内建类型而不能理解+运算符直接作用在两个复数类对象上有什么特殊含义。那么该怎么做呢?

在C++中,很多的运算符被当做是函数,这称为“运算符函数(operator function)”,设运算符为@,那么它对应的运算符函数的原型可以形式化地表示为:

返回值类型 operator @(参数列表);

这样,我们就可以像重载普通函数一样进行运算符函数的重载。

与普通函数一样,运算符重载函数应该有返回类型和参数。一些特殊的运算符函数如类型转换运算符不能指定返回值类型。此外,返回值和参数的设定必须与重载的运算符的含义相匹配。

在绝大多数情况下,参与重载运算的操作数(至少其中一个)是一个类对象,而重载的@运算函数与这个类相关。

以下是通过友元的方式来重载运算符:

#include <iostream>
//using namespace std;//frined:INTERNAL COMPILER ERROR
class complex
{
private:
	double real, imag;
public:
	complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
	//complex(const complex& c):real(c.real),imag(c.imag){} // 复制构造函数
	friend complex add(const complex &c1, const complex &c2);
	friend complex operator+(const complex& c1, const complex& c2);
	friend complex& assign(complex &c1, const complex &c2);
	friend void print(const complex& c);
};
complex add(const complex &c1, const complex &c2)
{
	complex temp(c1.real + c2.real, c1.imag + c2.imag);
	return temp;
}
complex operator+(const complex& c1, const complex& c2)
{
	complex temp(c1.real + c2.real, c1.imag + c2.imag);
	return temp;
}
complex& assign(complex &c1, const complex &c2)
{
	c1.real = c2.real; 
	c1.imag = c2.imag;
	return c1;
}
void print(const complex& c)
{
	std::cout<<"("<<c.real<<","<<c.imag<<")"<<std::endl;
}
int main()
{
	complex a(1,2), b(3,4), c, d;
	c = add(a,b);
	c = c+a;
	print(c);
	d = a;
	print(d);
	std::cin.get();
	return 0;
}

也可以定义到类内:

 complex operator+(const complex & p2)
 {
 complex temp(*this);
 temp.real = temp.real + p2.real;
		temp.imag = temp.imag + p2.imag;
 return temp;
 }

也可以定义到类外:

先在类内定义operator+=:

	complex& operator+=(const complex& p1)
 {
 real += p1.real;
 imag += p1.imag;
 return *this;
 }
然后在类外定义operator+
complex operator+(const complex & p1,const complex & p2)
{
 complex temp(p1);
 temp += p2;
 return temp;
}

注意operator+定义在类内时,只使用了一个参数。

When operator+ is defined inside class, left operand of operator is current instance. So, to declare a overload of operator+ you have 2 choices:

inside class, with only one parameter which is right operand;

outside of class, with two parameters, left and right operands.

全部代码:

#include <iostream>
//using namespace std;//frined:INTERNAL COMPILER ERROR
class complex
{
private:
	double real, imag;
public:
	complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
	//complex(const complex& c):real(c.real),imag(c.imag){} // 复制构造函数
	friend complex add(const complex &c1, const complex &c2);
	//friend complex operator+(const complex& c1, const complex& c2);
	friend complex& assign(complex &c1, const complex &c2);
	friend void print(const complex& c);
/*
 complex operator+(const complex & p2)
 {
 complex temp(*this);
 temp.real = temp.real + p2.real;
		temp.imag = temp.imag + p2.imag;
 return temp;
 }
*/
	complex& operator+=(const complex& p1)
 {
 real += p1.real;
 imag += p1.imag;
 return *this;
 }
};
complex operator+(const complex & p1,const complex & p2)
{
 complex temp(p1);
 temp += p2;
 return temp;
}
complex add(const complex &c1, const complex &c2)
{
	complex temp(c1.real + c2.real, c1.imag + c2.imag);
	return temp;
}
/*
complex operator+(const complex& c1, const complex& c2)
{
	complex temp(c1.real + c2.real, c1.imag + c2.imag);
	return temp;
}
*/
complex& assign(complex &c1, const complex &c2)
{
	c1.real = c2.real; 
	c1.imag = c2.imag;
	return c1;
}
void print(const complex& c)
{
	std::cout<<"("<<c.real<<","<<c.imag<<")"<<std::endl;
}
int main()
{
	complex a(1,2), b(3,4), c, d;
	c = add(a,b);
	c = c+a;
	print(c);
	d = a;
	print(d);
	std::cin.get();
	return 0;
}

-End-

最近发表
标签列表