^函数重载的匹配:

          当函数名被重载后,函数的匹配过程:首先寻找能精确匹配的函数,如果未能精确匹配,则尝试找一个可以模糊匹配的函数。

1)精确匹配:参数个数相同,类型相同。

2)模糊匹配:参数个数相同,类型不同,但支持隐式转换。

^参数默认值

1)具有默认值的参数必须要放在后面。

2)当函数声明与定义分开时,应把默认写在声明里,不能写在定义里。          

    void show(int x,int y,int z=1);  //默认值加在函数声明里              

    int main()             

 {            

          ...          

    }      

void show(int x,int y,int z)      //函数定义之处不能加默认值   

   {        

...     

 }

 ^内联函数:

在函数前加一个inline关键字,该函数称为内联函数。

^函数的递归调用:

1)将高阶问题转化为低阶相同问题。

2)必须设置终止条件,避免无限制递归。

3)可以替换为非递归算法,改善循环语法。

4)控制递归深度。

***指针^内存地址的表示:  a在内存中对应4个字节;            

  unsigned int a=0xA0A0A0A0;    //a内存中的值:A0 A0 A0 A0            

  a = 0xB1B1B1B1;              //a内存中的值:B1 B1 B1 B1  变量地址是一个整数,可以用操作符&来取得。

例如:      inta = 0;  

                double b = 0;      

                printf("%08X\n"&a);          //把地址按十六进制来打印    

                printf("%08X\n"&b);          /*把址按照十六进制来打印^指针的概念:    XXX* 表示XXX型变量的地址。可以为char,int,double等,这种带*的类型叫做指针类型。*/

  指针(Pointer)意思是Point to an address.例如:         

   int a = 1;  

  int *p = &a;                //定义了一个int*型变量p,其值为a的地址。

1)指针变量也是变量。

2)不同类型的指针,不能互相赋值。

3)指针是一个整数类型。    在用printf打印时,通常使用的格式符为%p,p代表pointer。

4)*位置比较随意。

5)同类型指针可以混合定义。

^星号操作  “*”可以用来修改内存值,用在指针变量上可以直接读写内存的值。星号操作是一种按地址访问的技术,只要知道了这块内存的地址,就可以直接修改这块内存的值。

1)只有指针类型才支持星号操作。              int addrress = 0x12345678;        *addr = 0                      //编译错误!只有指针才支持星号操作

2)其他指针类型的用法是一样的。

3)区分星号的上下文^指针与数组    数组在内存中就相当于一串紧密排列的变量,数组名代表的就是这一块内存的首地址。

^指针加减法^指针与数组的转换

1)p指向arr的任意一个值;              p = arr+3;                      //指向arr[3]      p = &arr[3];                    //指向arr[3]

2)给数组元素赋值;              arr[3] = 10;      或:      *(arr+3) = 10;      或:      int* p=arr+3;      *p = 10;

3)把p当成数组使用              int* p = &arr[1];              //p指向arr[1]      p[0] = 0xAA;                    //p[0]:自p开始的第0号元素      p[1] = 0xBB;                  //p[1]:即arr[2]

4)长度为1的数组;    普通变量int a可以视为长度为1的数组来操作。            int a = 10;    int* p = &a;    p[1] = 11;              //长度为1的数组5)

数组的遍历;

方法一:            int arr[4] = {1,2,3,4};    for(int i = 0;i<4;i++)    {        printf("%d\n",arr[i]);    }

方法二:用指针遍历,注意终止条件为P来访问对象的成员,例如:            p->id = 20141011;                //使用->访问对象成员    strcpy(p->phone,"15928682083");  //使用->访问对象成员    也可以使用(*p).id,但是不常用。

3)做为函数参数    和基本类型一样,结构体也可以作为函数参数

4)做为函数返回值

5)作为结构体成员^结构体的特殊写法    结构体定义允许放在函数内部,这么定义的类型只能在函数内可见。由于struct语法的初衷是要定义一个呗多处使用的自定义类型,正常情况下应该定义在函数体之外。

