4.运算符重载,explicit关键字和哑成员

二 模拟String类运算符重载

三 SmallInt类运算符重载

四 explicit关键字

五 哑成员

短整形运算符重载

//短整形运算符重载
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string.h>
using namespace std;
class Int
{
public:
	Int(long i = 0) : m_i(i)
	{}
	Int(const Int& x)
	{
		this->m_i = x.m_i;
	}
public:
	Int operator+(const Int& x)
	{
		return Int(this->m_i + x.m_i);
	}
	Int operator-(const Int& x)
	{
		return Int(this->m_i - x.m_i);
	}
	Int operator*(const Int& x)
	{
		return Int(this->m_i * x.m_i);
	}
	Int operator/(const Int& x)
	{
		return Int(this->m_i / x.m_i);
	}
	Int operator%(const Int& x)
	{
		return Int(this->m_i % x.m_i);
	}
public:
	Int& operator+=(const Int& x) // a += b;
	{
		this->m_i += x.m_i;
		return *this;
	}
	Int& operator-=(const Int& x)
	{
		this->m_i -= x.m_i;
		return *this;
	}
	Int& operator*=(const Int& x)
	{
		this->m_i *= x.m_i;
		return *this;
	}
	Int& operator/=(const Int& x)
	{
		this->m_i /= x.m_i;
		return *this;
	}
	Int& operator%=(const Int& x)
	{
		this->m_i %= x.m_i;
		return *this;
	}
public:
	Int operator>>(const Int& x) //a >> b
	{
		return Int(this->m_i >> x.m_i);
	}
	Int operator<<(const Int& x)
	{
		return Int(this->m_i << x.m_i);
	}
	Int& operator>>=(const Int& x)
	{
		this->m_i >>= x.m_i;
		return *this;
	}
	Int& operator<<=(const Int& x)
	{
		this->m_i <<= x.m_i;
		return *this;
	}
public:
	bool operator==(const Int& x)
	{
		if (this->m_i == x.m_i)
			return true;
		return false;
	}
	bool operator!=(const Int& x)
	{
		if (this->m_i != x.m_i)
			return true;
		return false;
	}
public:
	Int& operator++() //前置++    比后置++效率    可以引用返回
	{
		this->m_i++;
		return *this;
	}
	Int operator++(int)  //后置++
	{
		Int temp(*this);   //调用拷贝构造函数或:
		//Int tmp(this->m_i);  //用构造函数
		this->m_i++;      //*this++
		return temp;
	}
	Int& operator--() //前置--    比后置++效率    可以引用返回
	{
		this->m_i--;
		return *this;
	}
	Int operator--(int)  //后置--
	{
		Int Tmp(m_i);
		this->m_i--;
		return Tmp;
	}
public:
	int GetData()const
	{
		return this->m_i;
	}
private:
	long m_i;
};

int main()
{
	Int a(1);  //int a = 1;  a++  ++a  a-- --a
	Int b(2);

	cout << "a = " << a.GetData() << endl;
	cout << "b = " << b.GetData() << endl;

	Int result;
	result = a + b; //result = 1 + 2;
	cout << "result = " << result.GetData() << endl;

	result = ++a;
	result = a++;

	return 0;
}

以上同类型的符号重载只分析一种,因为+ - += -= * *= / /= % %= 都是一样的逻辑,知一晓百

(1) +号的重载 有整数 a, b = 1, c = 2, d = 3 a = b + c + d; 上述加法要能正常执行就必须等号右边每两个数相加并返回结果,计算顺序是无关的 要先算出(b+c) = 3, 然后再算出来3 + d = 6 或者先算出来(c + d) = 5, 然后再算出来b + 5 = 6 最终将结果6返回给a

有Test类的对象 a, b (1), c(2), d(3)
对象之间是无法直接相加的,因此需要运算符重载
对+号进行重载成类的成员函数如下:
a = b + c + d;
相当于:a = b + c.operator+(d); a = b.operator+( c.operator+(d) );
或: a = b.operator+© + d; a = ( b.operator+© ).operator+(d);

Int operator+(const Int& x)
{
	return Int(this->m_i + x.m_i);
}

