第十一章 使用类
2020-02-24 10:41:40 5 举报
AI智能生成
C++ plus
作者其他创作
大纲/内容
运算符重载
原理
C++根据操作数的数目和类型决定采用哪种操作
格式
原型
void(返回类型)operator op( argument - list )
例子
void operator *( )表示重载*运算符
说明
Time 一点要有吗?不是说可用于非成员函数吗?
//非成员且非友元重载运算符一定要有一个参数为自定义对象。
友元也是这样吧。
//非成员且非友元重载运算符一定要有一个参数为自定义对象。
友元也是这样吧。
调用
Time total , A ,B;
total=A+B; //=A.operate+( ... );
total=A+B; //=A.operate+( ... );
原型
void Time::operator +( ... )
{
}
{
}
计算时间
(运算符重载例子)
(运算符重载例子)
源代码(不用重载)
#include<iostream>
class Time
{
private://可有可无
int hours;
int minutes;
public:
Time();//构造函数没有返回类型
Time( int h ,int m=0);//如果只初始化h的时候
void AddMin(int m);
void AddHr(int h);
void Reset( int h = 0,int m =0 );
Time Sum( const Time & t )const;
void show() const;
};//注意逗号
Time::Time()
{
hours = minutes = 0;
}
Time::Time(int h,int m)
{
hours = h;
minutes = m;
}
void Time::AddMin( int m )
{
minutes += m;
hours += minutes / 60 ;
minutes = minutes % 60;
}
void Time::AddHr( int h )
{
hours += h;//过了24小时怎么办?
}
void Time::Reset( int h, int m )
{
hours = h;
minutes = m;
}
Time Time::Sum( const Time & t ) const//传递引用的效率更快
{
Time sum;
sum.minutes = minutes + t.minutes; //在 Time 中其它同一个类的不同对象可以用私有成员。!!!!
sum.hours = hours + t.hours + sum.minutes / 60;
sum.minutes = sum.minutes % 60;
return sum ; //返回不是引用, 原因是sum是临时变量
}
void Time::show() const
{
std::cout<< hours << " hours , "<< minutes <<" minutes "<<std::endl;
}
int main()
{
Time codding( 2 , 4 );
Time fixing( 5 , 55 );
Time total;
total = codding.Sum(fixing);
total.show();
}
用重载
修改
Time Sum( const Time & t )const;
Time operator+( const Time & t )const;
Time Time::Sum( const Time & t ) const//传递引用的效率更快
{
.......
}
只改函数头!!
Time Time::operator+( const Time & t )const
{
}
Time Time::operator+( const Time & t )const
{
}
total = codding.Sum(fixing);
total = codding+ fixing;
相当于
total.operatr+( fixing )
total.operatr+( fixing )
说明
total = codding+ fixing; = codding.operatr+( fixing )
+左边是调用对象,右边是作为参数被传递的对象
Time A , B , C;
total = A+B+C;可以的。 = A.operator+( B.operator+(C) )
total = A+B+C;可以的。 = A.operator+( B.operator+(C) )
重载限制
前言
重载的运算符(有些例外)不必是成员函数,
但必须至少有一个操作数是用户自定义类型
但必须至少有一个操作数是用户自定义类型
???不必是成员函数???
操作数是比如+的两边,可以是友元函数,也可以是普通函数
,但必须有一个参数是自定义类,否则会出错。
操作数是比如+的两边,可以是友元函数,也可以是普通函数
,但必须有一个参数是自定义类,否则会出错。
限制
重载的运算符不必是成员函数。(可以是友元函数、也可以是普通函数)
重载后的运算符必须至少有一个操作数是用户自定义类型。
重载后的运算符必须至少有一个操作数是用户自定义类型。
如果是成员函数,第一个操作数一定是用户定义类。
使用运算符时不能违反运算符原来的句法规则。
不能将求模运算符(%)重载为使用一个操作数。
int x;
Time shiva;
% x;
% shiva;//不太懂。。
Time shiva;
% x;
% shiva;//不太懂。。
不能改变运算符的优先级
不能创建新运算符
不能定义operator**( )函数来表示求幂。
不能重载下面的运算符
sizeof
. 成员运算符
.* 成员指针运算符
:: 作用域解析运算符
?: 条件运算符
typeid 一个RTTI运算符
onst_cast 强制类型转换运算符
dynamic_cast 强制类型转换运算符
tatic_cast 强制类型转换运算符
只能通过成员函数重载
=
()函数调用运算符
[] 下标运算符
-> 通过指针访问类成员的运算符
可重载的P388
友元
作用
可以访问类对象的私有部分,函数与类的成员函数有相同的访问权限
应用
重载运算符+时候,只能 对象*double数值 形式,
使用友元可以 double * 对象 有效。
使用友元可以 double * 对象 有效。
类型
友元函数
友元类
友元成员函数
后两种15章
创建
原型
friend Time operator*( double m, const Time & t);
说明
原型放在类声明中
虽然在类声明中定义,但不是成员函数,不能用成员运算符来调用
函数定义
Time operator*(double m , const Time & t)
{
Time result;
result.minute += t.minutes;//直接使用私有成员
.......
return result;
}
{
Time result;
result.minute += t.minutes;//直接使用私有成员
.......
return result;
}
说明
不是成员函数,所以不用Time::x限定符
有了以上定义 可以 A = 2.7 * B;
一般一个操作数是类对象,另一个不是
原型:
Time operator*( const Time &t );
friend Time operator*( double m, const Time &t );
定义:
Time Time::operator*( const Time & t)
{
...
}
Time operator*( double m ,const Time &t)
{
return t*m;///!!!!关键,可以就是这样
}
原型:
Time operator*( const Time &t );
friend Time operator*( double m, const Time &t );
定义:
Time Time::operator*( const Time & t)
{
...
}
Time operator*( double m ,const Time &t)
{
return t*m;///!!!!关键,可以就是这样
}
Time operator*( double m ,const Time &t)
{
return t*m;///!!!!关键,可以就是这样
}
//短,可以内联起来。
{
return t*m;///!!!!关键,可以就是这样
}
//短,可以内联起来。
用成员函数处理私有值,所以可以不必是友元,但一般都会弄成友元。
不必是友元,因为没有处理私有数据
不必是友元,因为没有处理私有数据
重载<<
功能
Time trip;
cout<<trip;//vaild ,不用 trip.show();
cout<<trip;//vaild ,不用 trip.show();
重载位置
在Time 类中声明为一个友元函数,在iostream 类中声明危险
友元的原因
如果是成员函数,需要tirp<<cout形式
如果是成员函数,需要tirp<<cout形式
格式
原型
friend ostream & operator<<(ostream & os,const Time &t )
定义
ostream & operator<<( ostream & os ,const Time &t )
{
os<< t.hours <<" hours, "<<t.minutes<<" minutes ";
retrurn os;
}
{
os<< t.hours <<" hours, "<<t.minutes<<" minutes ";
retrurn os;
}
说明
cout<<tirp; === operator<<( cout , trip )
ostream类对象,有cerr,有ofstream类对象
可以输入到文件中。
ofstream File("...");
Time tirp(...);
File<<tirp;
ofstream File("...");
Time tirp(...);
File<<tirp;
类继承能够让ostream 引用能够指向ostream对象和ofstream 对象
如果是void operator<<( ostream & os ,const Time &t )
cout<<trip<<"(Tuesday)\n";//can·t do
cout<<trip<<"(Tuesday)\n";//can·t do
ostream 在名称空间std,所以要写成 std::ostream 形式。
重载运算符:作为成员函数还是非成员函数
对于狠多运算符,可以选择使用成员函数或非成员函数实现运算符重载。
一般非成员函数应该是友元函数。
两个都是类对象时候,只能选择其中一个,不然会产生二义性
再谈重载:一个矢量类
特点
在同一个对象中包含两种描述同一样东西的不同方式
矢量
什么是矢量?
有大小和方向的量,所以应该创建一个类表示矢量
描述
方式1
大小(长度)和方向(角度)描述
方式2
分量 x 和 y 描述
可以在一个类中同时定义两种方式来描述矢量
P400代码
代码400
类的自动转换和强制
类型转换
类型转换
初始化的方式
Stock sally(.....);
Stock sally=Stock(.......);
C++11:
Stock sally={....};
Stock sally{....};
Stock sally=Stock(.......);
C++11:
Stock sally={....};
Stock sally{....};
内容
基本类型转换为类
自动转换
强制转换
类转换为基本类型
自动转换
强制转换
基本类型转换为类
基本类型
可以是int double...
隐式转换
(自动转换)
(自动转换)
使用Stone(double)
情况。隐式转换
情况。隐式转换
将Stonewt对象初始化为double
Stonewt myCat(19.7)....
Stonewt myCat=Stonewt(19.7)//这个是显式转换吧。??
Stonewt myCat=Stonewt(19.7)//这个是显式转换吧。??
将double值赋给Stone对象
情况2
将double值传递给Stone参数
void show( Stonewt &st ,int n);
show( 422 , 2 )//422转换为Stone类型
show( 422 , 2 )//422转换为Stone类型
返回值声明为Stonewt类,但返回double值
在上述任意一种情况下,使用可转换为double类型的内置类型
Stone June(7000)
June = 9300
7000和9300都不是double,但是会转换为double .
如果构造函数还定义了Stonewt( long )形式,则上两条是错误的,有二义性。
June = 9300
7000和9300都不是double,但是会转换为double .
如果构造函数还定义了Stonewt( long )形式,则上两条是错误的,有二义性。
情况2
前提:构造函数
StoneWt( double );只有一个参数,
或者其他参数有默认值:
BrassPlus( const Brass &ba ,double m1=500, double r=0.1 );
或者其他参数有默认值:
BrassPlus( const Brass &ba ,double m1=500, double r=0.1 );
(原型)StoneWt( double )
StoneWt myCat( 19.6 ),其实就是意味着把double 转换为StoneWt对象
StoneWt myCat( 19.6 ),其实就是意味着把double 转换为StoneWt对象
格式
StoneWt myCat;
myCat = 19.6;
myCat = 19.6;
原理
使用构造函数StoneWt(double)创建一个临时的Stone对象,将19.6作为初始化值。
采用成员赋值方式将该临时对象的内容复制到myCat中。称为隐式转换。
采用成员赋值方式将该临时对象的内容复制到myCat中。称为隐式转换。
使用到了构造函数,赋值函数。
说明
构造函数只有一个参数才能够这样
显式转换
格式
myCat=Stonewt(19.6)//不是相当于调用构造函数吗
myCat=(Stonewt)19.6;
关闭自动转换方法
只能使用显式转换
只能使用显式转换
方法
前缀explicit
声明构造函数原型:
explicit Stonewt( double lbs );
explicit Stonewt( double lbs );
定义不用吗?对
myCat = 19.6// no ok
myCat=Stonewt(19.6)//OK
myCat=(Stonewt)19.6;//OK
myCat=Stonewt(19.6)//OK
myCat=(Stonewt)19.6;//OK
其它、对于char类型的
已知前提
Star( const char *s );
Star( const Spectral & ,int member = 1);//没有影响
没有operator=( const char *)函数
定义了Star::operator = ( const Star & )函数//没有也可以,系统会自动生成
Star( const Spectral & ,int member = 1);//没有影响
没有operator=( const char *)函数
定义了Star::operator = ( const Star & )函数//没有也可以,系统会自动生成
隐式转换/显式转换
Star north;
north = "polaris";
north = Star("polaris");
north = "polaris";
north = Star("polaris");
转换函数
(类转换为基本类型)
(类转换为基本类型)
创建
原型
operator double( );//转换为double类型
operator int( );//转换为int类型
operator int( );//转换为int类型
说明
转换函数必须是类方法
不能指定返回类型
不能有参数
最好声明为const类型.
operator double( ) const;
operator double( ) const;
不是const会有什么情况???
定义
Stonewt::operator int() const
{
return int( pounds + 0.5 );//四舍五入
}
{
return int( pounds + 0.5 );//四舍五入
}
Stone::operator double() const
{
return pounds;
}
{
return pounds;
}
Stone::operator int*() const
{....}//转换成int*类型也可以。
{....}//转换成int*类型也可以。
说明
返回什么数据成员要自己写。
调用
显式转换
Stonewt wolf(258.5);
double host = doubel(wolf);
= (double)wolf;
double host = doubel(wolf);
= (double)wolf;
隐式转换
Stonewt wells(20,3);
double star =wells;
double star =wells;
注意说明
1
如果同时定义了 operator double( ); operator int( );
Stonewt dog;
long gone = dog;//错误,二义性
long gone = (double )dog;//可以。
cout<<dog;//no ok。没有重载<<,只有一个double 或int转换时候可以,输出转换函数的返回类型。//???
Stonewt dog;
long gone = dog;//错误,二义性
long gone = (double )dog;//可以。
cout<<dog;//no ok。没有重载<<,只有一个double 或int转换时候可以,输出转换函数的返回类型。//???
2
Stonewt ius(6.0);
Stonewt lux=ius+20.2//ambiguous
不知道是将ius转换为double还是20.1转换为Stonewt
Stonewt lux=ius+20.2//ambiguous
不知道是将ius转换为double还是20.1转换为Stonewt
关闭隐式转换方法
1
原型加explict
public:
.....
explict operator double() const;
.....
explict operator double() const;
2
定义一个功能相同的非转换成员函数
int Stonewt::Stone_to_int();
{ return XX;}
int plb = dog.Stone_to_int();
int plb =dog;//这样是非法的。
{ return XX;}
int plb = dog.Stone_to_int();
int plb =dog;//这样是非法的。
转换函数和友元函数
(加法的类型转换)
(加法的类型转换)
前提
假设没有定义operator double()转换函数
重载了运算符+:
Stonewt operator +( const Stonewt & );
friend Stonewt operator+(Stonewt & ,Stonewt &);
friend Stonewt operator+(Stonewt & ,Stonewt &);
参数都是类对象
构造函数有 Stonewt(double ,double );
Stonewt( double );
Stonewt( double );
情况1
Stonewt a( 9.12 );
Stonewt b(12,8);
Stonewt total;
total = a+b;
Stonewt b(12,8);
Stonewt total;
total = a+b;
情况2
Stonewt a( 9.12 );
double b = 146.0;
Stonewt total;
total = a+b;
double b = 146.0;
Stonewt total;
total = a+b;
成员函数可以,友元函数可以
Stonewt(double )将 b 对应的参数转换为了类对象
情况3
Stonewt a( 9.12 );
double b = 146.0;
Stonewt total;
total = b+a;
double b = 146.0;
Stonewt total;
total = b+a;
成员函数不可以,不会试图将b转换为Stonewt对象。(因为转换的是)成员函数参数。
实时加法选择
方法一
Stonewt operator +( const Stonewt & );
或者 友元函数:
friend Stonewt operator+(Stonewt & ,Stonewt &);
或者 友元函数:
friend Stonewt operator+(Stonewt & ,Stonewt &);
优点
程序简短。
缺点
增加时间内存开销
方法二
Stone operator+(double x);
friend Stone operate+(double x,Stonewt &)
friend Stone operate+(double x,Stonewt &)
优点
运行速度快
缺点
程序长
选择
经常使用double Stonewt加法——方法二,不然方法一。
问题
什么时候不会将。。。转换成临时的类对象//??
eg:
1.上:
Stonewt a( 9.12 );
double b = 146.0;
Stonewt total;
total = a+b;
eg:
1.上:
Stonewt a( 9.12 );
double b = 146.0;
Stonewt total;
total = a+b;
会创建的原因:
有Stonewt(double )构造函数。
有Stonewt(double )构造函数。
其他
Complex Complex::subComplex(Complex &c)
{
Complex sub;
sub.real=real-c.real;
sub.imaginary=imaginary-c.imaginary;
return sub;
}
直接return Complex(real+c.real , imaginary + c.imaginary )//不可行。
why??
原因:
要用到复制构造函数,所用复制构造函数最好有const 这样
才能接受临时对象的引用。
why??
原因:
要用到复制构造函数,所用复制构造函数最好有const 这样
才能接受临时对象的引用。
类内嵌对象
(类的组合)
(类的组合)
含义
一个类里面的数据成员是另一个类的对象,即内嵌其他类的对象作为自己的成员。
代码
class Point{
........
};
class Line{
private:
Point start, end;
......
}
........
};
class Line{
private:
Point start, end;
......
}
构造函数处理方法
语法
(类型名)::(类型名) : 内嵌对象1(形参表),内嵌对象2(形参表)
例子
Line::Line(Point pstart , Point pend):start(pstart),end( pend )
复制构造函数
语法
C::C( C & c1 ) :b(c1.b),...
例子
Line::Line(Line &1):start(l.start),end( l.end )
对比
Line::Line(Line &1)
{
start=l.start;
end=l.end;
}
{
start=l.start;
end=l.end;
}
或者Line::Line(Point pstart , Point pend)
{
start=pstart);
end= pend ;
}
{
start=pstart);
end= pend ;
}
说明
上做法是可以的。
现象:
如果类中有内嵌类对象,在创建对象时,先创建内嵌的类对象。
如果类中有内嵌类对象,在创建对象时,先创建内嵌的类对象。
即
Line line1(....)
先创建line1 中的point 类start end对象。先调用的是point 类的构造函数。再调用line 的构造函数
Line line1(....)
先创建line1 中的point 类start end对象。先调用的是point 类的构造函数。再调用line 的构造函数
如果没有初始化列表,先调用point类的默认!构造函数。
在调用Line 的构造函数。
在调用Line 的构造函数。
析构函数的调用执行顺序与构造函数相反
代码说明
代码
#include <iostream>
#include <cmath>
using namespace std;
class Point{
private:
int x, y;
public:
Point(int a = 0, int b = 0)
{
x = a; y = b;
cout << "Point construction: " << x << ", "<< y << endl;
}
Point(Point &p) // copy constructor,其实数据成员是int类型,默认也是一样的
{
x = p.x;
y = p.y;
cout << "Point copy construction: " << x << ", "<< y << endl;
}
int getX()
{
return x;
}
int getY()
{
return y;
}
};
class Line{
private:
Point start, end;
public:
Line(Point pstart, Point pend):start(pstart), end(pend) // 组合类的构造函数对内前对象成员的初始化必须采用初始化列表形式
{
cout << "Line constructior " << endl;
}
float getDistance()
{
double x = double(end.getX()-start.getX());
double y = double(end.getY()-start.getY());
return (float)(sqrt)(x*x+y*y);
}
};
int main()
{
Point p1(10,20),p2(100,200);
Line line(p1,p2);
cout << "The distance is: "<<line.getDistance()<<endl; return 0;
}
#include <cmath>
using namespace std;
class Point{
private:
int x, y;
public:
Point(int a = 0, int b = 0)
{
x = a; y = b;
cout << "Point construction: " << x << ", "<< y << endl;
}
Point(Point &p) // copy constructor,其实数据成员是int类型,默认也是一样的
{
x = p.x;
y = p.y;
cout << "Point copy construction: " << x << ", "<< y << endl;
}
int getX()
{
return x;
}
int getY()
{
return y;
}
};
class Line{
private:
Point start, end;
public:
Line(Point pstart, Point pend):start(pstart), end(pend) // 组合类的构造函数对内前对象成员的初始化必须采用初始化列表形式
{
cout << "Line constructior " << endl;
}
float getDistance()
{
double x = double(end.getX()-start.getX());
double y = double(end.getY()-start.getY());
return (float)(sqrt)(x*x+y*y);
}
};
int main()
{
Point p1(10,20),p2(100,200);
Line line(p1,p2);
cout << "The distance is: "<<line.getDistance()<<endl; return 0;
}
局部
Line(Point pstart, Point pend):start(pstart), end(pend)
{
cout << "Line constructior " << endl;
}
{
cout << "Line constructior " << endl;
}
//结果
Point construction: 10, 20
Point construction: 100, 200
Point copy construction: 100, 200
Point copy construction: 10, 20
Point copy construction: 10, 20
Point copy construction: 100, 200
Line constructior
The distance is: 201.246
Point construction: 10, 20
Point construction: 100, 200
Point copy construction: 100, 200
Point copy construction: 10, 20
Point copy construction: 10, 20
Point copy construction: 100, 200
Line constructior
The distance is: 201.246
Line(Point pstart, Point pend)
{
start=pstart;
end=pend;
cout << "Line constructior " << endl;
}
{
start=pstart;
end=pend;
cout << "Line constructior " << endl;
}
Point construction: 10, 20
Point construction: 100, 200
Point copy construction: 100, 200
Point copy construction: 10, 20
Point construction: 0, 0
Point construction: 0, 0
Line constructior
The distance is: 201.246
Point construction: 100, 200
Point copy construction: 100, 200
Point copy construction: 10, 20
Point construction: 0, 0
Point construction: 0, 0
Line constructior
The distance is: 201.246
由上可看出,用初始化列表初始化类对象的效率更高。
系统默认函数
复制构造函数
初始化类成员时,使用相应类的复制构造函数
赋值运算符
数据成员如果是类,默认成员赋值使用相应类的赋值运算符
0 条评论
下一页