2.1函数指针
2.1.1定义
类型名(*指针变量名)(parameter 1, parameter 2…)
e.g:
int(*p)(int, char)
2.1.2使用方法
函数指针名(actual parameter list)
eg1:
#include <iostream>
using namespace std;
void outmin(int a,int b){a<b?cout<<a<<endl:cout<<b<<endl;}//条件运算符,问号前语句为真执行冒号前问号后语句,为假则执行冒号后语句
int main(){
void(*p)(int,int)=outmin;
p(4,5);
return 0;
}
返回值:4
eg2:
//qsor函数
void qsort(void* base, int a, unsigned int width, int(*p)(const void*, const void*))
//void qsort(数组起始地址,数组元素个数,每个元素大小,自定义大小规则)
//自定义规则:如果返回值为负整数,则第一个元素在前,如果返回值是0,则相等,如果返回值是正整数,那么第一个元素在后。
eg3:
调用qsort,使数组按照个位数从小到大排序。
#include <iostream>
using namespace std;
int mycompare(const void* elem1,const void* elem2){
return *(int*)elem1%10-*(int*)elem2%10;//强制转换int*,然后比较个位数大小
}
int main(){
int an[]={8,123,11,10,4};
qsort(an,5,sizeof(int),mycompare);
for(int i=0;i<5;++i){
cout<<an[i]<<endl;
}
return 0;
}
返回值:
10
11
123
4
8
2.2位运算
2.2.1 按位于 &
将两操作数进行与操作,只有两者的相应二进制位皆为1时,结果为1.
eg1:
21&18
21二进制: 10101
18二进制: 10010
21&18二进制:10000(十进制16)
eg2:
判断一个int型变量n第七位是否为1
n & 0x80==0x80
0x80:10000000
若两者相等则为1
2.2.2按位或 |
将两操作数进行或操作,只有两者的相应二进制位只要有一个为1时,结果为1.
eg:
21|18
21二进制: 10101
18二进制: 10010
21&18二进制:10111(十进制23)
2.2.3按位异或 ^
将两操作数进行异或操作,只有两者的相应二进制位不相同时,结果为1.
eg:
21^18
21二进制: 10101
18二进制: 10010
21&18二进制:00111(十进制7)
ps:
1.异或运算的特点”若a^b=c,则有c^b=a, c^a=b”可以用来最简单的解密加密。
2.可以实现不通过零时变量进行两个变量值的交换:
int a=5,b=7;
a=a^b;
b=b^a;
a=a^b;
2.2.4按位非 ~
将一操作数进行非操作,0变成1,1变成0
eg:
~21
21二进制: 0000 0000 0000 0000 0000 0000 0001 0101
~21二进制: 1111 1111 1111 1111 1111 1111 1110 1010(0xffffffea)
2.2.5左移运算符 <<
a<
eg:
9<<4
9二进制: 0000 0000 0000 0000 0000 0000 0000 1001
9<<4二进制:0000 0000 0000 0000 0000 0000 1001 0000(十进制144)
2.2.6右移运算符 >>
a>>b
将a二进制位右移b位,移出右边丢弃。若为有符号数,右移时符号位一起移动,左边补充原符号位相同的数作为新符号位。
右移n位相当于左操作数除以
2n
后往小里取整
eg:
15>>2
15二进制: 1000 0000 0000 0000 0000 0000 0000 1111
15>>2二进制:1111 0000 0000 0000 0000 0000 0000 0011(3)
2.2.7思考题
Q:有两个int型变量a和n(
0<=n<=31
)求a的第n位
A1:
(a>>n)&1
A2:
(a&(1<<n)>>n)
2.3引用
2.3.1定义
引用等价于这个变量,对其修改等于对原变量修改
类型名&引用名=某变量名
e.g:
int n=4;
int& r=n //r的类型是int&
r=5;//此时n也变为5
2.3.2用法
eg1:用引用交换两个变量的值
#include <iostream>
using namespace std;
void swap(int &a,int &b){
int tmp;
tmp=a;a=b;b=tmp;
}
int main(){
int n1=5,n2=3;
swap(n1,n2);
cout<<n1<<" "<<n2<<endl;
return 0;
}
3 5
eg2:引用作为返回值
#include <iostream>
using namespace std;
int n=4;
int& setvalue(){
return n;
}
int main(){
setvalue()=40;
cout<<n<<endl;
return 0;
}
40
2.3.3常引用
常引用不能通过引用的量来修改原来的值。
int n;
const int& r=n;
2.4const关键字和常量
2.4.1定义常量
const int max=23
const double pi=3.14
const char* p="hello world!"
2.4.2定义常量指针
不可通过常量指针修改其指向的内容
int n,m;
const int* p=&n;
*p=5//编译出错,不可通过常量指针修改指定内容
n=4;//ok
p=&m;//ok,但常量指针的指向可以变化
不能把常量指针赋值给非常量指针,但反过来可以
const int* p1; int* p2;
p1=p2;//ok
p2=p1;//编译出错,不可将非常量赋值给常量
p2=(int*)p1;//ok,强制类型转换,但有风险
函数参数为常量指针时,可避免函数内部不小心改变参数指针所指向的地方(后面函数的参数一般都如此定义)
void myprint(const char* p){
strcpy(p,"this");//编译出错,不可改变参数指针指向的地方
cout<<*p<<endl;//ok,简单的调用可以,但不能改变
}
2.5动态内存分配
2.5.1用new运算符实现动态内存分配
2.5.1.1第一种用法,分配一个变量
t* p=new t;//p为t*类指针
2.5.1.2第二种用法,分配一个数组
t* p=new t[n];//p为t*类指针,n为数组个数
ps:特别注意的是,p作为数组时,可以用p[n]调用数组中第n个元素,很常见
2.5.2用delete运算符释放动态分配的内存
用new分配的内存空间,一定要记得用delete进行释放
2.5.2.1删除 new出来的变量
int* p=new int;
*p=5;
delete p;
delete p;//编译错误,空间不能被delete多次
2.5.2.2删除new出来的数组
int* p=new int[20];
p[0]=1;
delete [] p;
2.6内联函数
减少函数调用的开销
用法如下:
inline int max(int a, int b){
return a<b?b:a;
}
2.7函数重载
多个函数有同一个函数的名字,但函数的参数类型不同,叫做函数的重载
ex:以下三个是重载关系
int max(double n1, double n2){}
int max(int n1, int n2){}
int max(int n1, int n2, int n3){}
编译器会根据实际传入的参数来判定调用哪个函数。
ex:
max(3.2,2.5);//调用上述第一行的函数
max(2,4);//调用上述第二行的函数
max(1,2,3);//调用上述第三行的函数
2.8函数的缺省函数
调用函数时,可以不写相应的参数,编译器会以缺省值代替
void func(int x1, int x2=2, int x3=3){cout<<x1+x2+x3<<endl;}
func(10);//等效于func(10,2,3)
func(10,8);//等效于func(10,8,3)
func(10, ,8);//编译错误,只能最右边的连续若干个参数缺省
可以提高程序的可扩充性
2.9类的基础定义
2.9.1类的定义
class 类名
{
访问说明符:
变量1
变量2
成员函数
访问说明符:
变量3
变量4
成员函数
};
2.9.2调用类中的成员变量和成员函数
用法1:
对象名.成员函数名字/成员变量名字
用法2:
对象指针->成员函数名字/成员变量名字
用法3:
引用名.成员名
eg:
#include <iostream>
using namespace std;
class crectangle{
public:
int w,h;//成员变量
void init(int a,int b){w=a;h=b;}//成员函数1
int area(){return w*h;}//成员函数2
int perimeter(){return 2*(w+h);}//成员函数3
};
int main(){
int w=4,h=6;
//第一种调用
crectangle r;//从类中建立一个新的对象r,这个r拥有上述类所有的成员变量和成员函数
r.init(w,h);//调用成员函数初始化成员变量
cout<<r.area()<<endl<<r.perimeter()<<endl;
//第二种调用
crectangle* p=&r;//对象指针p
p->init(w,h);//第二种调用方式
cout<<p->area()<<endl<<p->perimeter()<<endl;
//第三种调用
crectangle& rr=r;
rr.init(w,h);
cout<<rr.area()<<endl<<rr.perimeter()<<endl;
return 0;
}
24
20
24
20
24
20
也可以只在定义类的时候只申明函数,在外面写函数体
class rectangle{
public:
int w,h;
int area();
int perimeter();
void init(int a, int b)
};
void rectangle::init(int a,int b){w=a;h=b;}//成员函数1
int rectangle::area(){return w*h;}//成员函数2
int rectangle::perimeter(){return 2*(w+h);}//成员函数3
2.10类成员的可访问范围
在上一节定义类的时候,有个访问说明符,分类如下:
private:其成员只能被成员函数访问
public:可以在任何地方访问
protected:后面会提到,有关于基类/派生类
#include <iostream>
using namespace std;
class crectangle{
public:
int w,h;
void init(int a,int b){w=a;h=b;}
int area(){return w*h;}
void seta(int x){a=x};
private:
int a;
int perimeter(){return 2*(w+h);}
};
int main(){
int w=4,h=6;
crectangle r;
r.init(w,h);//ok,可以调用public成员
cout<<r.area()<<endl//ok,可以调用publlic成员
cout<<r.perimeter()<<endl;//编译错误,不能调用private成员
r.w=5;//ok,可以调用
r.a=6;//编译错误,不能调用private成员
r.seta(6);//ok,想修改private的值只能通过其public成员函数
return 0;
}