类的进阶使用方法:运算符重载、友元、类型转换(C++ Primer Plus 第11章笔记)

一、运算符重载

基本使用方法

运算符重载:又名“运算符多态”(函数重载==函数多态)

使用方法:

operator [符号](参数列表)
// examples
operator +(const A &a){...}
// 当出现两个类相加时,代码替换为:
sum = A.operator+(B);

operator []()重载[],重载C++中不存在的运算符是无效的

处理对象的函数建议在参数中使用引用&,因为能提高传递效率,但返回值不建议使用引用,因为函数内声明的对象是局部变量,后期被删除后将产生错误。

// 连续的加号是可行的
sum = a + b + c;
// 实质:
sum = a.operator+(b.operator+(c))
定义为一般函数

一般可以定义成成员函数,也可以定义成一般函数+友元

// 需要传递两个参数
friend Time operator+(Time &a, Time &b);	// 使函数能够访问类的私有成员
Time operator+(Time &a, Time &b){...}
运算符重载实例
序号运算符和实例
1一元运算符重载
2二元运算符重载
3关系运算符重载
4输入/输出运算符重载
5++ 和 – 运算符重载
6赋值运算符重载
7函数调用运算符 () 重载
8下标运算符[]重载
9类成员访问运算符 -> 重载
重载限制
  • 运算符重载至少应有一个操作数是用户定义的类型(防止对基本类型进行重载)
  • 运算符原有的句法规则不能变
  • 运算符的优先级不变
  • 不能创建新运算符
  • 部分运算符只能通过成员函数进行重载:=()[]->
  • 部分运算符只能通过一般函数进行重载:<<
  • 部分运算符不能重载:sizeof..*和(成员指针运算符)、::?:
类型转换情况

有时会出现Time a = 5 + b;的情况,此时需要使用友元函数实现类型的自动转换。(见“经验:加法运算符的重载”)

示例:
class Time{
	private:
		int h;
		int m;
		int s;
	public:
		Time(int _h=0, int _m=0, int _s=0){
			if(_h>=0&&_h<=24 && _m>=0&&_m<=60
				&& _s>=0&&_s<=60){
				h=_h;
				m=_m;
				s=_s;		
			}
			else{
				cout << "error" << endl;
				return;
			}
		}
		Time operator +(Time &t) const{
			Time tmp;
			tmp.s = (this->s+t.s)%60;
			tmp.m = (this->m+t.m+(this->s+t.s)/60)%60;
			tmp.h = (this->h+t.h+((this->m+t.m+
					(this->s+t.s)/60)/60))%24;
			return tmp;
		}
		void print(){		// ???????
			cout << setw(2) << setfill('0') 
				<< h << ":" << m << ":" << s << endl;
		}
		~Time(){}
};

int main(){
	Time a={12,32,11}, b={1,34,58};
	Time c=a+b;
	c.print();
	return 0;
}

二、友元

基本使用方法

友元共3种,友元函数、友元类、友元成员函数,可赋予类成员函数相同的访问权限。

但在类中声明友元不意味着类函数可以调用这个友元函数,而友元函数访问权限与成员函数相同。

friend Time operator *(double m, const Time &t);
// 用非成员函数重载运算符
Time operator *(double m, const Time &t){
    ...
}
Time a;
Time c = 3.98 * a;
// 转换为operator*(3.98, a);
// 等效于a.operator*(3.98);
重载<<运算符
void operator<<(ostream &os, const Time &t){
    // ostream对象要使用引用,因为我们应该使用cout对象本身
	os << t.hours << "," << t.minutes << endl;
}
// 以上调整顺序
// 其他ostream对象:cerr(错误流)、ofstream(文件)……
cout << t;			// 使用
// 缺陷:cout << Time << "." << endl无法实现
重载<<运算符(改进)

ostream中,operator<<返回一个ostream类,即cout,所以可以连续书写。

ostream& operator<<(ostream &os, const Time &t){
	...
     return os;
}

三、再探运算符重载

当涉及到类定义类型转换时,使用非成员函数可能更好。(详情见最后一节)

实例:矢量类

(阅读示例代码11.13、11.14)(可自行书写vector.cpp并对照)

在类中如果声明了枚举,则全局使用时应如namespace_name::class_name::enum_name

对类设计的启示:

我们可以通过使用枚举等形式为类确定模式,使得类可以适用于多个标准,如人民币、美元,公斤、英镑等,如:

class Weight{
	public:
    	enum Mode {CNY, USD};
		Weight(double n, Mode mode){
            if(mode == CNY) {Yuan = n; Dollars = 0;}
            else {Dollars = n; Yuan = 0;}
        }
    private:
    	double Yuan;
    	double Dollars;
};
一个运算符的多个重载,如减号与负号:
Vector operator-() const;
Vector operator-(const Vector b) const;

类的自动转换和强制类型转换

int *p = 10;			// invalid
int *p = (int *)(10);	 // valid
隐式类型转换

通过构造函数,此构造函数只能有一个形参或之后的形参都有默认值。

Day(int day);
explicit Day(int day);		// 通过explicit关键字防止隐式类型转换的发生
Day d = 18;
Day d = (Day)18;	// 有了explicit关键字,可以使用显式的强制类型转换
Day d = (Day)(17.22)	// 允许,但需要转换无二义性
// 隐式类型转换时,有临时对象的生成和复制构造函数的调用
转换函数:类转换为其他数据类型
operator typename();
ClassName::operator int() const{
    return int(ObjectName.a);
}
double a = (double) ObjectName;		// 使用
double b = ObjectName;
// 如果不希望这种隐式类型转换,可以添加explicit关键字
// 另一种方法:添加功能相同的普通函数,如:
int ClassName::ClassToInt(){
    return int(ObjectName.a);
}
// 如此一来,就只能使用int a = obj.ClassToInt()来实现转换了

该函数无返回值,是类方法,无参数。

经验:加法运算符的重载
Time::Time(int h){...}
Time a(2, 4, 5);
int b = 3;
Time c = a + b;		// 重载为成员函数或友元函数时皆可用,转换为a.operator+(b)
Time c = b + a;		// 重载为友元函数时才可用,因为b.operator(a)无效,无法进行类型转换

结论:重载加号运算符时优先使用友元函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值