C/C++学习笔记汇总

^函数重载的匹配:

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

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

***异常

^

推荐阅读更多精彩内容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 8,283评论 1 52
  • 题目类型 a.C++与C差异(1-18) 1.C和C++中struct有什么区别? C没有Protection行为...
    阿面a阅读 7,187评论 0 10
  • 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; ...
    朱森阅读 2,878评论 3 44
  • 一.例子1.下载Quartz File:quartz.properties: 2.Quartz作业: 3.Quar...
    眼袋大大阅读 638评论 0 6
  • 那只狗在长安街待了好几天了。 他的整个身子灰溜溜的,身上原本的淡黄色的细毛就像被蘸进了刷墙用的水泥里又在地上滚了几...
    豆蔻年华Jay阅读 254评论 0 5
  • 翻开一页 似乎看到了梦中的木棉 又好像看到了世界 在手中 我带不走这一片静谧 只好席地而坐 把这一刹那安宁 装进心...
    夫子闲云孙宁富阅读 102评论 0 0
  • 这是昨天丁老师在晚上点评时说的一句话,而我认为这是目前我们儿童美术教育要突破的一个点。 昨天听各位专家老师的课,都...
    暖暖滴文子阅读 240评论 0 0
  • 一直以来,女性是弱者的代名词。即便在今天,无论在中国,还是在发达民主国家美国,女性在社会中,职场上依然受着传统的道...
    西风5阅读 212评论 1 2