C++语言程序设计
第 6讲,数组、指针与字符串
友元 friend
?友元是 C++提供的一种破坏数据封装和数
据隐藏的机制。
?通过将一个模块声明为另一个模块的友元,
一个模块能够引用到另一个模块中本是被
隐藏的信息。
?可以使用友元函数和友元类。
?为了确保数据的完整性,及数据封装与隐
藏的原则,建议尽量不使用或少使用友元。
例 5-6,使用友元函数计算两点距离
class Point //Point类声明
{ public,//外部接口
Point(int xx=0,int yy=0) {X=xx;Y=yy;}
int GetX() { return X; }
int GetY() { return Y; }
friend float fDist (Point &a,Point &b);
private,//私有数据成员
int X,Y;
};
float fDist ( Point& a,Point& b )
{
double dx = a.X-b.X;
double dy = a.Y-b.Y;
return (float)sqrt(dx*dx+dy*dy);
}
int main()
{ Point myp1(1.0f,1.0f),myp2(4.0f,5.0f);
cout<<"The distance is,;
cout<< fDist (myp1,myp2) << endl;
return 0;
}
友元类
class A {
friend class B; public,
void Display() { cout << x << endl; }
private,int x;
}
class B { public,
void Set(int i); void Display();
private,A a;
};
void B::Set(int i)
{
a.x = i;
}
void B::Display()
{
a.Display();
}
访问 A的私有成员
但在 A 中不能访问 B 的私有成员。 !!!
常类型 const
?常类型 是只读的意思。
?常类型的对象必须进行初始化,而且不能
被更新。
?常引用,被引用的对象不能被更新。
const 类型说明符 &引用名
?常对象,必须进行初始化,不能被更新。
类名 const 对象名
常对象举例
class A
{
public,
A(int i,int j) { x = i; y = j; }
,.,
private,
int x,y;
};
A const a(3,4); // a是常对象,不能被修改
例 5-8,常成员函数举例
class R
{ public,
R(int r1,int r2){ R1 = r1; R2 = r2; }
void print();
void print() const;
private,
int R1,R2;
};
void R::print()
{ cout<<R1<<":"<<R2<<endl;
}
void R::print() const
{ cout<<R1<<";"<<R2<<endl;
}
int main()
{ R a(5,4);
a.print();
//调用 void print()
const R b(20,52);
b.print();
//调用 void print() const
}
例 5-9,常数据成员举例
class A
{public,
A(int i);
void print();
const int& r;
private,
const int a;
static const int b; //静态常数据成员
};
const int A::b=10;
A::A(int i),a(i),r(a) {}
void A::print()
{ cout<<a<<":"<<b<<":"<<r<<endl; }
int main()
{/*建立对象 a和 b,并以 100和 0作为初值,
分别调用构造函数,通过构造函数的初始化
列表给对象的常数据成员赋初值 */
A a1(100),a2(0);
a1.print();
a2.print();
}
运行结果,
100:10:100
0:10:0
数组
?数组必须 先声明,后使用 。
? 一维数组的声明,
? 数据类型 数组名 [ 常量表达式 ];
? 例如,int b[10];
表示 b 为整型数组,有 10个元素,b[0]...b[9]
?多维数组的声明,
数据类型 标识符 [常量表达式 1][常量表达式 2] … ;
? 例如,int a[5][3];
? 表示 a为整型二维数组, 其中第一维有 5个下标
( 0~4), 第二维有 3个下标 ( 0~2), 数组的元素个
数为 15,可以用于存放 5行 3列的整型数据表格 。
int main()
{ int A[10],B[10];
for(int i=0;i<10;i++)
{
A[i]=i*2-1;
B[10-i-1]=A[i];
}
for(i=0;i<10;i++)
{
cout << "A[" << i << "]=,<< A[i];
cout << " B[" << i << "]=" << B[i]<<endl;
}
}
?
例 6-1:数组的说明与使用 输出,
A[0]=-1 B[0]=17
A[1]=1 B[1]=15
A[2]=3 B[2]=13
A[3]=5 B[3]=11
A[4]=7 B[4]=9
A[5]=9 B[5]=7
A[6]=11 B[6]=5
A[7]=13 B[7]=3
A[8]=15 B[8]=1
A[9]=17 B[9]=-1
一维数组的初始化
可以在编译阶段使数组得到初值,
?在声明数组时对数组元素赋以初值。
例如,static int a[10]={0,1,2,3,4,5,6,7,8,9};
?可以只给一部分元素赋初值。
例如,static int a[10]={0,1,2,3,4};
?在对全部数组元素赋初值时,可以不指定数组长度。
例如,static int a[]={1,2,3,4,5}
二维数组
?二维数组,float a[3][4];
?存储顺序
按行存放,上例中数组 a的存储顺序为,
a00 a01 a02 a03 a10 a11 a12 a13 a20 a21 a22 a23
?引用
例如,b[1][2]=a[2][3]/2
a[0]—— a00 a01 a02 a03
a[1]—— a10 a11 a12 a13
a[2]—— a20 a21 a22 a23
a 可以理解为,
二维数组的初始化
?将所有数据写在一个 {}内,按顺序赋值
例如,static int a[3][4]
= {1,2,3,4,5,6,7,8,9,10,11,12};
?分行给二维数组赋初值
例如,static int a[3][4]
= {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
?可以对部分元素赋初值
例如,static int a[3][4]={{1},{0,6},{0,0,11}};
数组作为函数参数
?数组元素作实参,与单个变量一样。
?数组名作参数,形、实参数都应是数组名,
类型要一样,传送的是数组首地址。对形
参数组的改变会直接影响到实参数组。
void RowSum(int A[][4],int nrow)
{ int sum;
for (int i = 0; i < nrow; i++)
{ sum = 0;
for(int j = 0; j < 4; j++)
sum += A[i][j];
cout << "Sum of row " << i
<< " is " << sum << endl;
A[i][0]=sum;
}
}
int main()
{ int Table[3][4] =
{{1,2,3,4},{2,3,4,5},{3,4,5,6}};
。。。。。。
RowSum(Table,3);
。。。。。。
}
对象数组初始化
?数组中每一个元素对象被创建时,系统都
会调用类构造函数初始化该对象。
?通过初始化列表赋值。
例,
Point A[2]={Point(1,2),Point(3,4)};
?如果没有为数组元素指定显式初始值,数
组元素便使用默认值初始化(调用默认构
造函数)。
int main()
{
cout<<"Entering main..."<<endl;
Point A[2];
for(int i=0;i<2;i++)
A[i].Move(i+10,i+20);
cout<<"Exiting main..."<<endl;
return 0;
}
指针变量的概念
? 概念
指针,内存地址,用于
间接访问内存单元
指针变量,
用于存放地址的变量
? 声明
例,static int i;
static int *i_pointer=&i;
指向整型变量的指针
? 使用
例 1,i=3;
例 2,*i_pointer = 3;
内存用户数据区
变量 i
变量 j
变量
i_pointer
3
6
2000
2000
2004
3010
2000 3
i_pointer
*i_pointer
i
2000
int main()
{int *i_pointer; //声明 int型指针 i_pointer
int i; //声明 int型数 i
i_pointer=&i; //取 i的地址赋给 i_pointer
i=10; //int型数赋初值
cout<<"Output int i="<<i<<endl;
//输出 int型数的值
cout<<"Output int pointer i="
<< *i_pointer << endl;
//输出 int型指针所指地址的内容
}
程序运行的结果是,
Output int i=10
Output int pointer i=10
例 6-6,void类型指针的使用
void vobject; //错,不能声明 void类型的变量
void *pv; //对,可以声明 void类型的指针
int *pint; int i;
int main()
{
pv = &i; //void类型指针指向整型变量
//void指针赋值给 int指针需要类型强制转换,
pint = (int *)pv;
}
指向常量的指针
?不能通过指针来改变所指对象的值,但指针本身
可以改变,可以指向另外的对象。
?例 1
char *name1="John"; //name1是一般指针
*name1='A'; //编译正确,运行出错
?例 2
const char *name1="John"; //指向常量的指针
char s[]="abc";
name1=s; //正确,name1本身的值可以改变
*name1='1'; //编译时指出错误
typedef char * LPBYTE;
const LPBYTE name1= "John";
char s[]="abc";
name1=s; //ERROR!
指向数组元素的指针
?声明与赋值
例,int a[10],*pa;
pa=&a[0]; 或 pa=a;
?通过指针引用数组元素
经过上述声明及赋值后,
? *pa就是 a[0],*(pa+1)就是 a[1],...,
*(pa+i)就是 a[i]。
? a[i],*(pa+i),*(a+i),pa[i]都是等效的。
? 不能写 a++,因为 a是数组首地址是常量。
例 6-7,用三种方法输出各元素
程序 1:使用数组名和下标,
void main()
{
int a[10]={1,2,3,4,5,6,7,8,9,0};
for(int i=0; i<10; i++)
cout << a[i] <<,,;
cout << endl;
}
程序 2:使用数组名和指针运算,
void main()
{
int a[10]={1,2,3,4,5,6,7,8,9,0};
for(int i=0; i<10; i++)
cout << *( a+i) <<,,;
cout << endl;
}
例 6-7,用三种方法输出各元素
程序 3:使用指针变量,
void main()
{
int a[10]={1,2,3,4,5,6,7,8,9,0};
for(int *p=a; p<(a+10); p++)
cout << *p <<,,;
cout << endl;
}
例 6-17 动态创建对象数组
int main()
{
Point *Ptr1=new Point[2]; // 创建
Ptr[0].Move(5,10);
Ptr[1].Move(15,20);
cout<<“Deleting..."<<endl;
delete[] Ptr1; // 删除
return 0;
}
?