01 面向对象程序设计基础

第一章 从C到C++

1.1引用

1概念:

某个变量的引用,等价于这个变量,相当于这个变量的一个别名

写法:类型名&引用名= 某变量名

1
2
3
4
int a=2;
int &b=a;
cout<<b<<endl;//b引用了a,b的类型是 int&
return 0;

上述程序输出为2

  • 注:改变引用变量的值,原变量也会改变,因为引用变量仅仅是一个别名
1
2
3
4
5
int a=2;
int &b=a;
b=8;//修改引用变量的值
cout<<a<<endl;//原变量的值也被修改
return 0;

上述程序输出为8

2 引用的要求:
  • 定义引用时,一定要将其初始化成引用某个变量

  • 初始化后,他就一直引用该变量,不会再引用其他变量

  • 引用只能引用变量,不能引用常量和表达式。

1
2
3
4
5
int a=2,c=9;
int &b=a;
b=c;//仅仅表示把c的值赋给b,即把c的值赋给a,b并没有引用
cout<<a<<endl;
return 0;
3 引用作为函数参数

可以在函数内部修改参数的值

相当于指针,但是不用像指针一样麻烦地每次都写*

用于交换a,c的程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
void swap(int &a,int &b)
{
a^=b;
b^=a;
a^=b;
}
signed main()
{
int a=2,c=9;
swap(a,c);
cout<<a<<" "<<c<<endl;
return 0;
}

上述程序输出为9 2

4 引用作为函数的返回值
1
2
3
4
5
6
7
8
9
10
int& setvalue()
{
return n;
}
signed main()
{
setvalue()=40;
cout<<n<<endl;
return 0;
}

上述程序输出为40

5 常引用

概念:

const int& r=nr的类型为const int &

不能通过常引用去修改其引用的内容(但内容本身可以修改)

1
2
3
4
5
int n=100;
const int &r=n;
n=0;//OK
r=9;//编译错误
return 0;

注:const int&int &是 不同类型

1.2 内联函数和重载函数

1 内联函数

作用:减少函数调用的开销

  • 编译器处理对内联函数的调用语句时,是将整个函数的代码 插入 到调用语句处,而不会产生调用函数的语句。

在函数定义前面加inline关键字,即可定义内联函数

1
2
3
4
5
6
7
8
9
10
inline int MAX(int a,int b)
{
return a>b?a:b;
}
signed main()
{
int a=9,b=8;
cout<<MAX(a,b)<<endl;
return 0;
}
2 函数重载

一个或多个函数,名字相同,然而参数个数或参数类型不相同,这叫做函数的重载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int MAX(int a,int b)//(1)
{
return a>b?a:b;
}
double MAX(double a,double b)//(2)
{
return a>b?a:b;
}
int MAX(int a,int b,int c)//(3)
{
int d=a>b?a:b;
return d>c?d:c;
}
signed main()
{
int a=9,b=8,c=12;
//2 int
//3 int
cout<<MAX(a,b)<<endl;
cout<<MAX(a,b,c)<<endl;
//2 double 调用(2)
double r=3.4,g=2.5;
cout<<MAX(r,g)<<endl;
//generate two-meaning error 二义性错误
double t=9.9;
cout<<MAX(t,b)<<endl;
//generate two-meaning error 二义性错误
cout<<MAX(3,2.4)<<endl;
return 0;
}

函数重载使得函数命名变得简单。编译器根据调用语句的中的实参的个数和类型判断应
该调用哪个函数。

3 函数的缺省值

定义函数的时候可以让最右边的连续若干个参数有缺省值,那么调用函数的时候,若相应位置不写参数,参数就是缺省值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int func(int a,int b=2,int c=3)
{

return a+b+c;
}
signed main()
{
int a=9,b=8,c=12;
cout<<func(a)<<endl;//等价于func(9,2,3)输出14
cout<<func(a,b)<<endl;//等价于func(9,8,3)输出20
cout<<func(a,b,c)<<endl;//等价于func(9,8,12)输出29
cout << func(a, , c) << endl; //不行,只能最右边的连续若干个参数缺省
return 0;
}

1.3 const关键字

  • 不可通过常量指针修改其指向的内容
1
2
3
4
5
6
7
8
9
signed main()
{
int a=8,b;
const int *p=&a;
a=5;//correct
*p=0;//error:表达式必须是可修改的左值
p = &b; // correct 常量指针的指向可以变化
return 0;
}
  • 不能把常量指针赋值给非常量指针,反过来可以:因为const int *类型的初始化的时候默认指向的值不可修改
1
2
3
4
5
6
7
8
9
signed main()
{
int a=8,b=9,*p2=&b;
const int *p=&a;
p=p2;
p2 = p; // 不能将 const int*类型的值分配到 int 类型的实体
p2=(int*)p;//correct
return 0;
}
  • 函数参数为常量指针时,可避免函数内部不小心改变参数指针所指地方的内容
1
2
3
4
5
void MyPrintf( const char * p )
{
strcpy( p,"this"); //编译出错
printf("%s",p); //ok
}

1.4 动态内存分配

1 分配一个变量

P = new T;
T是任意类型名,P是类型为T * 的指针。

动态分配出一片大小为 sizeof(T)字节的内存空间,并且将该内存空间的起始地址赋值给P。比如:

1
2
3
4
5
6
7
8
9
signed main()
{
int *p;
p = new int;
*p = 0;
cout << *p << endl;
delete p;
return 0;
}
2 分配一个数组

P = new T[N];

T :任意类型名
P :类型为T * 的指针
N :要分配的数组元素的个数,可以是整型表达式
动态分配出一片大小为 sizeof(T)*N字节的内存空间,并且将该内存空间的起始地址赋值给P。

1
2
3
4
5
6
7
8
9
signed main()
{
int *p;
p=new int[20];
p[5]=0;
cout<<p[5]<<endl;
delete []p;
return 0;
}
3 delete运算符释放动态分配的内存

用“new”动态分配的内存空间,一定要用“delete”运算符进行释放

句法:delete 指针;//该指针必须指向new出来的空间

1
2
3
4
int * p = new int;
* p = 5;
delete p;
delete p; //导致异常,一片空间不能被delete多次

用“delete”释放动态分配的数组,要加“[]”
句法:delete [ ] 指针;//该指针必须指向new出来的数组

1
2
3
int * p = new int[20];
p[0] = 1;
delete [] p;