oid 好不好?
考虑: a = b = c;
//等价于a.operator=(b.operator=(c));String 好不好?为什么是 String &
运算符重载时, 好的风格 -- 尽量保留运算符原本的特性
考虑: (a=b)=c; //会修改a的值
分别等价于:(a.operator=(b)).operator=(c);
5. 上面的String类是否就没有问题了?
?
为 String类编写 复制构造函数 时,会面临和 ‘=’ 同样的问题,用同样的方法处理
String::String(String & s)
{
if(s.str) {
str = new char[strlen(s.str)+1];
strcpy(str, s.str);
}
else
str = NULL;
}
4.3 运算符重载为友元函数
1. 运算符重载为友元
通常,将运算符重载为类的成员函数重载为友元函数的情况:
? 成员函数不能满足使用要求
? 普通函数,又不能访问类的私有成员
class Complex{
double real, imag;
public:
Complex(double r, double i):real(r), imag(i){ };
Complex operator+(double r);
};
Complex Complex::operator+(double r){ //能解释 c+5
return Complex(real + r, imag);
} 经过上述重载后:
Complex c ;
c = c + 5; //有定义,相当于 c = c.operator +(5);
但是:
c = 5 + c; //编译出错
为了使得上述表达式能成立, 需要将+重载为普通函数
Complex operator+ (double r, const Complex & c) {
//能解释 5+c
return Complex( c.real + r, c.imag);
} 普通函数不能访问私有成员 —> 将运算符+重载为友元函数
class Complex {
double real, imag;
public:
Complex( double r, double i):real(r),imag(i){ };
Complex operator+( double r );
friend Complex operator + (double r, const Complex & c);
};
4.4 实例 – 长度可变的整型数组类(可变长整型数组)
class CArray
{
int size; //数组元素的个数
int
*ptr; //指向动态分配的数组
public:
CArray(int s = 0); //s代表数组元素的个数
CArray(CArray & a);
~CArray();
void push_back(int v); //用于在数组尾部添加一个元素v
CArray & operator=( const CArray & a);
//用于数组对象间的赋值
int length() { return size; } //返回数组元素个数
int & CArray::operator[](inti) //返回值为 int 不行!不支持 a[i] = 4
{//用以支持根据下标访问数组元素,
//如n = a[i]和a[i] = 4; 这样的语句
return ptr[i];
}
};
CArray::CArray(int s):size(s)
{
if( s == 0)
ptr = NULL;
else
ptr = new int[s];
}
CArray::CArray(CArray & a)
{
if( !a.ptr) {
ptr = NULL;
size = 0;
return;
}
ptr = new int[a.size];
memcpy( ptr, a.ptr, sizeof(int ) * a.size);
size = a.size;
}
因为我们不写赋值构造函数的话,编译器自动生成的那个赋值构造函数会执行赋值的功能。这里所说的赋值的工作,只是把a1的成员变量赋值到a2里面去。那a1的成员变量ptr
被赋值到a2里面去,那自然a2.ptr就等于a1.ptr。那也就是说,a2的ptr和a1的ptr都指向了同一片存储空间。
CArray::~CArray()
{
if( ptr) delete [] ptr;
}
CArray & CArray::operator=( const CArray & a)
{ //赋值号的作用是使“=”左边对象里存放的数组,大小和内容都和右边的对象一样
if( ptr == a.ptr) //防止a=a这样的赋值导致出错
return * this;
if( a.ptr == NULL) { //如果a里面的数组是空的
if( ptr ) delete [] ptr;
ptr = NULL;
size = 0;
return * this;
}
if( size < a.size) { //如果原有空间够大,就不用分配新的空间
? if(ptr) delete [] ptr;
? ptr = new int[a.size];
}
memcpy( ptr,a.ptr,sizeof(int)*a.size);
size = a.size;
return * this;
} // CArray & CArray::operator=( const CArray & a)
void CArray::push_back(int v)
{ //在数组尾部添加一个元素
?if( ptr) {
? int * tmpPtr = new int[size+1]; //重新分配空间
? memcpy(tmpPtr,ptr,sizeof(int)*size); //拷贝原数组内容
? delete [] ptr;
? ptr = tmpPtr;
?}
?else //数组本来是空的
? ptr = new int[1];
?ptr[size++] = v; //加入新的数组元素
}
4.5 流插入运算符和流提取运算符的重载
1. 问题
cout << 5 << “this”;为什么能够成立?
cout是什么?“<<” 为什么能用在 cout上?
2. 流插入运算符的重载
cout 是在 iostream 中定义的,ostream 类的对象。
“<<” 能用在cout 上是因为,在iostream里对 “<<” 进行了重载。
考虑,怎么重载才能使得cout << 5; 和 cout << “this”都能成立?
有可能按以下方式重载成 ostream类的成员函数:
void ostream::operator<<(int n)
{
…… //输出n的代码
return;
}
?
因为ostream已经封装好了,不可能把这个重载写成成员函数,所以写成全局函数。
cout << 5 ; 即 cout.operator<<(5);
cout << “this”; 即 cout.operator<<( “this” );
怎么重载才能使得cout << 5 << “this” ;成立?
ostream & ostream::operator<<(int n)
{
…… //输出n的代码
return * this;
}
ostream & ostream::operator<<( const char * s )
{
…… //输出s的代码
return * this;
} cout << 5 << “this”;本质上的函数调用的形式是什么?
cout.operator<<(5).operator<<(“this”);
?
假定下面程序输出为 5hello, 该补写些什么
class CStudent{
public: int nAge;
};
int main(){
CStudent s ;
s.nAge = 5;
cout << s <<"hello";
return 0;
}
ostream & operator<<( ostream & o,const CStudent & s){
o << s.nAge ;
return o;
}
?
3. 例题
?
(教材P218)例子。可略了。
?