^结构体的命名    结构体命名:“数字、字母、下划线的组合,不能以字母开头”。其次,命名要有意义,一个好的名字应该直接反映其意义。下面有两种常见的格式:

1)纯小写,以下划线分开每个单词,例如good_job,large_buffer.

2)每个单词以大写开头,在C++中推荐使用这种风格,结构体内成员变量,通常是小写,并以下划线分割每个单词。

^传值与传地址    在传输参数时,如果传入的是一个对象的值,叫“传值”方式,如果传入的是一个对象的地址,叫做“传地址”方式。

1)传值方式

2)传地址方式:

***联合体

1)概念:

***动态内存分配

1)动态内存分配malloc申请内存    应用程序调用malloc函数可以申请一块指定大小的内存,函数原型为:            #includevoid* malloc(int size);    参数:size:内存空间的大小,以字节为单位。    返回值:申请出来的这块内存首地址。    用法示例:            char* p = (char*) malloc(84);  //申请一块84字节的空间    内存管理空间并不关心这一块内存的用途,所以malloc的返回值是void* ,仅表示内存的地址。应用程序可以用来存储任何类型的数据。例如申请一块100个int型数据,示例如下            int* p = (int*)malloc(100*4);  //申请100*4内存    for(int i = 0;i<100;i++)    {        p[i] = i * i;              //使用这块内存    }  

  free释放内存          

  #includevoid free(void* ptr);    在使用完毕后,应用程序应当调用free函数来释放内存,当内存交给内存管理器,传入的参数就是先前用malloc得到的指针.

2)内存管理器与堆    内存管理器(MM)的职责就是提供内存服务,它管理的区域称为堆,malloc得到的内存的位置是在堆区。原则:尽可能少的申请内存,尽可能快的释放。    

堆内部管理:MM对借出的内存块进行标识:            (p0,n0)(p1,n1)(p2,n2)...    它内部已经保证任意两块不会交叠,不会把一块内存同时借给两个应用程序使用。    内存泄漏

3)对象的生命期    对象的分类:全局对象,局部对象,动态对象。    ①当定义一个变量时:Object a;,则变量a对应了一个对象,类型为Object,地址为&a,如果这个变量是全局变量,则a称为全局对象,如果这个变量是局部变量,则a称为局部对象。    当用malloc动态申请内存时:Object* p = (Object*)malloc(sizeof(Object));此时p指向了一个对象。该对象内存使动态分配的,称为动态对象。    一个对象总是对应了一块内存,对象的值就是内存里的数据。    对象生命期:全局对象生命期是永恒的,只有程序退出时才失效;局部对象生命期是临时的在超出作用域后对象立即消失;动态对象生命期是动态的,在malloc时生命生效,在free时失效。

4)常见问题  用malloc申请的内存,用完以后要用free释放。  不适用malloc得到的内存不能用free释放。  及时归还,再借不难。  不能只free一部分。  程序退出时,malloc内存都会自动释放归还给MM。***链表^概念    把若干对象用指针串联起来,形成一个链状数据结构,称为“链表”。            struct Student    {        int id; char name[16]; Student* naxt;    }    其中添加一个成员变量next,用于指向下一个对象。

^链表的构造

1)先准备好四个对象            Student ss[4]=    {        {201501,"John",0}, {201502,"Jennifer",0}, {201503,"Anxi",0}, {201504,"Unnamed",0}    };

2)把这个对象“串“起来            ss[0].next = &ss[0];    ss[1].next = &ss[1];    ss[2].next = &ss[2];    ss[3].next = 0;    一个链表构造完毕。

3)头节点与末节点    当若干个对象被串起来以后,只要知道第一个对象,就可以访问链表中的每一个对象。把链表中每个对象,称为“节点”。第一个节点也叫“头节点”

4)链表头的作用:可以用于代表整个链表:Student* stu_list = &ss[0];

