c++ 复习笔记
2014-03-17 11:46
211 查看
1. 头文件的结构
(1)头文件开头处的版权和版本声明。(2)预处理块。
(3)函数和类结构声明等。
2. 定义文件的结构
(1)定义文件开头处的版权和版本声明。(2)对一些头文件的引用。
(3)程序的实现体(包括数据和代码)。
3. 头文件的作用
(1)通过头文件来调用库功能。在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的。编译器会从库中提取相应的代码。(2)头文件能加强类型安全检查。如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的负担。
4. if语句与0值比较
1. 布尔变量与零值比较if (flag) // 表示flag为真
if (!flag) // 表示flag为假
2. 整型变量与零值比较
if (value == 0)
if (value != 0)
3. 指针变量与零值比较
if (p == NULL) // p与NULL显式比较,强调p是指针变量
if (p != NULL)
5. 常量
1. 一种标识符,它的值在运行期间恒定不变。C语言用 #define来定义常量(称为宏常量)。C++ 语言除了 #define外还可以用const来定义常量(称为const常量),c++完全可以用const代替#define。
2. const与#define比较
(1) const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。(2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试
3. const修饰函数
可以修饰函数入参,出参和函数体。const char* getString(const string& str) const;
第一个:修饰函数返回值,该返回值的内容不能被修改。
第二个:修饰函数入参,该参数在函数内部不能被修改。
第三个:修饰函数体,表明该函数不能修改该类的成员变量。
6. 指针与引用比较
(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。(2)不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)。
(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
7. 内存
1. 内存分配方式
(1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
4. 常见的内存错误及其对策
u内存分配未成功,却使用了它。编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。如果是用malloc或new来申请内存,应该用if(p==NULL) 或if(p!=NULL)进行防错处理。
u内存分配虽然成功,但是尚未初始化就引用它。
犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。
内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有。所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。
u内存分配成功并且已经初始化,但操作越过了内存的边界。
例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for循环语句中,循环次数很容易搞错,导致数组操作越界。
u忘记了释放内存,造成内存泄露。
含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽。
动态内存的申请与释放必须配对,程序中malloc与free的使用次数一定要相同,否则肯定有错误(new/delete同理)。
u释放了内存却继续使用它。
有三种情况:
(1)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。
(2)函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。
(3)使用free或delete释放了内存后,没有将指针设置为NULL。导致产生“野指针”。
8. 多态
1. 函数重载
(1)相同的范围(在同一个类中);(2)函数名字相同;
(3)参数不同;
(4)virtual关键字可有可无。
2. 函数覆盖
(1)不同的范围(分别位于派生类与基类);(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual关键字。
3. 函数隐藏
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
4. C++多态的实现
1. 多态性可以简单的概括为“一个接口,多种方法”。程序运行时才决定调用的函数。2. 多态的目的则是为了接口重用。
3. C++支持两种多态性:编译时多态性,运行时多态性。
a、编译时多态性:通过重载函数实现
b、运行时多态性:通过虚函数实现。
多态:多态分为两类:静态多态性和动态多态性,以前学过的函数重载和运算符重载实现的多态性属于静态多态性,在
程序编译时系统就能决定调用哪个函数,因此静态多态性又称为编译时的多态性。静态多态性是通过函数的重载实现的。
动态多态性是在程序运行过程中才动态地确定操作所针对的对象。它又称运行时的多态性。动态多态性是通过虚函数实现的。
9. 类的构造函数,析构函数,赋值函数
对于任意一个类A,C++编译器将自动为A产生四个缺省的函数,如:A(void); // 缺省的无参数构造函数
A(const A &a); // 缺省的拷贝构造函数
~A(void); // 缺省的析构函数
A & operate =(const A &a); // 缺省的赋值函数
注意:若类中有指针,且是动态申请(new出来的),则需要自己手动写上述缺省函数。
一般把基类的析构函数声明为virtual,若不是,则在如下情况会发生错误:
Class A{};
Class B : public A {};// B中有new的指针成员
A* a = new B();
Delete a;// B的析构函数未调用,导致错误。
10. 面向对象编程
所谓面向过程的编程思想,就是分析解决问题的步骤,将这些步骤用一个个函数实现,最后一个个调用。所谓面向对象的编程思想,就是将任何事物都看成一个对象,对象有各种属性(attribute)和行为(behavior),在解决问题的过程中,将系统分解,使之模块化。
面向对象的程序设计有四个主要特点:抽象、封装、继承、多态。
抽象(abstract):就是将不同对象的共性归纳、集中。在C++中,类是对象的抽象,对象是类的实例(instance)。
封装(encapsulation):就是将对象的内部实现和外部行为分隔开来,人们通过接口来进行外部控制,而不用关心内部细节。
继承(inheritance):在一个已存在的类的基础上建立一个新的类,新的类具有它所继承的类的全部特性,且可以增加一些新的特性。继承可以说是面向对象的程序设计最重要的特点。它实现了软件的可重用性(reuseability)。
多态(polymorphism):当向不同的对象发送同一消息时,不同的对象在接收到消息后会产生不同的行为。即,每个对象可以用自己的方法去响应共同的消息。函数的重载就是多态一个很好的例子。
11. 大小端问题
1.1定义:
大端格式:数据的高字节存放在低地址中,而数据的低字节则存放在高地址中。小端格式:数据的高字节存放在地址在中,而数据的低字节则存放在低地址中。
1.2 内存分布:
32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:内存地址 | 0x4000 | 0x4001 | 0x4002 | 0x4003 |
存放内容 | 0x78 | 0x56 | 0x34 | 0x12 |
内存地址 | 0x4000 | 0x4001 | 0x4002 | 0x4003 |
存放内容 | 0x12 | 0x34 | 0x56 | 0x78 |
1.3验证大端小端
请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1int checkCPU( )
{
{
union w
{
Int a;
char b;
} c;
c.a = 1;
return(c.b ==1);
}
}
或
short cc = 0x1234;
char *p = (char*)&cc;
若*p等于0x34则是大端,反之则是小端。
12. 函数入栈
在大端中:先入函数的参数,顺序从右到左,然后是函数内变量。
栈地址顺序为高地址到低地址。
int myprint(int a = 0,int b = 0,int c = 0,int d = 0)
{
int e = 0;
int f = 0;
cout<<"a-->"<<&a<<endl;
cout<<"b-->"<<&b<<endl;
cout<<"c-->"<<&c<<endl;
cout<<"d-->"<<&d<<endl;
cout<<endl;
cout<<"e-->"<<&e<<endl;
cout<<"f-->"<<&f<<endl;
return 0;
}
// 输出
a-->0022F9D0
b-->0022F9D4
c-->0022F9D8
d-->0022F9DC
e-->0022F9BC
f-->0022F9B0
函数返回参数存储在一个临时存储区(也在栈上),所以下面的代码:
string str(“abce”);
return str;
可以优化成如下代码:
return string(“abce”);
该对象会直接在外部的临时存储区上创建,省去了在函数内部中的构造和析构,提升效率。
13. C/C++程序内存分布
1. 栈区:有系统自动分配和释放,用于存放函数的参数,局部变量值,在内存中是一块连续的存储区,有低地址向高地址延伸。2. 堆区:有程序员分配和释放,若不释放则结束程序时可能有操作系统释放回收,旗存储区在内存中是不连续的,分配类型类似与链式。
3. 静态区:又称全局区,程序结束后又系统释放,用于存放全局变量值和静态变量,初始化的全局变量和静态变量在一看区域,为初始化的则存储在相邻的区域。
4. 文字常量区:程序结束后由系统释放,用户存储常量和字符常量。
5. 程序代码区:存放函数体的二进制代码。
14. New/malloc delete/free 区别
相同点:都可以动态申请,释放内存。不同点:
1. New是c++中的操作符,malloc则是标准库函数。
2. New不止申请内存,还调用类的构造函数。
3. 内存泄漏了对于new和malloc都可以检查出来,而new则可以指明到那个文件那一行。
相关文章推荐
- C/C++知识点复习笔记18/3/7
- effective C++第六章 复习笔记
- C++复习笔记
- C++代码复习笔记:第三章
- C/C++知识点复习笔记18/2/28
- 复习C++ 走一遍基础 留下笔记----{三}
- c/c++复习笔记--001
- C++复习笔记——初始化列表顺序
- C++复习笔记
- c /c++复习笔记 第四天
- C/C++知识点复习笔记18/3/5
- C/C++知识点复习笔记18/3/6
- 数据结构复习笔记:C++程序设计(一)
- 复习C++ 走一遍基础 留下笔记----{四}
- C++复习笔记01
- c++复习笔记(动态数组)
- C++静态成员复习笔记
- c/c++ 复习笔记 第五天
- C++要点复习笔记
- c /c++复习笔记 第三天