再谈重载——一个实例深入重载

这里,我们借用《C++ Primer plus》中的实例——矢量类,更加深入了解一下重载到底高效和好用在哪里

什么是矢量?

矢量作为高中毕业生的必备知识,我们不再多加赘述,这里,我们集中注意力在计算机是如何对矢量进行抽象的

1.使用定义

矢量有两个必备条件,第一,矢量是有长度的线段,第二,矢量是有箭头的线段,由此我们可以用两个参数对应一个向量,方向和长度,这就是向量的第一种计算机表述

2.使用矢量的分量

以n个向量为基底向量,且这n个向量两两互相垂直,那么对以一个n维向量来说,可以表示为\left ( a_{1},a_{2},......,a_{n} \right ),所以很容易知道,对于n维向量来说,n个参数便可以表示

实操

这里我们分出定义和实现,以下是完整代码

完整代码

//vect.h
#ifndef VECTOR_H_
#define VECTOR_H_
#include <iostream>//并不推荐在头文件中引入其他库,否则很容易造成重复引用
namespace VECTOR
{
    class Vector{
    
    public:
        enum Mode{RECT,POL};
    private:
        double x;
        double y;
        double mag;
        double ang;
        Mode mode;
        
        void set_x();
        void set_y();
        void set_mag();
        void set_ang();
    public:
        Vector();
        Vector(double n_1,double n_2,Mode form = RECT);
        void reset(double n_1,double n_2,Mode form = RECT);
		~Vector();
		double xval() const {return x;};
		double yval() const {return y;};
		double magval() const{return mag;};
		double angval()	const{return ang;};
		
		Vector operator+(const Vector & b) const;
		Vector operator-(const Vector & b) const;
		Vector operator-() const;
		Vector operator*(double n ) const; 
		void polar_mode();
		void rect_mode();
		
		friend Vector operator*(double,const Vector & a);
		friend std::ostream &
				operator<<(std::ostream & os , const Vector & v);
    };
}
#endif
#include <cmath>
#include "vect.h"
using std::sqrt;
using std::sin;
using std::cos;
using std::atan;
using std::atan2;
using std::cout;

namespace VECTOR{
	const double Rad_to_deg = 45.0 / atan(1.0);
	
	void Vector::set_mag(){
		mag = sqrt(x*x + y*y);
	}
	
	void Vector::set_ang(){
		if(x == 0.0 && y == 0.0)
			ang = 0.0;
		else
			ang = atan2(y,x);
	}
	
	void Vector::set_x(){
		x = mag*cos(ang);
	} 
	
	void Vector::set_y(){
		y = mag*sin(ang);
	}


	Vector::Vector(){
		x = y = mag = ang = 0.0;
		mode = RECT;
	} 	

	Vector::Vector(double n_1,double n_2,Mode form){
		mode = form;
		if (form == RECT){
			x = n_1;
			y = n_2;
			set_mag();
			set_ang(); 
		}	 
		else if (form == POL){
			mag = n_1;
			ang = n_2;
			set_x();
			set_y();
		}
		else {
			cout <<"false argument!!!";
			cout <<"all will set to 0!!!";
			x = y = mag = ang = 0;
			form = RECT;
		}
	} 

	void Vector::reset(double n_1,double n_2,Mode form){
		mode = form;
		if (form == RECT){
			x = n_1;
			y = n_2;
			set_mag();
			set_ang(); 
		} 
		else if (form == POL){
			mag = n_1;
			ang = n_2 / Rad_to_deg; 
			set_x();
			set_y();
		}
		else {
			cout <<"false argument!!!";
			cout <<"all will set to 0!!!";
			x = y = mag = ang = 0;
			form = RECT;
		}
	}		 
	Vector::~Vector(){
		
	} //destructor
	void Vector::polar_mode(){
		mode = POL; 
	}

	void Vector::rect_mode(){
		mode = RECT;
	} 

	Vector Vector::operator-(const Vector & b) const{
		return Vector(x-b.x,y-b.y);
	} 

	Vector Vector::operator+(const Vector & b) const{
		return Vector(x+b.x,y+b.y);
	} 

	Vector Vector::operator-() const{
		return Vector(-x,-y);
	}

	Vector Vector::operator*(double n ) const {
		return Vector(n*x,n*y);
	}

	Vector operator*(double n , const Vector & a){
		return a * n;
	}
	
