C/C++程序设计
1
九、访问成员运算符 - >
十、数组下标运算符 [ ]
十一、梅举类变量的运算性质
C/C++程序设计
2
九、访问成员运算符 - >
一般轻易不重载直接类的箭头访问运算符,直接类也就是成员数据集合具有物理数学意义的类。
瞄准这个直接类,建立一个相关的类,这个相关的类可以称为间接类,在间接类中提交访问成员运算符函数
operator->()
这是一种迂回曲折的编程技术。
箭头运算符 ->被当作单目运算符函数出现于间接类中,
仅作为非静态的成员函数。
但调用时依然期望横跨两个操作数,间接类的实例当作指针操作直接类的成员。
C/C++程序设计
3
箭头 - >运算符函数在间接类 CTypeOther的声明类似于其它单目运算符的格式:
CType* operator->();
/类类型 * 箭头运算符函数名 ();
在类外定义的格式为:
CType* CTypeOther::operator->()
{;...; return (CType*)ptrExpre ;}
上面的返回值 ptrExpre可在构造函数 CTypeOther中初始化。
C/C++程序设计
4
[例 ] smart指针 other涉及的主要步骤
#include<stdio.h>
class CType
{ public,
int m_right;
} obj={1};
class CTypeOther
{ CType* ptrExpre;
public,CTypeOther (CType* pobj) { ptrExpre=pobj; }
CType* operator->() { return ptrExpre ; }
};
C/C++程序设计
5
void main ()
{ CTypeOther other (&obj);
other.operator->()->m_right+=1000;
printf ("%d\t",other->m_right);
CTypeOther * pO=&other;
(*pO)->m_right+=1000;
pO->operator ->();
pO->operator - >()->m_right+=1000;
printf ("%d\n",(&obj) ->m_right);
}
C/C++程序设计
6
//输出结果,1001 3001
说明,->m_right的左侧指望一个 CType*的对象指针,但取而代之的是 CTypeOther的对象 other,形成
other->m_right的格式,other相对于 CType的对象来说就相当于一个指针,本身又是对象。
这是 other的灵巧之处,原因在于 other->m_right诱发编译器调用 other.operator->(),该函数返回指向对象 obj的指针。
C/C++程序设计
7
隐含调用 other->m_right编译器暗中转换为,
other.operator->()-> m_right
进一步处理为,(&obj)->m_right
(&obj)->m_right是对象指针访问自身类的数据成员,
这是原来的对象指针直接访问途径。
而 smart指针间接的操作 other->m_right是新增的间接的逼近方式。
C/C++程序设计
8
十、数组下标运算符 [ ]
下标运算符 [ ]是索引数组集合数据的运算符。对于整数
k,type型变量 e,type*型指针 p,表达式 p[k]是左值,除非限定为只读数组。数组下标使用格式为:
p[k]=e; e= p[k];
对于如下形式的 CType类:
struct CType{ type * m_pData; } obj;
访问公共的指针成员 m_pData指向的内存的格式为,
obj.m_pData[k]
重载下标运算符 [ ]可将上面的调用格式简洁的书写为,
obj[k]
C/C++程序设计
9
在 C语言中通过引进一个指针 p= obj.m_pData索引:
p[k]
下标运算符 operator[ ]函数是非静态的成员函数,带有一个整型参量。下标运算符函数可提供两种版本,只读版本和非只读版本。
1,只读下标运算符函数定义和调用特点:
const type& CType::operator[ ] (int n) const
{ ;...; return m_pData[n]; }
调用匹配,obj[k] 对象 [下标 ]
obj[k]作为右值表达式。
此处的对象可以是只读对象也可以是非只读对象。
C/C++程序设计
10
2,非只读下标运算符函数一般格式定义和调用特点:
type& CType::operator[ ] (int n)
{;...; return m_pData[n];}
调用匹配,obj[k] 可变对象 [下标 ]
obj[k]可为左值表达式。此种形式的对象名只能是非只读的对象。
下面例子提供数组下标运算符函数以及类型转换函数。
下标运算符函数实施了数组索引下标的越界检查,提供一定程度安全屏障。
在全局范围内布置指针 pch,对于此种安全措施提出挑战。对这个存在指针成员的字符串类,尚应提供拷贝构造函数进行深拷贝。
C/C++程序设计
11
[例 ] 字符串类的下标运算符函数
#include <stdio.h>
#include<string.h>
char * pch;
class CType
{ public:
CType (const char* s) ;
CType& operator= (const CType& r);
operator const char*() const{ return m_pData;}
operator char*() { return m_pData;}
const char& operator[] (int n) const ;
char& operator[] (int n) ;
C/C++程序设计
12
~CType() { delete [ ] m_pData; }
private,
char * m_pData;
int m_nLength;
static char cErro;
};
char CType::cErro=-1;
const char& CType::operator[ ] (int n) const
{ if (n>=0&&n<m_nLength) return m_pData[n];
else { printf ("\nconst!bound violated!\t");
return cErro; }
}
C/C++程序设计
13
char& CType::operator[ ] (int n)
{ if(n>=0&&n<m_nLength)
return m_pData[n];
else { printf ("bound violated!\n");
return cErro; }
}
CType::CType (const char* s)
{ m_nLength=strlen (s);
m_pData=new char [m_nLength+1];
strcpy (m_pData,s);
pch=m_pData;
}
C/C++程序设计
14
CType& CType::operator= (const CType& r)
{ if(m_nLength!=r.m_nLength)
{ delete [ ] m_pData;
m_nLength=r.m_nLength;
m_pData=new char [m_nLength+1];
}
strcpy (m_pData,r.m_pData);
return *this;
}
inline void display(const char* s) { printf ("%s\t",s); }
void main()
{ const CType abc ("ABCDEFGH");
C/C++程序设计
15
display (abc);
pch[0]='a';pch[1]='b';pch[2]='c';
const char cs[ ]="12345";
pch=(char*)cs+1; *pch='*';
CType num=cs; display (num);
const int len=strlen (num);
int k ; for (k=0;k<len;k++) num [k]=abc[k];
display (num);num[k]=abc [k+len]; num=abc;
display (num);
for (k=0;k<len;k++) pch[k]=cs[k];
display (num);
}
C/C++程序设计
16
对于 num[k]=abc[k],abc是只读对象仅匹配只读成员函数版本,num是变动的对象优先调用非 const型的成员函数。
运算符类型转换函数 operator char*()将对象的类型转换指定的类型 char*。 display(num) 调用 operator char*()
转换函数。 display(abc) 调用只读类型转换函数。
有了下标运算符函数和类型转换函数以及等号赋值运算符函数,该类的对象可以视为普通的字符数组使用,但同时具有动态可扩展的灵活性。
ch是一个全局指针,在构造函数中赋值。全局指针的进攻性是相当强的,本例中全局指针 pch轻而易举的入侵改写了只读数组 cs的内存区和只读对象 abc的私有数据区。
C/C++程序设计
17
十一、梅举类变量的运算性质
enum关键字主流用法是引入多个梅举常数,这些梅举常数可以在其作用范围内直接采用。
梅举常数相当于 #define指令定义的整型符号常数,但加上了作用域的限制。
系统支持梅举变量参入其它仅需要右值运算符的运算,
在这种运算过程中将梅举变量提升为 int型变量。将整型变量转换为梅举变量时需要采用强制类型转换。
C/C++程序设计
18
例如:
EName e =(EName) (intExpre);
梅举变量在程序中采用的机会不多,主要原因是梅举变量仅享受 operator=,operator&提供的待遇。
其余要求左值的运算符,例如自增 L++运算符以及复合赋值运算符 e+=n等,需要根据梅举类的实际取值自行定义。
操作梅举变量的运算符函数只能是全局函数。
C/C++程序设计
19
[例 ] 重载需要左值作为操作数的运算符以支撑梅举变量的相关运算
#include<stdio.h>
enum EName { E1=1,E2,E3,E4,E5,E6,E7 };
const EName operator++(EName& e,int)
{ const EName temp=e;
e=(EName) (temp+1);
return temp;
}
inline EName& operator+=(EName& e,int x)
{ return e=(EName) (e+x); }
inline EName& operator-=(EName& e,int x)
{ return e=(EName) (e-x); }
C/C++程序设计
20
void main()
{ EName e1=E1,e2= (EName)1;
e2+=E6; e1++;
if (e1==E2)
printf ("e1==E2=%d\t",e1);
e2-=E6;
if (e2==E1)
printf ("e2==E1=%d\n",e2);
}
//输出结果,e1==E2=2 e2==E1=1
C/C++程序设计
21