这里,我们借用《C++ Primer plus》中的实例——矢量类,更加深入了解一下重载到底高效和好用在哪里
什么是矢量?
矢量作为高中毕业生的必备知识,我们不再多加赘述,这里,我们集中注意力在计算机是如何对矢量进行抽象的
1.使用定义
矢量有两个必备条件,第一,矢量是有长度的线段,第二,矢量是有箭头的线段,由此我们可以用两个参数对应一个向量,方向和长度,这就是向量的第一种计算机表述
2.使用矢量的分量
以n个向量为基底向量,且这n个向量两两互相垂直,那么对以一个n维向量来说,可以表示为,所以很容易知道,对于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++》,但需要注意,友元函数不是成员函数,但是需要在类声明时声明,同时也不应在函数定义前加上类限定符::
尾声
今天我们借用一个实例,让大家更加全面地了解了类的基本特性,但这还远远不够!下一期会讨论如何在类中进行内存分配!
3152

被折叠的 条评论
为什么被折叠?