	std::ostream & operator<<(std::ostream & os,const Vector & v){
		if (v.mode == Vector::RECT){
			os << "(x,y) = (" << v.x << ","<< v.y <<")";
		}
		else if (v.mode == Vector::POL){
			os <<"(m,a) = (" << v.mag << "," << v.ang * Rad_to_deg <<")"; 
		} 
		return os;
		}
}

代码很长,但不用急,我们一步步来看

矢量的表示

public:
        enum Mode{RECT,POL};
private:
        double x;
        double y;
        double mag;
        double ang;
        Mode mode;

我们来看这一段定义,这里我们定义了四个参数,分别是x,y,mag,ang,对于x,y,对应的是矢量的坐标表示,而mag和ang对应的是矢量的极坐标表示,mag是长度,而ang则是角度

而Mode则是枚举量,用来控制矢量的表示方式,换句话说,就是指明矢量究竟是用何种方式表示,这里因为只有两种表示方式,所以只有两个量

矢量类的实现

public:
        Vector();
        Vector(double n_1,double n_2,Mode form = RECT);
        void reset(double n_1,double n_2,Mode form = RECT);
		~Vector();
		double xval() const {return x;};
		double yval() const {return y;};
		double magval() const{return mag;};
		double angval()	const{return ang;};

这里我们来看一下矢量类的实现,首先Vector函数和其重载函数为构造函数,定义类时会默认调用构造类,同样,~Vector函数为析构函数,一个类结束其生命周期后会自动调用,用来释放内存,但这里并没有什么用,但这里依然显性声明,意在让读者加深印象,如果要具体了解,详见以前的文章《浅析C++的类---2》

reset函数与Vector函数实现基本一致,至于其他函数,实现都较为简单,这里不再多加赘述

矢量的运算

public:
        Vector operator+(const Vector & b) const;
		Vector operator-(const Vector & b) const;
		Vector operator-() const;
		Vector operator*(double n ) const; 
        friend Vector operator*(double,const Vector & a);

矢量的运算是矢量类实现最为复杂,也是最能体现类强大,方便的地方

我们先来看看矢量类的加减是如何实现的

​Vector Vector::operator-(const Vector & b) const{
		return Vector(x-b.x,y-b.y);
} 

Vector Vector::operator+(const Vector & b) const{
		return Vector(x+b.x,y+b.y);
}

加和减十分相似,这里就以减为例,加以此类推

首先,函数接受一个Vector对象,并返回另一个Vector对象,这里有个需要注意的点,我们在类的实现时,习惯在函数后加const,指明该函数不会对传入参数进行更改的操作,这是个十分好的习惯,有助于我们规避一些奇怪且不易察觉的问题

我们重点来看看矢量的减法是如何是实现的,这里返回了一个 x-b.x,y-b.y 的Vector对象,我们不禁疑惑为什么一定是x-b.x,y-b.y呢?不能是b.x-x,b.y-y吗?这里牵扯到重载的特性,我们来详细地解释一下

运算符重载时发生了什么?

A = B - C;

对于如上的语句,实际上等效于

A = B.operate-(C);

左侧对象为操作象,而右侧对象为调用对象,如果颠倒,那么结果就会大不相同,C就会变为操作对象,B则为调用对象,这也是为什么一定是x-b.x,y-b.y,而不是b.x-x,b.y-y

我们再来看看矢量类的乘法实现

friend Vector operator*(double,const Vector & a);
Vector operator*(double n ) const; 
Vector Vector::operator*(double n ) const {
		return Vector(n*x,n*y);
}

Vector operator*(double n , const Vector & a){
		return a * n;
}

这里实际上并没有实现矢量和矢量的相乘,而是数字和矢量,而这种实现有助于认识类的特性——友元

和友元有什么关系?

参考上文对于如下

A = B * 2.75;

A = 2.75 * B;

实际上等同于

A = B.operate*(2.75);

A = 2.75.operate*(B); 

发现了什么不对了吗?

2.75明显是个右值,显然不能是操作对象,那么怎么办?这里我们要用到友元

也就是以friend为开头的函数,详细可以先参考以前的文章《谈谈运算符重载--更加全面了解C++》,但需要注意,友元函数不是成员函数,但是需要在类声明时声明,同时也不应在函数定义前加上类限定符::

尾声

今天我们借用一个实例,让大家更加全面地了解了类的基本特性,但这还远远不够!下一期会讨论如何在类中进行内存分配!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值