^有头链表

1)概念:用一个固定的头节点来指代整个链表,所有的对象挂在这个头节点下面,而头节点本身并不包含有效数据。2)定义一个有头链表    只需要定义一个对象作为其节点,将成员next初始化为NULL。            Student m_head = {0,"head",NULL};    或者写:            Student m_head = {0};    当有对象加入时,直接加在后面就可以,当他的next为NULL时表示该节点没有数据节点(链表长度为0)。

3)添加一个节点            void add(Student* obj)    {          obj->next = m_head.next;  m_head.next = obj->next;    }    创建一个对象,然后调用add函数插入列表中。            Student* obj = (Student*)malloc(sizeof(Student));    obj->id = 12;    strcpy(obj->name,"X");    add(obj);                            上面的add函数直接把新的节点插在最前面,也可以把节点插到末尾,代码如下:            void add(Student* obj)    {          Student* p = &m_head;  while(p->next)        p = p->next;          p->next = obj;  obj->next = NULL;    }

4)有头链表的遍历    在遍历时,有头链表的头节点由于不含有数据,是不参与遍历的实际遍历时,只访问链表中的数据节点。            void show_all()    {                Student* p = m_head.next;  while(p)  {        printf("ID: %d,name: %s\n",p->id,p->name);p = p->next;  }    }

5)按顺序插入节点    先遍历链表,并比较ID的值,找到目标位置,并记录前一个节点为pre,找到位置后,把新的节点直接挂在pre后面。            obj->next = next->next;    pre->next = obj;6)查找和删除节点***引用^定义    在类型之后加上一个&符号,该变量即为引用类型。    引用只相当于对象的一个别名。

^与指针的区别    引用在定义时必须初始化关联到一个对象,例如:Object* p = NULL;      //允许在定义的时候不指向任何一个对象,如果在定义一个引用时不初始化,则编译器就会报错,例如:Object a;  Object& r;  //语法错,定义引用时必须初始化!    引用与某个对象绑定,中途无法解绑,而指针的使用较为灵活,一个指针可以先指向对象a,再指向对象b,引用可以视为功能受限的指针。

^简单的例子^作为函数参数   

 引用类型可以作为函数的参数,可以达到与指针相同的效果。

^做为函数返回值   

 引用也可以作为函数返回值,指针作为返回值是把某个对象的地址返回,^const引用    const引用限定被引用的对象为只读的,不能修改的对象,常用作函数的参数。***字符串^字符串的三种形式

1)字符数组    当以char型数组来存放字符串时,数组名时字符串的首地址。

2)动态字符串    可以动态分配一块内存,然后在这块内存里存放一串字符。也就是说,这个字符串对象在堆上。

3)字符串常量    在代码中用双引号包括,包含0..N个字符,称为字符串常量。^字符串常量多行表示    当一个字符串常量要表达的内容特别长时,在单独一行代码中可能书写不下,可以用两种方法分成多行。   

 第一种:使用双引号将多段文本连接起来,两个字符串之间可以被空格分开,不影响最终效果。例如              const char* str = "hello" "world";相当于:const char* str = "helloworld";   

 第二种:在末尾添加一个反斜线,例如:              const char* poem = "江山定\n\      风雨遮前路,冰火伴我行.\n\      一度波澜惊,而今江山定.\n\;

^字符串与与普通数据    

字符串是以char*表示的,它指向了字符串的首地址。实际上仅当这块内存用于存储字符串的时候,才把它称作字符串,如果只是把它用于存储一些普通数据则不能把它称作字符串。

^字符串的遍历    

遍历字符串指的是从前往后访问每一个字符,有两种方法:索引遍历和指针遍历。都需要检测结束符'\0'来判断是否结束。

^字符串长度    

是指从第一个字符开始,一直到末尾的结束符,中间的有效字符的个数,长度不包含末尾的'\0'在内。^字符串复制    字符串的复制,是指将源字符串的每一个字符挨个复制到目标缓存区,最终保证目标缓冲区的字符串末尾有一个'\0'字符。    

