您的位置:首页 > 编程语言 > C语言/C++

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

而在Big-endian模式CPU内存中的存放方式则为:

内存地址

0x4000

0x4001

0x4002

0x4003

存放内容

0x12

0x34

0x56

0x78

1.3验证大端小端

请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1

int 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则可以指明到那个文件那一行。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: