1、什么是函数原型、声明和定义
2、数组和指针的区别
3、怎样理解a+++++b
4、详细解释const和volatile, 一个变量可以同时被说明为const和volatile吗?
5、什么是左值、右值?
1)函数定义是告诉编译器,"遇到这个函数的调用时应该这样做..."
函数声明是告诉编译器,"这个函数存在了,请不要装作不知道。"
函数原型是告诉编译器,"想进入这个函数的大门需要按照我指明的这种方式来敲门,不然就是敲错门了。"
所谓声明,是用来将一个object,function,class或template的型别告诉编译器,声明式并不带有细节信息。
所谓定义,是用来将细节信息提供给编译器。对object而言,其定义式是编译器为他配置内存的地点,对function或function template而言,其定义是提供函数本体。对class或class template而言,其定义式必须给出该class或template所有members.
所谓函数原型包括关于参数类型和返回值的全部信息。C++要求必须写出函数原型。
说明一个变量意味着向编译程序描述变量的类型,但并不为变量分配存储空间。定义一个变量意味着在说明变量的同时还要为变量分配存储空间。
2.以下选自网上:/* extn.c */
char s[] = "Hello world.";
__________________________________
/* main.c */
extern char *s; /* XXX */
int main()
{
s[0] = 'A';
return 0;
}
__________________________________
$ gcc -c main.c extn.c
$ gcc -o main main.o extn.o
$ ./main
Segmentation fault (core dumped)
这是 C 中容易犯的一个经典错误。正确的应该是在 main.c 中声明 s 为
extern char s[];
把修改前后的 main.c 分别 gcc -S 编译成汇编,一比较就清楚了:
$ diff main.s.old main.s
12,13c12
< movl s, %eax
< movb $65, (%eax)
---
> movb $65, s
可见:错误版本中 s 声明为指针后,执行时先要取到 s 这个符号本身的地址,
从该地址处取出 s 代表的地址放到 eax 中,然后往这个地址存 'A',但是,s
在 extn.o 中作为数组存放在 .text 段中,这个符号的地址就是 extn.o 中那
个数组的地址,该处存放的东西是 "Hello world.",取一个指针出来就是四个
字节'H', 'e', 'l', 'l'代表的 long,在 little-endian 平台上它是
0x6c6c6548,再往这个地址存字符 'A',就 core dump 了。而在正确版本中,
已经知道它是数组了,就只会往 s 这个符号本身的地址处存放了。这便是数组
与指针的区别。
总结:访问指针时,先要找到指针变量本身的地址,从该地址处再取到存放的指
针值,然后再对指针指向的对象进行访问,是间接访问。访问数组则是先找数组
变量符号代表的地址,对这个地址指向的对象进行访问,是直接访问。
C语言的声明的优先级规则如下:
---------------------------------------------------------------
A 声明从它的名字开始读取,然后按照优先级顺序依次读取
B 优先级从高到低依次是:
B.1 声明中被括号括起来的那一部分
B.2 后缀操作符【圆括号()表示这是一个函数,方括号[]表
示这是一个数组】
B.3 前缀操作符【*表示这是一个指向......的指针】
C 如果const或volatile关键字的后面紧跟说明符【如int、long等】,
那么它作用于类型说明符。在其他情况下,const和【或】volatile
关键字作用于它左边紧邻的指针星号。
----------------------------------------------------------------
下面看一个例子:
char * const *(*next)();
-------- ------------------------------------------------------
运用规则 解释
-------- ------------------------------------------------------
A 首先,看变量名"next",并注意到它直接被括号括住
B.1 先把括号里面的东西看成一个整体,得出"next是一个指向......
的指针"
B 然后考虑括号外面的东西,在星号前缀和括号后缀之间做出选择
B.2 B.2规则告诉我们优先级较高的是右边的函数括号,所以得出"next
是一个函数指针,指向一个返回......的函数"
B.3 然后,处理前缀"*",得出指针所指的内容
C 最后,把"char* const"解释为指向字符的常量指针
什么是引用
引用是个别名,当建立引用时,程序用另一个变量或对象的名字初始化它。从那时起,引用作为目标的别名而使用,对引用的改动实际就是对目标的改动。引用不是值,不占存储空间,声明引用时,目标的存储状态不会改变。所以既然定义的概念有具体分配空间的含义,那么引用只有声明,没有定义。C++没有提供访问引用本身地址的方法,因为它与指针或其他变量的地址不同,它没有任何意义。
抽象类
一个不能有实例对象的类,就是抽象类,这样的类唯一的用途是被继承。一个抽象类至少具有一个纯虚函数。
虚拟继承
纯虚函数的需要性
1. 多态性是核心概念,它通过虚函数实现。
2. 纯虚函数引伸出抽象类,后者主要是向层次结构中的各成员定义接口。或向其它类提供基类,其他类从基类继承和实现接口。
3. 动态关联是指将:
基类中的虚函数与派生类中虚函数的实现相联系,联系的钮带是基类指针。
4. 实现多态性的前提是:
------系统中已存在基类与派生类(一般是作为基类的对象)
------派生类功能的实现是通过调用基类的虚函数,进而由编译器实现具体的派生类的功能
5. 虚函数的形式是:前面加有VIRTUAL,没参数,不返回值,的常量函数。
6. 纯虚函数是初始化为0的虚函数。
7. 多态性是指:通过继承相关但又不同的类,他们的对象能够对同一个函数的调用作出不同的响应。
8. 多态的扩展性表现为:软件可用与接受消息的对象类型无关的方式编写。
9. 派生类对象的多态操作能力是通过引用基类的指针实现的。
10.派生类对象的基类指针是一个或多个啊,指向派生类对象的基类指针是实例化了吧,因此才会有多个,也才能形成指针数组。
1.了解C++是如何在内部实现多态,虚拟函数和动态关联的目的是:能在最合适的时候选择使用多态.
2.特殊的数据结构:
-----多态能在运行时实现是依靠编译器建立的特殊需要的数据结构
-----执行程序只有利用了这些特殊的数据结构才能实现虚拟函数
-----执行程序也只有利用了这些特殊的数据结构才能实现与多态相关的动态关联.
3.虚函数的正确实现依靠的是:虚函数表(那个特殊的数据结构)
4.纯虚函数:即没有实现方法的虚函数.
1. 与动态关联相关的概念:
----虚函数表,函数指针,数组,类的虚函数,,属类的对象的虚函数版本
----带有虚函数类的每一个对象都包含一指向该类虚函数表的指针
-----虚函数调用VS对应类的虚函数版本
-----每一个包含虚函数的类都有一个虚函数表
-----虚函数表实现为包含函数指针的数组
-----虚函数版本是虚函数在类的对象中的实现,是具体化了的虚函数。
2. 虚函数调用流程的第二次阅读:
----建立特殊的数据结构
----编译器利用此数据结构执行虚函数,实现多态的动态关联
----虚函数表让执行程序选择每次执行(调用)虚函数时正确的实现方法,而虚函数表本身实现为包含函数指针的数组。
----抽象类的两种解释方法:纯虚函数,与虚函数表的0指针。
3. 多态是通过复杂的数据结构实现的,涉及三层指针
---------虚函数表中的函数指针
---------虚函数表本身的指针(放在对象前面,将其与对象(由带虚函数的类实例化而来的对象)相连接)
---------指向被虚函数调用的对象的指针。
1. 虚函数,多态,动态关联是一组相联系的概念。
2. 抽象类唯一的用途是为其它类提供合适的基类,其它类从它这里继承和实现接口,抽象类或抽象基类因为含义太广泛而定义不出实在的对象
3. 有了虚函数与多态性,程序员要做是处理普遍性而编译器则来处理特殊性
4. 与虚函数和多态性相关的动态关联为ISV(独立软件商)提供了盈利空间,ISV可开发能处理专用的类出售,软件开发者买这个类以便使用它的派生类来为自己的产品服务。
1. 基类的纯虚函数无具体的实现。
2. 派生类构造函数参数的个数多于其基类的构造函数,其共同的部分是由传递给基类构造函数参数而生成实现。
3. 类成员的构成:
------构造和析构函数
------派生类构造函数参数,其特殊的部分多半是纯虚函数的实现而设置
------而构造函数的参数而设置的函数
------私有部分:为构造函数的参数而设置的数据成员
4. 纯虚函数:无参数,无返回值,前面加有VIRTUAL的函数,且设为0的函数。
5. 多态性可通过:
基类引用 或 基类指针
调用虚函数来完成。
1. 实现与接口两种继承的结构是不一样的:
------实现继承通常是并联的形式,派生类之间无关系
------接口继承通常是串联的形式,派生类之间仍有层次的继承关系。
2. 并联继承在高层实现一些功能,串联继承是在具体的类中实现一些功能。
3. 两种方式中基类指针或引用的使用是一样的,是多态性成立的基本技巧。
const关键字实在是神通广大。在类的外面,它可以用于全局或名字空间常量,以及静态对象(某一文件或程序块范围内的局部对象)。在类的内部,它可以用于静态和非静态成员。对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const,还有,两者都不指定为const:
char *p = "hello"; //非const指针, //非const数据
const char *p = "hello"; //非const指针,//const数据
char *const p = "hello"; //const指针, 非const数据
const char * const p = "hello"; //const指针,const数据