类的成员函数都是用对象来驱动的,被调用的成员方法的this指针便指向驱动它的对象
用this指针访问前对象的私有数据成员this->m_i,用常引用的形参const Int& x访问后对象的私有数据成员x.m_i,将数据相加然后构造一个临时对象,返回值按值返回,便实现了将两个对象相加,并将结果返回,于是连续的对象相加也能实现

(2) +=重载 对象加等于a += b; 就是 a = a + b ; 单看a += b 则重载的+=是用对象a驱动的,a.operator+=(b); 最终只有a的值发生了改变,所以b可以用常引用。对象a的生命不受重载的+=函数影响,所以a的值发生改变后可以引用返回,以值的方式返回也不会有任何问题。我们都知道函数参数按值返回会借助临时变量,由于是对象按值返回,这样的话就会多调用构造函数,即临时变量占用了内存空间又调用了构造函数,导致效率明显降低了。 所以函数中不受函数控制生存周期的变量,强烈建议用引用返回
Int& operator+=(const Int& x) // a += b;
{
	this->m_i += x.m_i;
	return *this;
}
(3) 以下右移位操作符>>和右移位等的操作符>>=重载和上面分析的+和+=是一样的道理
Int operator>>(const Int& x) //a >> b
{
	return Int(this->m_i >> x.m_i);
}


Int& operator>>=(const Int& x)
{
	this->m_i >>= x.m_i;
	return *this;
}
(4) ==操作符重载如下
bool operator==(const Int& x)
{
	if (this->m_i == x.m_i)
		return true;
	return false;
}

觉得上述啰嗦吧,也可以如下的写法:
但是上面的有更好的逻辑性,且返回值类型匹配都是布尔类型

 bool operator==(const Int& x)
{
	return (this->m_i == x.m_i)
}
(5) 前置++ 和后置++ 的重载

Int& operator++() //前置++ 比后置++ 效率 可以引用返回
{
this->m_i++;
return *this;
}

Int operator++(int)  //后置++
{
	Int temp(*this);   //调用拷贝构造函数或:
	//Int tmp(this->m_i);  //用构造函数
	this->m_i++;      //*this++
	return temp;
}

前置++ 和后置++ 的重载,其实相当简单,只需要知道后置++ 要在函数列表设一个int就OK
前置++ ,是给对象先+1,然后以引用返回
后置++ ,是先返回对象,再给对象+1,错错错,大错特错。再函数中一但返回,函数就结束了,return后面的语句就不再执行了
正确的思路是先借助临时变量记录一下++的左值,然后给左值+1,然后按值返回临时变量(不能用引用返回,语义上临时变量不能用引用返回,临时变量一出作用 域就死亡了,死亡的东西就是未知的也许,那块临时空间还保留着出函数的值,恰巧结果可以正确,但未知和不确定的实物可能就是整个程序死亡的诱因,要严谨要严谨)。

模拟String类运算符重载
对于3中对模拟String类实现的剖析的例子,我们继续对其进行运算符重载
实现友元函数:length()
重载以下运算符:

operator[]
operator+
operator+=
operator> 	
operator<	
operator>=	 
operator<=	 
operator==
operator!=
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<assert.h>
#include<string.h>
using namespace std;

/
//length()
//operator[]
//operator+
//operator+=
//operator > < >= <= == !=
/
class String
{
public:
	String(const char* str = "")   //常类型到常类型
	{
		m_data = new char[strlen(str) + 1];
		strcpy(m_data, str);
	}
	String(const String& s)
	{
		m_data = new char[strlen(s.m_data) + 1];
		strcpy(m_data, s.m_data);
	}
	~String()
	{
		delete[]m_data;
		m_data = NULL;
	}
public:
	size_t length()const
	{
		return strlen(m_data);
	}

	char operator[](int i)
	{
		assert(i >= 0 && i < length());
		return m_data[i];
	}

	String& operator=(const String& s)
	{
		if (this != &s)
		{
			delete[](this->m_data);
			//new char[s.length() + 1];
			this->m_data = new char[s.length() + 1];  
			strcpy(this->m_data, s.m_data);
		}
		return *this;
	}

	String operator+(const String& s)
	{
		char* tmp = new char[length() + s.length() + 1];    //??????
		strcpy(tmp, this->m_data);
		strcat(tmp, s.m_data);

		String temp(tmp);
		delete []tmp;
		return temp;
	}
	
