捋了一下这些日子使用指针碰到过的坑,如图:
可通过右下角目录直接到达相应模块。
一、函数与指针:
这模块分为 指向函数的指针 和 返回指针的函数。
1、指向函数的指针(即函数指针):
要定义函数指针,首先得清楚函数的类型,
函数的类型由:函数的参数 和 返回类型 来区分。
例子如下:
int Max(double,double);
double*Max(double,double,int);
第一个式子的 函数参数 为两个double,返回类型 为int,
第二个式子的 函数参数 为三个,返回类型 为double*(即指向double型的指针)。
函数的参数 和 返回类型 统称为函数的接口。
感觉有点绕,但大体的意思就是 如果函数的接口相同就可以归纳为同一类,如图:
注意头字母大写是为了与头文件< cmath >中的函数区分。
更具体的例子:
//compareNum.h
#include<iostream>
#include<cmath>
using namespace std;
double Max(double a,double b){
cout<<"两数间最大值:";
return max(a,b);
}
double Min(double a,double b){
cout<<"两数间最小值:";
return min(a,b);
}
double Aver(double a,double b){
cout<<"两数的平均值:";
return (a+b)/2;
}
//Main.cpp
#include"compareNum.h"
typedef double Compare(double,double);//注意这里
int main()
{
double p=2,q=3;
Compare *Com;//此时声明一个指针Com: 返回类型为double, 函数参数为(double,double)。
//double (*Com)(double,double); 也可以像左边这样直接声明,但个人感觉是上面的好用,下面会说到原因。
Com=Max;
cout<<Com(p,q)<<'\n';
Com=Min;
cout<<Com(p,q)<<'\n';
Com=Aver;
cout<<Com(p,q)<<'\n';
return 0;
}
运行结果:
//用typedef更好用的原因:
typedef double Compare(double,double); Compare *Com;
double (*Com)(double,double);
//假设有下面的函数:
double freeCompare(Compare *Com,double a,double b);
double freeCompare(double(*Com)(double,double),double a,double b);
比较第一和第二个,
老实说,把一推东西塞进 函数参数列表 看得有点头皮发麻。。。
函数指针与普通指针类似,这里最重要是把 函数 抽象成 和(int、double) 是同一类的“东西”。
为什么可以这样抽象的原因:
typedef double Compare(double,double); Compare *Com1;
double *Com2;
sizeof(Com1)==sizeof(Com2)==8;
即在计算机里,都是一块连续的区域。
2、返回指针的函数:
这个部分相对上面的更容易理解,看下例子:
#include<iostream>
#include<cmath>
using namespace std;
double *Max(double a,double b){
double *p;
double t=max(a,b);
p=&t;
return p;//返回p的地址
}
int main()
{
double *u=Max(3,5);//u指向 Max(3,5) 传回来的p的地址
cout<<*u;
return 0;
}
运行结果是5。
这个部分在数据结构中比较常用,比如 排序后返回头节点 或者是 关于树的操作返回根节点。
二、数组与指针:
这模块分为 指针数组 和 数组指针。
这两个的区分主要看后缀,
前一个是一个数组,后一个是一个指针。
1、指针数组:
数组里的元素全部都是指针的 数组,如下:
#include<iostream>
using namespace std;
int main()
{
int *pi[3];
int a=11,b=22,c=33;
pi[0]=&a;
pi[1]=&b;
pi[2]=&c;
for(int i=0;i<3;i++){//输出数组里的指针 指向的数据
cout<<*pi[i]<<' ';
}
cout<<endl;
for(int i=0;i<3;i++){//输出数组里的 指针(即数据的地址)
cout<<pi[i]<<' ';
}
cout<<endl;
for(int i=0;i<3;i++){//输出数组的 地址(即存放指针的地址)
cout<<&pi[i]<<' ';
}
return 0;
}
运行结果:
有没有发现规律?
中间的三个地址之间相差4字节(对应a、b、c),
最下面的三个地址之间相差8字节(对应指针数组)。
2、数组指针:
指向数组的指针,例子如下:
#include<iostream>
using namespace std;
int main()
{
int a[3]={4,5,6};
int *p=a;
for(int i=0;i<3;i++){
cout<<a[i]<<' '<<&a[i];//用数组输出 数据和地址
cout<<*(p+i)<<' '<<(p+i)<<'\t';//用指针输出 数据和地址
cout<<endl;
}
return 0;
}
运行结果:
三、类与指针:
这模块要说的是 基类指针与虚函数 和 this指针。
1、基类指针与虚函数:
这部分常用的是 基类指针 指向 派生类对象 从而实现多态性,例子如下:
//A.h
#include<iostream>
using namespace std;
class A{
public:
virtual out(){
cout<<"A:你好,多态性"<<endl;
}
out1(){
cout<<"A:你好,非多态性"<<endl;
}
};
class B:public A{
public:
out(){
cout<<"B:你好,多态性"<<endl;
}
out1(){
cout<<"A:你好,非多态性"<<endl;
}
};
class C:public A{
public:
out(){
cout<<"C:你好,多态性"<<endl;
}
out1(){
cout<<"A:你好,非多态性"<<endl;
}
};
//Main.cpp
#include"A.h"
int main()
{
A u0;
B u1;
C u2;
A *p=&u0;
p->out();
p->out1();
cout<<endl;
p=&u1;
p->out();
p->out1();
((B*)p)->out1();
cout<<endl;
p=&u2;
p->out();
p->out1();
((C*)p)->out1();
return 0;
}
运行结果:
截图在同一面中可能更直观一些。
基类指针 指向 派生类对象 与 virtual 联用能实现多态,
但是如果不与 virtual 联用则只能调用基类成员;
使用 基类指针 指向 派生类对象 的原因:可以实现 异质链表 从而用一个基类的头节点管理 各个不同的派生类对象。
2、this指针:
//p.h
#include<iostream>
using namespace std;
class p{
public:
void out1(){
cout<<x<<' '<<y<<endl;
}
void out2(){
cout<<this->x<<' '<<this->y<<endl;
}
private:
int x=2,y=3;
};
//Main.cpp
#include"p.h"
int main()
{
p u;
u.out1();
u.out2();
return 0;
}
this指针需要注意的点是 不能显式说明,但可以在成员函数中直接显式使用;
this指针初始化后 不能 修改和赋值。
以上的内容都是咱使用过并且认为相对重要的点,如果想看更多详细内容请翻回手上的 教材。
如果对你有用 或者 让你想起了某些已经遗忘的知识 ,
顺手给我来波三连哇。