注意事项:目标缓冲区要足够大;目标缓冲区保证以0结束。可以使用里的strcpy函数。   

 区分深拷贝和浅拷贝:例如              char* p1 = "hello,world";      char* p2 = p1;    这种简单的这振赋值,就叫做浅拷贝,一句话表示:两个指针指向了同一个字符串对象。              char* p2 = (char*)malloc(strlen(p1)+1);      strcpy(2,p1);    这段代码新申请一块相同大小的内存,然后把字符串内容复制到这块内存,那么这两个字符串对象(两块内存),他们的内容相同,这种拷贝叫做深拷贝,一句话表示:两个指针(p1,p2),分别指向两个字符串对象。

^字符串比较    

字符串也可以比较是否相等,以及大小关系,只有当所有字符全部相同时才认为两者相等。两个字符则是按它们的ASCII码值大小进行比较。   

 一般直接使用里的strcmp函数来比较两个字符串。strcmp(a,b),当相等时返回值为0,当ab时返回值为1.

^字符串插入与删除

1)删除字符

函数Erase用于字符串中某个字符的删除。

2)插入字符

函数Insert用于在源字符中插入一个字符。

^字符串分割

一个字符串由若干信息组成,每一段信息中间用分隔符分开,解析这个字符串,得到一段内容称之为字符串的分割。

^用数组还是指针

数组方式:

指针方式:

**标准C函数库

1)stdio.h

标准输入/输出函数

2)math.h

3)time.h

4)stdib.h

5)string.h

***文件操作

^认识文件

文件的作用是持久化存储数据。所谓持久化是指当关闭计算机电源后数据依然存在,再次打开计算机时,还可以重新加载显示这些数据。

^保存数据

使用ANSI C中的stdio.h里的相关数据来进行文件读写操作。步骤如下;

1)fopen:打开文件。

fopen函数用于打开文件,得到一个FILE*指针,该指针指代该文件,后续的fwrite/fclose等函数都需要传入这个文件指针。其原型为:

FILE fopen(const char *filename,const char *mode);

其中,filename:表示要打开的文件路径;mode:固定使用"wb"(w表示write,b表示binary);

用法示例:FILE* fp = fopen("c:/aaa.txt","wb");

if(fp == NULL)

{

printf("文件打开失败\n");

}

2)fwrite:写入数据。

当数据写入完毕,该文件不再被指针使用时,要及时调用fclose函数来关闭文件,其原型为:

int fclose(FILE* stream);

参数:stream就是前面fopen的返回值。用法示例:

fclose(p);

3)fclose:关闭文件。

fwrite用于向文件中写入数据,其原型为:

size_t fwrite(const void * buf,size_t size,size__t nelem,FILE * stream);

参数:stream就是前面fopen的返回值;buf:要写入的数据首地址;size:总是传1;nelem:数据长度。用法示例:

char buf[] = "hello";

fwrite(buf,1,5,fp);

^读取数据:就是把曾经写入的文件读取出来。

读取数据分为三步。

1)fopen打开文件

2)fread读取文件

fread函数原型为:

size_t fread(void * buf,size_t size,size_t nelem,FILE * stream);

参数:stream:前面fopen函数的返回值;buf:内存缓冲区,用于存储数据的内存位置;size:恒为1;nelem:最多读取多少字节;返回值:实际读取字节,如果

返回-1,则读取失败。用法示例:

char buf[128];

int n = fread(buf,1,128,fp);

如果文件中的数据不超过128字节,则返回值n就是实际的字节数。如果文件中的数据超过128字节,那这次操作只能读取128个字节。

3)fclose关闭文件

^数据的存储格式

第一种方式:

int x = 100;

int y = 100;

fwrite(&x,1,2,fp);

fwrite(&y,1,4,fp);

当以这种方式写入时,一共8个字节。可以用相应的代码,从文件中读取数据,并恢复x,y坐标。