	String& operator+=(const String& s)
	{
		char* tmp = new char[strlen(this->m_data) + strlen(s.m_data) + 1];
		strcpy(tmp, this->m_data);
		strcat(tmp, s.m_data);

		delete[]m_data;
		this->m_data = tmp;
		return *this;
	}

public:
	bool operator==(const String& s)
	{
		if (strcmp(this->m_data, s.m_data) == 0)
			return true;
		return false;
	}
	bool operator!=(const String& s)
	{
		if (strcmp(this->m_data, s.m_data) != 0)
			return true;
		return false;
	}
	bool operator>(const String& s)
	{
		if (strcmp(this->m_data, s.m_data) > 0)
			return true;
		return false;
	}
	bool operator<(const String& s)
	{
		if (strcmp(this->m_data, s.m_data) < 0)
			return true;
		return false;
	}
	bool operator>=(const String& s)
	{
		if (strcmp(this->m_data, s.m_data) < 0)
			return false;
		return true;
	}
	bool operator<=(const String& s)
	{
		if (strcmp(this->m_data, s.m_data) > 0)
			return false;
		return true;
	}
private:
	char* m_data;
};


int main()
{
	String s1("Hello"); //s1[0] ==> H
	String s2("Bit.");

	s1 = s2;
	for (int i = 0; i < s1.length(); ++i)
		cout << s1[i];
	cout << endl;


	String s = s1 + s2; //s = HelloBit
	for (int i = 0; i < s.length(); ++i)
		cout << s[i];
	cout << endl;


	s1 += s2;    //

	for (int i = 0; i < s1.length(); ++i)
		cout << s1[i];
	cout << endl;

	return 0;
}

运算符重载之重载为成员函数:
下面的程序定义了一个简单的SmallInt类,用来表示从-128到127之间的整数。
类的唯一的数据成员val存放一个-128到127(包含-128和127这两个数)之间的整数,为了方便,
类SmallInt还重载了一些运算符。
阅读SmallInt的定义,回答题目后面的问题。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class SmallInt;
ostream& operator<<(ostream& os, const SmallInt& si);
istream& operator>>(istream& is, SmallInt& si);

//SmallInt si(1000);

class SmallInt
{
public:
	SmallInt(int i = 0);
	//重载插入和抽取运算符
	friend ostream& operator<<(ostream& os, const SmallInt& si);
	friend istream& operator>>(istream& is, SmallInt& si);

	//重载算术运算符
	SmallInt operator+(const SmallInt& si) { return SmallInt(val + si.val); }
	SmallInt operator-(const SmallInt& si) { return SmallInt(val - si.val); }
	SmallInt operator*(const SmallInt& si) { return SmallInt(val * si.val); }
	SmallInt operator/(const SmallInt& si) { return SmallInt(val / si.val); }
	//重载比较运算符
	bool operator==(const SmallInt& si) { return (val == si.val); }
private:
	char val;
};
SmallInt::SmallInt(int i)
{
	while (i > 127)
		i -= 256;
	while (i < -128)
		i += 256;
	val = i;
}

ostream& operator<<(ostream& os, const SmallInt& si)
{
	os << (int)si.val;
	return os;
}

istream& operator>>(istream& is, SmallInt& si)
{
	int tmp;
	is >> tmp;
	si = SmallInt(tmp);
	return is;
}

int  main()
{
	SmallInt si(1000);
	cout << si << endl;
	SmallInt si1;
	cin >> si1;
	cout << si1 << endl; 
	return 0;
}

问题:(本小题4分)上面的类定义中,
重载的插入运算符和抽取运算符被定义为类的友元函数?//可以
能不能将这两个运算符定义为类的成员函数?//不能
如果能,写出函数原型,如果不能,说明理由。

语法上可以,语义上不可以代码如下:
///

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//SmallInt si(1000);

class SmallInt
{
public:
	SmallInt(int i = 0);
	//重载插入和抽取运算符
	ostream& operator<<(ostream& os)
	{
		os << (int)this->val;
		return os;
	}
	istream& operator>>(istream& is)
	{
		int tmp;
		is >> tmp;
		this->val = tmp;
		return is;
	}

