第四章 指针和引用一,与对象有关的指针
1,对象的指针形式:
类名 *对象的指针名例子:
Cylinder cylinder1;
Cylinder *pcylinder = &cylinder1;
Pcylinder -> sercylinder(20.0,20.0);
Pcylinder -> surface_area();
2 This指针
This 指针是指向对象的指针,它隐含在类成员函数中,用来指向成员函数所属类的正在被操作的对象 。
编译器会把对象的地址赋给成员函数中的 this指针 。 确保访问到的数据成员属于这个对象例如:
Date::Date(int y,int m,int d)
{
this -> year = y;
this -> month = m;
this -> day = d;
}
注:静态成员函数没有 this 指针
3,指向类成员的指针指向数据成员的指针:
数据类型 类名,:*数据成员指针名;
赋值:
数据成员指针名 = &类名::数据成员名例如:
classA
{
public:

int a;

};
int A::*p = &A::a;
访问数据成员:
对象名,*类成员指针名 //通过对象名访问对象指针名 -> *类成员指针名 //通过对象的指针访问
void main()
{
A a(1.5,2.6),*pa=&a; //pa指向 a对象
double A::*px = &A::x; //声明指向 A的 x的指针
a.outputxy();
cout<<"a.x=\t"<<a.x<<endl;
cout<<"a.*px=\t"<<a.*px<<endl; //数据成员指针访问 x
cout<<"pa->*px=\t"<<pa->*px<<endl;//指向对象的指针及数据成员指针访问 x
二,void和 const指针
1,Void 指针
Void指针可以指向任何 C++数据类型 。 Void 指针的值赋给其他类型的指针,需要进行强制类型转换 。
例如:
int a;
int *p1=&a;
void *p2=p1;
int *p3=p2;
int *p4=(int *)p2;
例子:
#include <iostream.h>
void main()
{
int a=1;
int *pa1= &a,*pa2 = 0;
void *pv = pa1;
cout<<"a=\t"<<a<<endl;
cout<<“*(int *)pv=\t”<<*(int *)pv<<endl;//输出结果时 void指针必须强制类型转换
pa2 = (int *)pv;
cout<<"*pa2=\t"<<*pa2<<endl;
}
2.指针传递给函数的四种方法:
l) 非常量数据的非常量指针 ( 指针和数据都可以修改 )
char *sPtr
2) 常量数据的非常量指针 ( 指针可以指向其它数据,但不能通过指针修改数据 )
const char *sPtr
Int a = 1,b =2
Const int *p1=0;
P1=&a;
*p1=2;
a=2;
p1=&b;
3) 非常量数据的常量指针 ( 指针总是指向相同的地址,地址里的数据可以通过指针修改 )
int *const ptr=&x; //声明为 const指针应在声明时初始化
Int a=1,b=2;
Int *const p1=&a;
Int *const p2;
*p1=2;
p1=&b;
4) 常量数据的常量指针 ( 指针总是指向相同的地址,地址里的数据不能修改 )
const int *const ptr=&x;
三,动态内存分配程序所使用的存储区:
1,静态存储区 ―― 程序初始化时分配的存储区 。
2,栈 ―― 局部变量分配的存储区域 。
3,堆 ―― 自由存储单元,动态分配存储区 。
1,运算符 new
形式:
指针 = new 数据类型
new 返回首地址 。
例子:
int *p;
p=new int ;
int *p = new int(1); //可以初始化
int *p=new int[5]; //数组动态分配
int *p=new int[5]={1,2,3,4,5}; //错,不能动态分配内存时对数组初始化
2,运算符 delete
形式:
delete 指针名; //释放非数组内存单元
delete[] 指针名; //释放数组内存单元例子:
int *p1 =new int(1);
Date *p2 = new date[5];
Delete p1;
Delete[] p2;
Int *p=new int;
*p=2;
p=new int;
//内存泄漏类中的数据成员也可以是指向堆中某一内存单元的指针,内存单元可以由构造函数分配,析构函数来释放
//exp4_9
#include <iostream.h>
class A
{
public:
A(int x1=0,int y1=0);
~A();
void setxy(int x1,int y1);
void outputxy() const;
private:
int *x,*y;
};
//exp4_9.cpp
#include "exp4_9.h"
A::A(int x1,int y1)
{
x = new int(x1);
y = new int(y1);
}
A::~A()
{
delete x;
delete y;
}
void A::setxy(int x1,int y1)
{
*x = x1;
*y = y1;
}
voidA::outputxy() const
{
cout<<"*x=\t"<<*x<<"\t*y=\t"<<*y<<endl;
}
void main()
{
A *p = new A(1,1);
p->outputxy();
p->setxy(2,5);
p->outputxy();
delete p;
}
四,函数指针声明:数据类型 ( *指针名 ) ( 参数表 )
赋值:函数指针名 = 函数名通过函数指针名来调用函数
#include <iostream.h>
void add(double x,double y);
void sub(double x,double y);
void mul(double x,double y);
void div(double x,double y);
void main()
{
void (* pfun)(double,double);
double x,y;
char choice;
cout << "Please enter the two digit that will is calculated:";
cin >> x >> y;
cout << endl;
cout << "Please the type of calculation:(+ or - or * or /)";
cin >> choice;
cout << endl;
if(choice == '+') pfun = add;
else if(choice == '-') pfun = sub;
else if(choice == '*') pfun = mul;
else if(choice == '/') pfun = div;
else
cout << "The input is error!";
pfun(x,y);
}
void add(double x,double y)
{
cout<<"x="<<x<<"\ty="<<y<<"\t\tx+y="<<x+y<<endl;
}
void sub(double x,double y)
{
cout<<"x="<<x<<"\ty="<<y<<"\t\tx-y="<<x-y<<endl;
}
void mul(double x,double y)
{
cout<<"x="<<x<<"\ty="<<y<<"\t\tx*y="<<x*y<<endl;
}
void div(double x,double y)
{
cout<<"x="<<x<<"\ty="<<y<<"\t\tx/y="<<x/y<<endl;
}
五,指向类成员函数的函数指针声明:数据类型 (类名:,*指针名 )( 参数表 )
赋值:指针名 =类名::成员函数名成员函数指针的使用,其实与函数指针的使用方法完全一样 。 只不过与数据成员指针一样,需要利用相应类的对象来调用它
//exp4_20.h
#include <iostream.h>
classA
{
public:
A(double x1=0.0,double y1=0.0);
~A(){}
void setxy(double x1,double y1);
void outputxy() const;
double x;
private:
double y;
};
#include "exp4_20.h"
A::A(double x1,double y1):x(x1),y(y1)
{
}
voidA::setxy(double x1,double y1)
{
x = x1;
y = y1;
}
voidA::outputxy() const
{
cout<<"x=\t"<<x<<"\ty="<<y<<endl;
}
void main()
{
A a(1.5,2.6),*pa = &a;
void (A::*pfun)()const;
pfun=A::outputxy;
(a.*pfun)();
(pa->*pfun)();
指向静态成员的指针
//exp4_21.h
#include <iostream.h>
classA
{
public:
A(){};
~A(){}
void setx(double x1);
static void outputx();
static double x;
};
//exp4_21.cpp
#include "exp4_21.h"
double A::x =2;
voidA::setx(double x1)
{ x = x1;}
voidA::outputx()
{
cout<<"x=\t"<<x<<endl;
}
void main()
{
double *px=&A::x;
void (*pfun)();
pfun=A::outputx;
cout<<"x\t"<<*px<<endl;
pfun();
}
注:静态成员不属于任何对象六,字符串:
string.h是进行字符串操作的头文件,其中定义有一些字符串函数的原型 。
常用的字符串函数有:
(1) int strlen(const char*s);
(2) char *strcpy(char *dest,const char *src);
(3) char *strcat(char *dest,const char *src);
(4) int strcmp(const char *s1,const char *s2);
(5) char *strchr(const char *s,int c);
(6) char *strrchr(const char *s,int c);
(7) char *strstr(const char *s1,const char *s2);
七,引用
1引用是个别名,当建立引用时,程序用另一个变量或对象 (目标 )的名字初始化它,从那时起,引用作为目标的别名而使用,对引用的改动实际就是对目标的改动 。
2引用不占存储空间,声明引用时,目标的存储状态不会改变,引 用 在 声 明 时 必 须 被 初 始 化 。
int &b ; //错误
3一般函数只能返回一个值,如果程序需要从函数返回两个值,可以采用引用给函数传递两个参数,然后由函数目标中写入两个值,相当于返回两个值引用传递(引用参数)
1250x3492034
符号表
para
void func1()
{
int i = 125;
func2 (i);
cout << i;
}
void func2(int & para)
{
para = 45;
}
引用参数的特点
好处
–简单、自然(调用时不需要加 &)
–节省栈空间
–通过引用参数返回函数的操作结果
坏处
–可能对实在参数产生副作用(用 const限制)
#include <iostream.h>
void main()
{
int a(0);
int &ra=a; //引用变量必须初始化
cout<<"a="<<a<<endl;
cout<<"ra="<<ra<<endl;
ra=2;
cout<<"a="<<a<<endl;
cout<<"ra="<<ra<<endl;
cout<<"&a="<<&a<<endl;
cout<<"&ra="<<&ra<<endl;
}
把引用用作函数参数引用可以实现指针作为函数参数的功能
1)引用做函数形参时,实参用变量名
2) 引用做参数时,对引用的改变就是直接通过引用来改变实参变量的值
3) 引用常量 const:模拟按值调用,不可修改实参,避免传递大对象副本的开销
–性能提示:消除复制大量数据的开销
–软件工程视点:安全性
#include <iostream.h>
void swap(int &a,int &b);
void main(void)
{
int x(1),y(2);
cout<<"In main function"<<endl;
cout<<"Before swap:";
cout<<"x="<<x<<'\t'<<"y="<<y<<endl;
swap(x,y);
cout<<"In main function:"<<endl;
cout<<"After swap:";
cout<<"x="<<x<<'\t'<<"y="<<y<<endl;
}
void swap(int &a,int &b)
{
int temp;
cout<<"In Swap function:"<<endl;
cout<<"Before swap:";
cout<<"a="<<a<<'\t'<<"b="<<b<<endl;
temp=a;
a=b;
b=temp;
cout<<"After swap:";
cout<<"a="<<a<<'\t'<<"b="<<b<<endl;
}
八,以同类的对象为实象声明对象,将调用拷贝构造函数拷贝构造函数特点:
1) 是一种特殊的构造函数
2) 函数名同类名,不指定返回类型
3) 只有一个参数,并且是对某个对象的引用
4) 每个类都必须有一个拷贝初始化函数,格式如下:
<类名 >::<拷贝初始化构造函数 >(const<类名 >&<引用名 >)
5) 系统没有说明时编译系统自动生成一个功能:将参数代表的对象逐域拷贝到新创建的对象中,即用一个已知的对象初始化一个被创建的同类对象 。
//exp4_26
#include <iostream.h>
classA
{
public:
A(int a=0)
{
cout<<”调用构造函数,<<endl;
x=a;
}
A(const A &a)
{
cout<<”调用拷贝构造函数,<<endl;
x=a.x;
}
~A {}
void output()
{
cout<<”x=”<<x<<endl;
}
private:
int x;
};
void main()
{
A a1(2);
a1.output();
A a2(a1);
a2.outputx();
}
对于类中的数据成员中有指针的情况,采用默认的构造函数是将产生虚指针 。
为了避免这种情况,程序中应自定义拷贝构造函数,为对象中的指针成员申请必要的内存以进行深拷贝 。
深拷贝:在成功分配内存后,把被复制对象的指针指向内存中的值拷贝到新申请的内存中
#include <iostream.h>
classA
{
public:
A();
A(const A & a)
~A();
void setx(int x1)
{ *x=x1;}
void outputx() const
{ cout<<”*x=”<<*x<<endl;}
int getx() const
{ return *x;}
private:
int *x;
};
A::A()
{x=new int ;
*x=2;}
A::A(const A &a)
{ x=new int;
*x=a.getx();}
A::~A()
{ delete x;
x=0;}
void main()
{ A a1;
a1.outputx();
A a2(a1);
a2.outputx();
a1.setx(5);
……,.}
对象指针做函数参数,
优点:
实现传址调用
不进行副本的拷贝,提高效率,减少时空开销分析下面程序的输出结果,
#include<iostream.h>
class M{
public:
M( ) {x=y=0;}
M(int i,int j){x=i;y=j;}
void copy(M *m);
void setxy (int i,int j){x=i;y=j;}
void print(){cout<<x<“,”<<y<<endl;}
private:
int x,y;
}
void M::copy(M *m)
{x=m->x;y=m->y;}
void fun(M m1,M *m2)
{m1.setxy(12,15);
m2->setxy(22,25);}
void main()
{M p(5,7),q;
q.copy(&p);
fun(p,&q);
p.print();
q.print();
}
对象引用做函数参数优点:
具有对象指针做函数参数的优点,但更简单更直接 。
将上例改为对象引用:
void copy(M *m)改为 void copy(M &m){x=m.x;y=m.y;}
void fun(M m1,M *m2)改为
void fun(M m1,M&m2)
{m1.setxy(12,15);
m2.setxy(22,25);
}