int x,y;

fread(&x,1,4,fp);

fread(&y,1,4,fp);

第二种方式:

int x = 100;

int y = 200;

char buf[128];

sprintf*(buf,"x = %d,y = %d",x,y);

fwrite(buf,1,strlen(buf),fp);

这种方式把一个字符串“x = 100,y = 200”写入文件。

第三种方式:

int x = 123;

int y = 456;

char buf[128];

sprintf(buf,"%d%d",x,y);

fwrite(buf,1,strlen(buf),fp);

^存储格式:按字节存储

1)存储char类型

char ch = 12;

fwrite(&ch,1,1,fp);    //存

fread(&ch,1,1,fp);    //取

2)存储int类型

int n = 12;

fwrite(&n,1,sizeof(int),fp);    //存

fread(&n,1,sizeof(int),fp);      //取

3)存储double类型

double val = 123.456;

fwrite(&val,1,sizeof(val),fp);

fread(&val,1,sizeof(val),fp)

4)存储结构体类型

Object obj = {123};

fwrite(&obj,1,sizeof(obj),fp);

fread(&obj,1,sizeof(obj),fp);

5)存储字符串

char name[32] = "shaofa";

fwrite(name,1,32,fp);

fread(name,1,32,fp);

^存储格式:文本化存储

当数据量比较少时,可以把数据格式化为文本的形式来存储。

1)fprintf按行格式化写入

2)fgets按行读取

^文件的随机访问

在描述一个文件的可访问属性时,有两个术语。

顺序访问:不能跳跃。

随机访问:随意跳到一个位置访问。

1)fseek

2)文件位置指示器

3)随机访问实例

4)fseek的物理限制

5)文件被重复打开的情况

^文件打开模式

rb:读模式。当读一个文件使用。如果该文件不存在,则fopen返回NULL.

wb:写模式。再写一个文件时使用。

ab:附加模式。表示打开文件但不清空里面的内容。

***多文件项目及编译过程

^extern

1)extern声明全局函数

想要在main.cpp中调用其他.cpp中定义的函数,那么就必须在main.cpp里用extern声明这个函数,写法如下:

extern double get_area(double r);

以关键字extern修饰,在后面加上函数的原型,关键字extern不仅可以声明一个外部函数,还可以声明一个外部的全局变量。在声明全局时,关键字extern是

可以声明不写的。

2)extern声明全局变量

也可以在A.cpp里访问B.cpp里的全局变量,需要在A.cpp里用extern声明这个全局变量。

注意:在声明变量时不能加初始值;必须要在前面加上extern。

3)深入理解全局变量

extern的作用是通知编译器在本cpp中要用到某个符号,这个符号可能不在本cpp中定义,它表示在某个cpp文件中存在这么一个全局变量/函数,这个符号可以

再别的cpp中定义,亦可以在本cpp中定义

^多文件项目的生成

1)第一阶段:编译

编译,这一阶段是处理每个cpp文件,把cpp文件中的代码转换为中间文件(*.obj),可以再debug文件夹中找到这个中间文件,A.cpp->A.object,C.cpp->B.obj。

在变异过程中各个cpp文件都不区分顺序,谁先谁后都一样,只要声明了一个函数为extern,编译器就不检测是否真的存在这个符号。

2)第二阶段:链接

如果迁移阶段编译成功,则进行连接过程。此过程作用是将各个obj文件综合在一起,生成可执行程序。A.obj,B.obj,..->test.exe。

在连接阶段编译器会检测所有extern的福海是否真的存在。

3)用伪代码表示整个过程

可以用一段伪代码来表示编译器和连接过程,并非真正的c++代码,仅用于解释说明。

4)全量编译与增量编译

全量编译是将所有的cpp文件重新编译一下,“重新生成解决方案”就是全量编译。

增量编译是指对有改动的文件进行执行,当执行生成解决方案时执行的是增量编译,这也是大多数编译器的默认动作。

