引用的概念:相当于给某个变量起一个别名
//如何定义一个引用,并将其初始化
//引用的基本格式: 类型名 & 引用名 = 变量名
int a = 10;
int &b = a; //b引用a,b的数据类型是int &
某个变量和引用,等价于这个变量,相当于给该变量起了一个别名
int n = 7;
int &r = n;
r = 4;
cout<<r; //输出4
cout<<n; //输出4,因为r引用n,等价于n,给引用赋值,相当于给n这个变量赋值
n=5;
cout<<r; //输出5,变量改变了,其引用的值也变了
使用引用需要注意的点:
1、定义引用时一定要将其初始化成引用某个变量(不能引用常量和表达式)
2、初始化后,它就一直引用该变量,不会再引用别的变量了(从一而终)
double a = 4, b = 5;
double &r1 = a;
double &r2 = r1; //r2和r1都引用a
r2 = 10;
cout<<a<<endl; //输出10
r1 = b; //r1并不会去引用b,而是将b的值赋值给r1引用的对象
cout<<a<<endl; //输出5
在c语言中,交换两个变量值的函数,通过指针完成
void swap(int *a, int *b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
int a = 10, b = 20;
swap(&a, &b);
在c++中,通过引用就能完成,使代码更加简洁明了
void swap(int &a, int &b)
{
int tmp;
tmp = a;
a = b;
b = tmp;
}
int a = 10, b = 20;
swap(a,b); //a和b的值将会被交换
引用作为函数的返回值
int n = 4;
int &SetValue()
{
return n;
}
int main()
{
SetValue() = 40;
cout<<n; //输出40
return 0;
}
常引用:定义引用时,前面加上const关键字,即为“常引用”
int n;
const int &r = n; //r的数据类型是const int &
不能通过常引用去修改其引用的内容(并不意味着不能变量不能被修改,仍然可以通过变量名修改)
int n = 100;
const int &r = n;
r = 200; //编译报错
n = 300; //可以修改
注意:const int & 和 int & 是不同的类型!
int & 类型的引用,或者int型的变量,都可以用来初始化const int &类型的引用
const int 类型的常变量,和const int &类型的引用,不能用来初始化int &类型的引用,除非进行强制类型转换
const关键字的用法:用来定义常量
const int MAX = 20;
const char *NAME = "pig";
定义常量指针
//不可以通过常量指针去修改其指向的内容
int n, m;
const int *p = &n;
*p = 5; //会报错,无法通过指针修改指向的值
n = 4; //没毛病
p = &m; //没毛病
//不能把常量指针赋值给非常量指针,反之可以
const int *p1;
int *p2;
p1 = p2; //ok
p2 = p1; //报错
p2 = (int *)p1; //ok,可以进行强制类型转换
函数参数为常量指针时,可以避免函数内部不小心改变了参数指针所指向地方的内容
void myPrint(const char *p)
{
strcpy(p, "this"); //编译报错
printf("%s",p); //没问题
}
动态内存分配:用new运算符实现动态内存分配
第一种用法:分配一个变量
//动态分配一片大小为sizeof(int)字节的内存空间,并且将该内存空间的起始地址赋值给p
int *p;
p = new int;
*p = 10;
第二种用法:分配一个数组
//动态分配一片大小为10 * sizeof(int)字节的内存空间,并且将该内存空间的起始地址赋值给p
int *p;
p = new int[10];
使用delete运算符释放动态分配的内存
用“new”动态分配的内存空间,一定要用“delete”运算符进行释放
//delete 指针; //该指针必须指向new出来的空间
int *p = new int;
int *p1 = new int[10];
delete p;
delete []p1;
内联函数
- 函数调用是有时间开销的。如果函数本身只有几条语句,执行非常快,而且函数被反复执行很多次,相比之下,调用函数所产生的开销会显得比较大
- 为了减少函数调用的开销,引入了内联函数机制。编译器处理对内联函数的调用语句时,是将整个函数的代码插入到调用语句处,而不会产生调用函数的语句
在函数定义前面加 inline 关键字,即可定义内联函数
inline int max(int a, int b)
{
return a>b?a:b;
}
函数重载:一个或多个函数,名字相同,然而参数个数或者参数类型不相同,这叫做函数的重载
//以下三个函数是重载关系
int max(int a, int b);
int max(double a, double b);
int max(int a, int b, int c);
函数的重载使得函数命名变得简单,编译器根据调用语句的实参个数和类型判断应该调用哪个函数
函数的缺省参数
c++中,定义函数的时候可以让最右边的连续若干个参数有缺省值,那么调用函数的时候,相应位置不写参数,参数就是缺省值
void func(int a, int b=2, int c=3);
func(10); //等效于func(10,2,3)
func(10,20); //等效于func(10,20,3)
func(10, ,20) //编译出错,只能缺省最右边的连续若干个参数
函数参数可缺省的目的在于提高程序的可扩充性
如果某个写好的函数要添加新的参数,要添加新的参数,而原先那些调用该函数的语句,末尾需要使用新增的参数,那么为了避免对原来那些函数调用的语句的修改,就可以使用缺省参数。
结构化程序设计
- c语言使用结构化程序设计:程序 = 数据结构 + 算法
- 程序由全局变量以及众多的相互调用的函数组成
- 算法以函数的形式实现,用于对数据结构进行操作
结构化程序设计的不足:
- 结构化程序设计中,函数和其操作的数据结构,没有直观的联系
- 随着程序规模的增加,程序逐渐难以理解
- 没有“封装”和“隐藏”的概念,要访问某个数据结构中的某个变量,可以直接访问,当该变量的定义有改动时,就要把所有访问该变量的语句找出来修改,十分不利于程序的维护、扩充
- 难以查错,当某个数据结构的值不正确时,难以找出是哪个函数导致的
- 想要抽取一部分复杂的代码进行重用,是很困难的事情
面向对象的程序设计方法
- 将某类客观事物的共同特点(属性)归纳出来,形成一个数据结构(可以用多个变量描述事物的属性)
- 将这类事物所能进行的行为也归纳出来,形成一个个函数,这些函数可以用来操作数据结构,这叫做抽象
- 通过某种语法形式,将数据结构和操作该数据结构的函数捆绑在一起,形成一个类,从而使得数据结构和操作该数据结构的算法呈现出显而易见的紧密关系,这就是封装
- 面向对象程序设计的特点:抽象、封装、继承、多态
对象调用的三种方法:
class ABC //先定义一个类abc
{
int w,h;
}
ABC a; //定义一个对象a
ABC *p1 = &a; //定义一个指针指向对象a
ABC &p2 = a; //定义一个引用去引用对象a
//1 通过成员运算符.去调用
a.w = 10;
//2 通过指针调用
p1->w = 10;
//3 通过引用调用
p2.w = 10;