C++远征之离港篇
章节介绍:
每章小结:
- 第一章:大致讲了一下本章会讲的内容:引用vs指针、const vs #define(这个我在C里都没用过)、函数变得更强大、内存管理要小心之类的。
- 第二章:按视频所说,引用就像别名一样,所以不能只有引用而没有本体,而且对引用的操作与对变量直接操作完全一样。引用的声明方法:
类型标识符 & 引用名 = 目标变量名
,如果是指针的引用则是类型标识符 * & 引用名 = 目标变量名
。然而我并没有搞懂引用到底有什么好处。视频接下来举了一些例子,有一个是引用作为函数的参数:
这让我想起了当初用指针做两个数的调换的优势体现在两个数比较大的时候,明显有效率的多,看样子引用也可以做到,看上去还更简洁。还有就是,如果去掉上图中右边函数形参中的两个&的话,实参并不会被交换,因为实参和形参是单向的值传递(不怎么用函数都忘记了)。还有一个是结构体引用:结构体的定义多了一个typedef
,还以为是C++有所不同,保险还是查了一下(后来发现C的书上就有,完全没印象。。。),发现比我想象得复杂得多:(来自http://www.cnblogs.com/qyaizs/articles/2039101.html后面的看不大懂就不写了。)
1、 首先:注意在C和C++里不同
在C中定义一个结构体类型要用
typedef
typedef struct Student
{
int a;
}Stu;
于是在声明变量的时候就可:`Stu stu1;`(如果没有`typedef`就必须用`struct Student stu1;`来声明)这里的`Stu`实际上就是`struct Student`的别名。`Stu==struct Student`
另外这里也可以不写`Student`(于是也不能`struct Student stu1;`了,必须是`Stu stu1;`)
typedef struct
{
int a;
}Stu;
但在c++里很简单,直接
struct Student
{
int a;
};
于是就定义了结构体类型`Student`,声明变量时直接`Student stu2;`*(这倒是和C不一样)*
2.其次:
在c++中如果用
typedef
的话,又会造成区别:
struct Student
{
int a;
}stu1;//stu1是一个变量
=======================
typedef struct Student2
{
int a;
}stu2;//stu2是一个结构体类型=struct Student
使用时可以直接访问stu1.a 但是stu2则必须先stu2 s2;然后s2.a=10;(上面的第一点里C讲了一大堆感觉就是第二点讲的意思:加不加就是类型和变量的区别)
感觉得视频里讲的有点少,就加了一些自己找的东西(<http://www.cnblogs.com/Mr-xu/archive/2012/08/07/2626973.html>)。
一些说明:
例1:
int a; int &ra=a;
//定义引用ra,它是变量a的引用,即别名(1)&在此不是求地址运算,而是起标识作用。
(2)类型标识符是指目标变量的类型。
(3)声明引用时,必须同时对其进行初始化。
(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。 ra=1; 等价于 a=1;
(5)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&ra与&a相等。
(6)不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。
引用应用:
引用作为参数
引用的一个重要作用就是作为函数的参数。以前的C语言中函数参数传递是值传递,如果有大块数据作为参数传递的时候,采用的方案往往是指针,因为 这样可以避免将整块数据全部压栈,可以提高程序的效率。但是现在(C++中)又增加了一种同样有效率的选择(在某些特殊情况下又是必须的选择),就是引用。
例2:
void swap(int &p1, int &p2)//此处函数的形参p1, p2都是引用 { int p; p=p1; p1=p2; p2=p; }
为在程序中调用该函数,则相应的主调函数的调用点处,直接以变量作为实参进行调用即可(感觉有点绕不过来啊。。。),而不需要实参变量有任何的特殊要求。如:对应上面定义的swap函数,相应的主调函数可写为:
main( ) { int a,b; cin>>a>>b; //输入a,b两变量的值 swap(a,b); //直接以变量a和b作为实参调用swap函数 cout<<a<< ' ' <<b; //输出结果 }
上述程序运行时,如果输入数据10 20并回车后,则输出结果为20 10。
由例2可看出:
(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。(调用函数的时候偷偷给形参的引用初始化的意思吧)
(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给 形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效 率和所占空间都好。
(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用指针变量名的 形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
(接下来的常引用、引用作为返回值和引用和多态,分别涉及到了const和函数等超纲的东西,就先不写了。)
引用小结:
(1)在引用的使用中,单纯给某个变量取个别名是毫无意义的,引用的目的主要用于在函数参数传递中,解决大块数据或对象的传递效率和空间不如意的问题。
(2)引用与指针的区别是,指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。
为了记忆最后再来实际打一点(当然是调试过的,主要是单词老记错。。。)。
#include<stdlib.h>
#include<iostream>
using namespace std;
namespace exchange
{
void fun(int &a,int &b)
{
int c;
c=a;
a=b;
b=c;
}
}
typedef struct
{
int x;
int y;
}Coor;
int main()
{
using namespace exchange;
Coor C;
Coor & C1=C;
cin>>C.x>>C.y;
fun(C1.x,C1.y);
cout<<C.x<<" "<<C.y<<endl;
system("pause");
return 0;
}
- 第三章:C++语言const关键字。
感觉就是定义个常量而已(然而我一般都没用到常量),定义的一般格式就是const 数据类型 常量名=某个值;
(原来C里是#define 常量名 某个值
并没有分号,它是宏定义),定义指针常量的时候则有些不一样的地方:像const int *p
和int const *p
是等价的,而int const *p
和int * const p
则是不一样的。具体来说,定义为int const *p
时,可以对p进行赋值,而不可以对*p
进行赋值;定义为int * const p
时则可以对*p
赋值,而不可以对p赋值;如果是int const * const p
则两个都不能进行赋值。还有就是视频里提到了这两个代码:const int x=3;int *p=&x;
、int x=3;const int *p=&x;
,说是第一个的定义是错的,用一个指针变量指向常量会有风险,而第二个则可以,相当于用权限小的指向权限大的。最后视频还提到常引用的问题,感觉这个倒是有可能会用到,再和函数结合起来的话(但是形参定义成常引用的话,在函数里还能做什么操作啊)。 - 第四章:C++函数新亮点。
-
函数参数默认值。就是说在函数声明的时候(按视频说,定义的时候赋值的话有的编译器不接受)可以对参数赋值,而且要从右边开始,然后调用的时候无实参就用默认值,有实参就覆盖默认值。举个例子来说,有如下的代码:
#include<stdlib.h> #include<iostream> using namespace std; void fun(int i=10,int j=20,int k=30); int main() { fun(); fun(100); fun(100,200); fun(100,200,300); system("pause"); return 0; } void fun(int i,int j,int k) { cout<<i<<","<<j<<","<<k<<endl; }
-
运行结果:
这也便于理解默认值要从右边开始。
- 函数重载。就是说参数可辨(参数的类型和个数有差别)的函数可以使用同一个名称,调用函数的时候编译器会根据输入的参数来自行判断使用哪个函数(这倒是很方便)。
- 内联函数。定义一个内联函数只要在定义函数时加上内联函数的关键字inline
就好。内联函数的好处是效率高,具体是因为它省略了图中的②③两步:
但是使用内联函数有一定的条件:当函数逻辑简单且被循环调用时就很适合用内联函数来提高效率,但是递归函数无法使用内联,而且内联函数还是建议式的命令,最终由编译器决定,不适合的也会被当成一般函数来处理。
-
第五章:C++内存管理。
按视频所说,内存管理就是申请、归还内存资源。申请内存要用到运算符new
,归还内存要用到运算符delete
。具体来说,申请一个整型内存的代码是int *p=new int;
(int *p=new int (10);
则是申请的同时初始化为10),对应的释放内存只要delete p;
;还可以申请块内存,代码是int *p=new int [n];
(其中n表示申请的内存大小,感觉就是数组的长度),要注意的是释放块内存的代码是delete []p;
,不然的话就只有数组的第一个会被释放。这是内存管理的第一个注意事项,就是要一一对应,还包括(话说在C里面还是没用过):
第二点是内存的申请有可能会失败(像没有足够的内存给你申请时),要检查是否成功(具体就是指针是不是空指针)。第三点是,释放完内存后要把指针设置为空指针,这也是为了避免不必要的错误(据说释放两次会报错)。再来实际打一点代码:#include<stdlib.h> #include<iostream> using namespace std; int main() { int *p=new int[10]; if(p==NULL) { cout<<"内存申请失败"<<endl; system("pause"); return 0; } p[0]=10; p[1]=20; //没有加*耶 cout<<p[0]<<","<<p[1]<<endl; delete []p; p=NULL; system("pause"); return 0; }