^头文件#include指令

头文件的后缀名一般为.h,相应的把.cpp文件叫做头文件。

1)为什么需要头文件

同一结构的定义要在不同的cpp里重复好几遍。如果不写,就是语法错误。由于每个cpp时独立编译的,在other.cpp中定义的结构类型对main.cpp没有任何

影响

2)使用头文件

吓死项目中新增一个头文件Objeect.h,然后把Object的类型定义写在里面,然后在需要它的cpp里加上#include"Object.h",这样就解决了前面所说的问题,需

要扩展Object结构时,只需要修改Object.h即可。头文件写法:后缀一般为.h;内容一般为几种:类型定义,extern函数声明,extern变量声明。

3)#include指令的原理

#include"Object"

其中,#include称为一条“预处理指令”。"Object.h"表示要包含的头文件的路径,以双引号包围。预处理过程是:编译器过程的作用是,编译器在处理每个cpp

之前,首先将文件里的所有预处理指令进行处理,形成一个中间文件,然后对这个中间文件进行编译。

4)头文件的重复包含问题

5)头文件里的内容

头文件里,一般放以下内容:

公用类型定义:如果一个类型要在多个cpp中使用,可以放在头文件里。

extern函数声明:extern变量声明。

嵌入包含其他文件。

^宏定义#define指令

所有已#开头的行,称为预处理指令。#define指令通常称为宏定义。两个用法:

1)#define的一个数值

使用#define可以起到定义一个“常量”的效果。

2)#define的一个算式

使用#define可以定义一个类似“函数”的东西,它不是函数。学这些东西只是为了能够看懂一些老旧的代码。

3)几个常见宏定义

NULL空指针:#define NULL 0

RAND_MAX的宏定义:见16章。

^条件编译指令

1)#if...#endif

2)#ifdef...#endif

#ifdef表示如果对应的宏有定义,则相应的代码被编译。可以使用#undef指令去定义。

#ifndef表示的意思和#ifdef恰好相反:当相应的宏不存在时,才编译相应的代码。

3)结局头文件重复包含的问题

通常要对头文件用条件编译指令对其进行保护,之后便可以对其重复包含了。

^main函数的参数和返回值

1)main函数的参数

2)main函数的返回值

^static的用法

1)static修饰变量

2)static修饰函数

***面向对象编程

^面向对象设计的过程

^实例演示

^封装

***类

^类和成员变量

^类和成员函数

^变量名字和覆盖

^命名规范

^类的封装

^类的分离式写法

^const对象与const函数

***构造与析构

^构造函数

^析构函数

^自动生成构造/析构函数

^默认构造函数

^混合使用两种初始化方式

^构造与析构的顺序

^分离式写法

^无名对象

^构造函数与类型转换

***动态创建对象

^回顾malloc/free

^用new和delete创建/销毁对象

^new/delete与malloc/free的区别

^为new指定初始化对象

^默认构造函数的必要性

^注意事项

***继承

^继承的概念

^访问修饰符protected

^成员函数重写

^虚拟继承

^虚函数virtual

^继承关系下的构造与析构

^多重继承

^继承函数与纯虚类

^以protected/private方式继承

***拷贝构造函数

^定义

^拷贝构造函数的调用

^默认拷贝构造函数

^定义拷贝构造函数

^深度拷贝

***静态成员

^static定义全局变量

^static定义全局函数

^与普通成员函数的区别

^static语法的特点

^实例

***朋友成员

^类的朋友

^friend的语法

^实例

***重载操作符

^算术操作符

^赋值操作符=

^自增操作符++与自减操作符--

^关系操作符

^逻辑操作符

^类型转换操作符()

^元素操作符

^输入/输出操作符>>与<<

^操作符new与delete

***内部类和名字空间

^内部类

^名字空间

**模板

^函数模板

^类模板

^模板参数

^实例

***标准函数库

^一般使用方法

^向量vector

^list

^string

^map

^stack

^queue

***异常

^