	//重载算术运算符
	SmallInt operator+(const SmallInt& si) { return SmallInt(val + si.val); }
	SmallInt operator-(const SmallInt& si) { return SmallInt(val - si.val); }
	SmallInt operator*(const SmallInt& si) { return SmallInt(val * si.val); }
	SmallInt operator/(const SmallInt& si) { return SmallInt(val / si.val); }
	//重载比较运算符
	bool operator==(const SmallInt& si) { return (val == si.val); }
private:
	char val;
};
SmallInt::SmallInt(int i)
{
	while (i > 127)
		i -= 256;
	while (i < -128)
		i += 256;
	val = i;
}
int  main()
{
	SmallInt si(1000);
	si << cout << endl;
	SmallInt si1;
	si1 >> cin;
	si1 << cout << endl;
	return 0;
}

按题目要求重载,只能像知识总结中一中的那种不符合使用习惯(使用就必须如下)的重载办法

si << cout << endl;
si1 >> cin;
si1 << cout << endl;

重载为友元函数是可以的

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//SmallInt si(1000);

class SmallInt
{
public:
	SmallInt(int i = 0);
	//重载插入和抽取运算符
	friend ostream& operator<<(ostream& os, const SmallInt& s);
	friend istream& operator>>(istream& is, SmallInt& s);

	//重载算术运算符
	SmallInt operator+(const SmallInt& si) { return SmallInt(val + si.val); }
	SmallInt operator-(const SmallInt& si) { return SmallInt(val - si.val); }
	SmallInt operator*(const SmallInt& si) { return SmallInt(val * si.val); }
	SmallInt operator/(const SmallInt& si) { return SmallInt(val / si.val); }
	//重载比较运算符
	bool operator==(const SmallInt& si) { return (val == si.val); }
private:
	char val;
};
SmallInt::SmallInt(int i)
{
	while (i > 127)
		i -= 256;
	while (i < -128)
		i += 256;
	val = i;
}
ostream& operator<<(ostream& os, const SmallInt& s)
{
	os << s.val;
	return os;
}
istream& operator>>(istream& is, SmallInt& s)
{
	int tmp;
	is >> tmp;
	s.val = tmp;
	return is;
}

int  main()
{
	SmallInt si(65);
	SmallInt si1;
	cout << si<<endl;
	cin>>si1;
	cout << si1 << endl;
	return 0;
}

explicit关键字

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Test
{
public:
	Test(int d = 0) : m_data(d)  	//构造函数的类型转换(隐式)Test t1;   t1 = 998;
	{
		m_count++;
	}
	~Test()
	{
		m_count--;
	}
public:
	int GetData()const
	{return this->m_data;}
public:
	operator int() //强制转换
	{
		return this->m_data;
	}
public:
	void list()
	{
		fun();
	}
	static void fun()
	{
		m_count = 10;
		cout<<"Test::fun() static"<<endl;
	}
private:
	int m_data;
	static int m_count;
};

int Test::m_count = 0; //

int main()
{
	Test t1;
	t1 = 998;
	return 0;
}

t1 = 998; //理论上我们知道普通变量是不能给对象赋值的
但是实际上构造函数不仅可以构造对象还能够隐式的做类型转换,998给对象赋值,会首先调用构造函数将整形998构造成一个匿名对象,然后用匿名对象去给对象t1赋值。

如果我们给上述代码的构造函数前加上explicit 关键字,构造函数便不能做隐式的类型转化了

explicit Test(int d = 0) : m_data(d)
{
	m_count++;
}

这样写编译就无法通过如下图:

哑成员

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Test
{
public:
	Test(int d = 0) :m_data(d)
	{}
public:
	class Tmp //内部类
	{
	public:
		Tmp(int a , int b) : x(a), y(b)
		{}
	public:
		int GetX()
		{return x;}
	public:
		int x;  //
		int y;  //哑成员
	};
public:
	int m_data;
};
int main()
{
	Test T;
	cout <<"sizeof(T) = "<< sizeof(T) << endl;
	Test::Tmp tp(1,2);
	cout<<"sizeof(tp) = "<<sizeof(tp) << endl;
	return 0;
}

内部类的数据成员称为哑成员,因为实例的外部类对象的大小不包含内部类的大小,并且外部类中只能访问自己的数据成员及函数成员,无法访问内部类的任何成员

创建类中类的对象时必须加外部类的作用域访问符:
Test::Tmp tp(1,2); //创建内部类Tmp的对象时就得加外部类Test的作用域访问符Test::
创建的外部类的对象大小不带内部类

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值