C++学习笔记(一) 基础语法 —参考阿发你好

这里写图片描述
这里写图片描述
这里写图片描述
-###———————————————————————–


阿发你好第2章

输出指定宽度的数字

#include <stdio.h>
#include <stdlib.h>
int main(){
    printf("number is %d\n", 3);
    printf("number is %d\n", 33);
    printf("number is %d\n", 333);
    printf("number is %4d\n", 3);
    printf("number is %4d\n", 33);
    printf("number is %4d\n", 333);//表示输出都是4个自出长度
    printf("number is %04d\n", 3);
    printf("number is %04d\n", 33);
    printf("number is %04d\n", 333);//04d表示不足四位用0补齐
    system("pause");
}

这里写图片描述

#include "stdio.h"
int main(){
  printf("x=%lf\n",1.23424);
  return 0;

}

这里写图片描述

用变量表示小数
(我使用的IDE是kdevelop F8是编译 F9是运行)

#include "stdio.h"
int main(){
  double a=1.123;
  printf("x=%lf\n",a);
  return 0;
}

指定小数后的位数

#include "stdio.h"
int main(){
  double a=1.12334;
  printf("x=%.2lf\n",a);
  return 0;

}

这里写图片描述
输入一个长整型

#include "stdio.h"
int main(){
  scanf("%lf",&a);
  return 0;
}

变量的命名规则:必须是字母下划线数字的组合,可以用字母或者下划线开头,但是不能用数字开头
char 型变量能够表示-128~127之间的整数2^7=128最高为是符号位。
long :4字节 占32位,
printf 可以输出char short int 都可以

无符号类型

unsigned char 表示的范围0~255 就是i原来2^8-1 就是255
在调用无符号整数用%u来表示
%u 在用scanf的时候,只能用unsigned int 来接收,不能用unsigned char/short

#include "stdio.h"
int main(){
  unsigned int a=0;
  scanf("%u\n",&a);
  return 0;
}

浮点类型

用于小数的类型有两种:double,float 类型,统称为浮点型,需要表示高的精度类型应该用double ,当精度要求不高的时候,用float类型

float a= 3.14f//这里数字后面要加一个f
scanf("%f\n",a);

单步调试

通常我们总是用F5进行编译,原来不知道有F7也可以进行生成解决方案,用F10进行单步运行。用F9进行打断点

第一次调用数组
#include <iostream>
using namespace std;
int main()
{
    int data[4] = { 1, 2, 3, 4 };
    int total=0;
    for (int i = 0; i <4; ++i)
    {
        total += data[i];
    }
    cout << total<<endl;
    return 0;
    system("puase");
}

如何描述一下错误:
前提条件:当我输入…….的时候,
预期结果:可以正常编译
实际结果:不是我预期的效果(或者是无法正常编译)

对于测试人员:将问题进行复现,只有将问题复现才能解决

指针变量如何看他的地址:

这里写图片描述
在地址的框写入p 然后按回车就可以自动把p的地址写到这个里面

阿发你好 第3章

2016/11/13

十进制、十六进制的表示方法

int a=123;//表示的10进制
int a=0x123;//表示的16进制 将这个16进制的数化成10进制的话 等于3*16^0+2*16^1+1*16^2

内存的表示

内存使用在存储计算机中的二进制数
内存的基本单元是字节,一个字节由8位二进制来表示
这里写图片描述
一个字节能够表示的范围是0~255
每个内存单元都是有地址的,地址表示的范围;00000000~FFFFFFFF;

int a=10;//10就是一个字面常量,a就是一个变量

变量与内存

char 型变量:占1个字节
short型变量:占2个字节
int 型变量:占4个字节 4 个字节(4*8=32位 那么最大值:2^33-1)
float 类型占4个字节
double类型占8个字节

//用sizeof(int)可以返回出这个类型所占的字节
#include <iostream>
using namespace std;
int main()
{
    cout << sizeof(int) << endl;
    system("pause");
}

这里写图片描述
然后求一个变量的所占的字节

#include <iostream>
using namespace std;
int main()
{
    int a = 10;
    cout << sizeof(a) << endl;
    system("pause");
}

这里写图片描述

#include <iostream>
using namespace std;
int main()
{
    int a = 10;
    cout << &a<< endl;
    system("pause");
}

这里写图片描述
一个字节能够表示的范围;00~FF
在C语言中,我们用sizeof来测量一个变量或者类型的大小

const 限制符

如果一个东西,被const修饰之后,那么他就变成一个只读的常量

字面值常量

int a=1;//1 就是字面值常量,a是一个变量。

阿发你好 第4章 数组

数组的初始化

 char arr[4]={1,2,3,4};

注意事项:
1、char arr[3];//可以在定义的时候,不初始化初值
2、
char arr[3]={1};//可以只初始化部分初值
3、
char arr[]={1,2};//可以只有数值没有长度`
4、char arr[5]={0};

访问数组的元素

char arr[3];
arr[0]=1;

用sizeof(arr)来访问数组的长度

char arr[3];
sizeof(arr);

多维数组

int arr[1][2];
arr[0][0]=1;

对一个二唯数组进行初始化

int a[3][4]=
{
    {11,12,13},
    {21,22,23},
    {31,32,33},
    {41,42,43},
};

阿发你好第5章 字符与字符串数组

在printf的时候我们用%c来表示字符串

char ch=65;
printf("%c",ch);

输出的结果是A
字符常量

char ch='a';
printf("%d",ch);

字符数组

1、一般的初始化方法

char str[3]={'i','l','\0'};

2、特殊的初始化方法

char str[3]="il";//最后默认添加了/0来表示数组的终止

输出字符串
用printf向程序输出字符串的话,使用格式符%s

char str[2]="I";
printf("%s\n",str);

可以使用gets来获取一个字符串

char burf[2];
gets (burf);gets()的括号里面是数组的名字
printf("%s/n",burf);

在编写字符串的时候一定要有/0来便是终止。

char str[]={'I',0};
printf("%s",str);

在输出的时候,碰到0就截止
转义字符
1、\n 换行
2、\用于输出目录
3、\t输出制表符
4、输出引号printf(“\”asdf”\”);


阿发你好第6章 表达式与操作符

算数表达式
赋值表达式
关系表达式
条件表达式

expr1?expr2:expr3;
如果条件expr1为真的时候就执行expr2,否则执行expr3
这个是一个表达式

举个例子:

printf("%c\n",score>60'T':'F');

我们都是用这条语句来简化计算的
逻辑表达式
!表示非 && 用来表示与 || 用来表示 或

逗号表达式

expr1,expr2,expr3

是从逗号分开的一组表示式,从左到右依次计算,最后一个表达式为整个表示式的值

int nnn=(1,2,3);//赋值的最后结果是nnn=3

自增自减运算符

阿发你好第7章 语句

if语句

if (expr)
    statement1
else (expr)
    staement2

switch语句

switch(expr)
{
case option_1:  break;
case option_2:  break;
case option_3:  break;
case option_4:  break;
}

switch 语句的注意事项:
1、switch和case的选项必须都是整型
case的选项的值必须常量

for语句

for (expr1;expr2;expr3)
    {statement;}

在for语句中,如果存在break语句,可以直接跳出for循环
continue语句如果存在for语句大括号内,
当continue语句被执行的时候,表示结束本次循环,直接进入下一次循环


while语句

while语句也用于是实现循环,他的基本表达形式:

while (expr)//当expr为1的时候,进入循环体
    statement

t

//将编写的条件内置
int main(){
  int i=0;
  while(1){
  if(i>100)break;//当i=101的时候,直接跳出循环
  i++;
}
}
int day=2;
switch(day)
{
case 1: printf("1");
break;//如果没有break的话,程序会从匹配的入口依次往下执行
case 2:printf("2");
break;
default: printf("3");
break;
}

do while 语句

do
{
statement
}while(expr1)

首先执行statement,当while(expr)为真的的时候,返回循环,为假,跳出循环


阿发你好 第8章 函数

全局变量与局部变量

#include<stdio.h>
int arr[3]={0,1,2};
int main(){
printf("%d",arr[2]);
return 0;
}

函数的声明

#include<stdio.h>
int find_q(int a);//函数声明部分
int main(){
    return 0;
}
int find_q(int a){
statement;
}

参数的默认值
1、具有默认值的参数必须在列的最后面
2、当函数的声明与定义分来的时候,不能写在定义里面

也就是说,在声明里面添加默认值,不能写在定义里面
#include<stdio.h>
void funtion(int a, int b, int c=1);
int main(){
  return 0;
}
void function(int a.int b, int c)
{
return 0;
}

内联函数 inline关键字

inline int max(int a, int b)
{
  return 0;
}
int main()
{
 int a=max(1,2);
}

函数的递归

阿发你好 第9章 指针

指针类型用于表示内存地址
星号操作符号,直接用于指针变量上,直接用来读写内存值的
下面是代码`#include
using namespace std;
int main()
{
int a = 0;
int *p = &a;
*p = 0x11;//修改内存里的内容
*p = 0x12;
a=0x14;
system(“pause”);
}


char* 表示char类型的地址
short* 表示short类型的地址
int* 表示的是int型的地址
float* 表示float型地址
double* 表示double型地址
int* p=&a

使用p这个变量来存储a这个变量的地址。

这里写图片描述

这里写图片描述

&以为向下走,也就是去取地址
*相当于往上走,去地址中的内容
*p 可以用修改地址中的内容,他的效果和a修改的效果是一样
p则表示的a所在位置的地址。

指针当使用printf的时候使用%p进行输出。

星号 跟前面的char* 这里一定要区分开,他们是没有任何联系的
*p=0x1231231312;
使用*p来修改和内存的数值。

#include <iostream>
using namespace std;
int main()
{
    int arr[4] = { 1, 2, 3, 4 };
    system("pause");
}

查看一个数组的地址,直接输入数组名就可以了
这里写图片描述
数组名称,本身就是一个内存的地址

指针的类型:
1、chat* :表示一个char型变量的地址
2、short* : 表示一个short型变量的地址
3、int* : 表示一个int型变量的地址
4、float* : 表示一个float型变量的地址
5、double * :表示一个double型变量的地址

在打印的地址的时候用%p来表示指针类型

读写操作

写操作
*p=0x1231231;
度操作
int b=*p;
分析下面的代码
int main()
{
    int a=0;
    int* p=&a;//说明p指向a的地址
    *p =0x1231231;//修改p指向内存地址的位置


}

数组本质上就是一段指针。

#include <iostream>
using namespace std;
int main()
{
    int arr[4] = { 1, 2, 3, 4 };
    int *p1 = arr;
    int *p2 = &arr[0];//这两句是等价的

    system("pause");
}

这里写图片描述
这种写法值得注意:int *p=arr;

指针的移动

#include <iostream>
using namespace std;
int main()
{
    int arr[4] = { 1, 2, 3, 4 };
    int *p1 = arr;
    int *p2 = &arr[0];//这两句是等价的
    p1 += 1;
    cout << *p1;
    system("pause");
}

这里写图片描述
总结:等式左边表示声明他的类型

#include <iostream>
using namespace std;
int main()
{
    int arr[4] = { 1, 2, 3, 4 };
    int *p = &arr[1];
    p[0] = 0xAA;
    p[1] = 0xBB;
    return 0;
    system("pause");
}

也就是说通过p修改了数组arr中的元素
实例说明P修改arr中的元素

#include <iostream>
using namespace std;
int main()
{
    int arr[4] = { 1, 2, 3, 4 };
    int *p = arr;
    for (int i = 0; i < 4; ++i)
    {
        p[i] += p[i];
        cout << p[i]<< endl;
    }
    for (int ii = 0; ii < 4;++ii)
    {
        cout << arr[ii] << endl;
    }
    system("pause");
}

这里写图片描述

把指针作为一个函数的参数来使用

#include <iostream>
using namespace std;
void test(int *p);
int main()
{

    int a =0x11;
    test(&a);
    system("pause");
}
void test(int *p)
{
     *p=0x12;
    cout << "change the value:" << *p << endl;

}

在这当中相当于添加了依据 int *p=&a

*相当于上一次级
&相当下一个级别

这里写图片描述

const int *p=&a;//也就是说明 p指针只能指向a,或者的表示只能读,不能写
int *const p=&a;//这就是说p不能被修改,而*p可以被修改

写一个在源码当中很常见的一个例子

#include <stdio.h>
int main()
{
int a=1;
int b=2;
int c=0;
void add(&a, &b,&c);

printf("c=%d",c);
return 0 ;
}
void add(int *a, int *b, int *c)
{
int result=0;
result=*a+*b;
 *c=result;

}

数组作为函数的参数。

这个例子也是很重要的

int agv(int *p ,int len)
{
    int sum=0;
    for(int i=0;i<len;i++)
    {

        sum+=p[i];


    }   


return sum/len
}
int main()
{

int arr[]={0,1,2,3};
int ret;
ret=avg(arr,4)
}

指针作为函数的返回值

int number=1;
 int *get()
 {

return &number;
}

const指针
const int *p=&a 也即是说 p只能指向a的地址。

const指针常常用于限定函数的参数

int test(const int *p ,int len)
{


}

也即是这个函数只能进行内存,而不能改变他的指向

指针只能指向变量或者数组
野指针就是指的那个没有指向变量和数组的指针
**空指针**iint *p=0;

#include <iostream>
using namespace std;
int main()
{

    int *p = 0;
    if (p)
    {
        cout << "p不是一个空指针" << endl;
    }
    system("pause");
}

这里写图片描述
这里写图片描述说明输出结果就时一个空指针
也就是说,当你传入一个空指针的时候,一个函数是有办法判断他是不是一个空指针的

数组作为函数的参数

int arg(int *p, int len)
{
   int sum=0;
}
int main()
{
  int arr[]={0,1,2,3};
  int n;
  n=avg(arr,3)
}
方法1int avg(int *p,int len)
方法2int avg(int p[],int len)

当数组作为函数的参数的时候的引用。

#include<stdio.h>
int avg(int*p,int len )
{
    int sum = 0;
    for (int i = 0; i < len; i++)
    {
        sum += p[i];
    }
    return sum ;
}
int main(){
    int arr[4] = { 1, 2, 3, 4 };
    int rect = 0;
    rect = avg(arr, 4);
    printf("%d", rect);
    int a;
    getchar();
    //scanf("%d\n",&a);
    return 0;
}

指针类型作为函数的返回值

#include <iostream>
#include <stdlib.h>
int number = 1;
int *get()
{
    return &number;
}
int main()
{
    int *p = get();
    //*p = 12;
    printf("%d\n", p);
    printf("%d\n", *p);
    system("pause");
    return 0;
}

const指针
在普通的指针类型上加上关键字const修饰,叫做const指针类型
const的作用封禁了信号操作里的写内存功能

int test(const int *p, int len)
{
}

const指针常用于限定函数参数
这个参数限定为const类型,用于显式地指定:“该函数是输入参数,在函数里只是读取内存的值,而不会修改这个内存的值”

void * 型指针
void * 型指针仅仅表示一个内存地址,它不指明内存里的存放何种类型的数据。
void * 型指针不支持加减法

空指针的使用

int *p=0;//空指针
if(p)//用if语句来判断指针类型是否为空值
{
    printf("%d\n",*p);
}

指针的使用规范:
1、杜绝野指针
当一个指针初始化的时候,要么将其置为空指针,要么将其指向为某个变量的地址
2、防止数组越界
3、目标内存是否已经生效
用一个指针指向一个变量,但是要注意这个变量的作用域。

二重指针

指针也是变量,凡是变量就是有地址 那就就是进行指针的多重嵌套。

int **p=&q;
#include<stdio.h>
int number=0;
void set(int **p )
{
    *p=&number;
}
int main()
{
    int *p=NULL;
    set(&p);
    *p=23;
    return 0;
}

注意这个地方二维数组的定义就可以了。
二维数组

顺次打印这个行列式的值

#include <stdlib.h>
#include <iostream>
using namespace std;
void test(int data[][4], int rows)//打印这个行列式
{
    for (int i = 0; i < rows;i++)
    {
        for (int j = 0; j < 4;j++)
        {
            printf("%d\n", data[i][j]);
        }
    }
}
int main()
{
    int arr[2][4] =
    {
        { 1, 2, 3, 4 },
        { 11, 12, 13, 14 }, 
    };
    test(arr, 2);
        system("pause");
    return 0;
}

二维数组和指针之间的变换

int aa[6][4];
int (* row0)[4]=aa;//row0:指向第0行
int (* row1)[4]=aa;//row1:指向第1行
(*row0)[0]=1;//第一种方法:使用指针的方式,访问第0行第0列;
row0[0][1]=2;
row[0][0]=3;

阿发你好 第10章 结构体

这里写图片描述

结构体的定义和初始化

这里写图片描述
定义结构体数组
这里写图片描述

结构体的赋值:

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
struct Highschool
{
    char name;
    int num;
    int  phone;

};
int main()
{
    Highschool a={ 'c', 1, 2 };
    Highschool b = a;//将a中的数据赋值给b
    cout << b.name << endl;
    system("pause");
}

这里写图片描述

结构体访问

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
struct Highschool
{
    char name;
    int num;
    int  phone;

};
int main()
{
    Highschool a={ 'c', 1, 2 };
    Highschool *p = &a;
    cout << p->name << endl;//使用->箭头来访问结构体的成员变量(这是常用写法)
    cout<<(*p).id<<endl;
    system("pause");
}

结构体作为函数的参数

这里写图片描述

结构体作为函数的返回值

这里写图片描述

匿名struct

using namespace std;
struct 
{
    char name;
    int num;
    int  phone;

} info;
int main()
{
    info.num = 1;
    cout << info.num << endl;
    system("pause");
}

这里写图片描述
注意事项:
大括号只能用于初始化、不能直接用在赋值上

strcpy 函数用于复制字符串,在使用之前需要向其中添加#include<string.h>

结构体的内存视图

struct 
{
    int id;
    char name[8];
}obj

一个obj占据12个字节,一个char是占一个字节,那么一个id是4个字节,一个name[8]占据8个字节

结构体指针

contact *p=&a;//结构体加上相应的*号就相当于对应类型的指针类型
p->id=123;//对于结构体指针类型用->来访问对象的数据成员

将结构体作为函数的参数

作为传值方式

void test(contact a)
{
printf("id is %d/n name is %s",a.id,a.name)
}

作为传地址的方式

void test(contact *a)
{
printf("id is %d/n name is %s/n",a->id, a->name);
}

作为函数的返回值

和基本的结构体一样、结构体类型也可以作为函数返回值的类型

contact create(int id)
{
    contact a;
    a.id=id;
    return a;
} 

结构体作为结构体的成员函数

成员函数的类型也可以是结构体类型

struct color
{
    unsigned char r;
    unsigned char g;
    unsigned char b;
};
struct object
{
    int x,y;
    color rgb
};
object obj=
{
    1,2,
    {0xFF,0X00,0X00}


}

结构体紧凑的表示方式

struct object
{
int x;
int y;
}a,*p;//在这里直接初始化两个对象,一个结构体类型的a,和一个结构体指针类型的*p

传值和传地址

传值-是说明:在传递参数的时候,如果传入对象是一个值,那么就叫做传值
传地址-是说明:在传递参数的时候,如果传入的对象是一个地址,那么就叫做传地址

位字段 bit-filed

在结构体当中有一种特殊的成员,叫做位字段。他是在变量名后面加冒号,然后标明该字段到底占用了多少个位
Struct object
{
Unsigned char a : 1;
Unsigned char b : 2;
};
表明该字段到底是占了1位,如果是1位的话,则只能取值[0,1],占了2位,取值范围[0~3],占了三位[0~7]

联合体union

Union 在实际的工程当中,一般不会使用这个语法,学这个语法的目的,只是为了读懂一种老旧的C语言代码

Union somedata
{
Unsigned int a;
Unsigned char b;
Char c[10];
}
联合体是共享同一片内存区域的

联合体的大小就是所有成员里体积最大的那个成员的大小

阿发你好 第12章 动态分配内存

在操作系统中,有一个内存管理器(MM),
在C语言中,内存的管理和释放用malloc/free这两个函数来调用
malloc申请内存,申请的内存在堆区(heap)

申请一块84字节的空间,
这个函数定义是

void * malloc(int size)

然后后面使用char*进行改变

char *p=(char*) malloc(84);

总之这个最后记住
char p=(char ) malloc(84)
malloc申请的内存的位置在堆上。

在C++当中 . 和 -> 之间有什么去呗

struct A
{
   int a;
   int b;
};
A *point = malloc(sizeof(struct A));
point->a = 1;
A object;
object.a = 1;

malloc是在堆中实例化,那么内存需要你自己释放

.是在栈中进行实例化对象,内存不需要你自己释放,由计算机进行管理
这样理解的的话,

然后上面对上卖弄的结果提实例化对象
A *point =malloc(sizeof(struct A));
point ->a =1
然后是里面的成员进行赋值
另外一种方面就是 A object 这是个对栈中进行实例化对象,然后对这个对象进行赋值。
这个方式记得在最后一定要是去释放内存
free

然后在这里为说明一个列子进行分析

struct car
{
    char maker[32];
    int pice;
};
struct Citizen
{
    char  name[32];
    int deposite;
    car *car;//使用这个
}

Citeizen davidhan={"davidhan",12,NULL};
void buy(Citeizen *own)
{
//从堆中进行实例化对象的时候,首先要分配内存
car *car=(car *)malloc(sizeof(car));
strcpy(car->maker,"davidwife");//将车主名字给了david的老婆
car->price=11;
own->car=car;
own->depoite-=car->price;//计算存款
}

void discard(Citzen * owner)
{
    free(onwer->car);//释放内存
    own-car=NULL;
}

如果是在实例化对象的时候,没有星号,就是从栈里面进行实例化对象,那么这样的话,就是使用 . 进行对里面的成员进行赋值。如果是有星号的话,就是从堆里面进行实例化对象。

一个系统当中只有一个内存管理器,系统中所有的进程都向同一个内存管理器来申请内存。
内存泄漏:就是程序长时间运行,如果不能及时清理内存,内存积累的就会越来越多,导致系统没有内存空间可是使用,这种现象,就是内存泄漏。
对象的分类:
1、全局对象
2、局部对象
3、动态对象
一个对象就对应了一块内存,对象的值就是内存里的数据

阿发你好 第13章 链表

链表是一种数据的组织方式,将若干个对象用指针传链起来。
也即是说在定义一个结构体的时候,多定义一个指针,这个指针的作用就是将所哟的数据连接起来了。

链表的构造

struct student
{
    int id;
    char name[16];
    student *next;//添加一个指针用于指向下一个对象
}

初始化一个结构体数组

student ss[4]=
{
    {123,"123",0},
    {123,"123",1},
    {123,"123",2}
}

将这三个对象串联起来,就是通过,上一个的这个指针的位置,然后去下一个位置的地址。

ss[0].next=&ss[1];
ss[1].next=&ss[2];
ss[2].next=0;

前面虽然将他们串起来,但是并不知道头和尾是什么样子,头结点和末节点

//当若干个对象别串起来的时候,只需要添加第一个对象,就可以访问到链表中的每一个对象
student *p=&ss[0];
while(p)
{
    printf("%d,%s",p->id,p->name)
    p=p->next;//指向下一个对象
}

链表头的作用:链表头可以用于代表整个链表,其实就是可以遍历整个链表。

有头链表

有一个固定的头节点来指代整个链表,所有的对象都挂在这个头节点下面,而头节点本身不包含有效数据
使用头链表的目的是,简化链表的操作使之容易实现
头部的节点称为头节点,头节点是不包含任何数据的,后面的节点称为数据节点
无头链表是没有固定的头结点,当节点被删除的时候,在插入节点比较困难。
使用头链表的目的是为了简化链表的操作。
定义一个有头节点
在原来的结构体中,只需要将一个对象作为节点,将他的成员next初始化为NULL
添加一个节点

void add(student *obj)
{
    obj->next=m_head.next;
    m_head.next=obj;
}

有头链表的遍历
按顺序插入链表
查找和删除节点


阿发你好 第14章 引用

struct object
{
    int value;
};
int main()
{
    object a = {1};
    object &r =a;//r相当于a的一个别名
}

记得在结构体后面要有分号
引用和指针最大的区别就是 引用在定义的时候必须关联一个对象。而指针不用。

#include <iostream>
using namespace std;

struct object
{

    int value;
};

int main() {
    object a;
    a.value=1;
    object &r=a;
    cout  << r.value<<endl;
    return 0;
}

指针和引用的最大的区别在于:引用在定义的时候,必须关联到一个对象。而指针的话,不必在初始化的时候就指向一个对象,可以指向一个空指针

#include <iostream>
using namespace std;

struct object
{

    int value;
};

int main() {
    object a;
    a.value=1;
    object &r=a;
    cout  << r.value<<endl;
    object *p=&a;
    cout<<p->value<<endl;
    return 0;
}

上面的代码输出都是 1 1 。

int a=123;
int &r=a;

如果输出r 那么r就是23 &z这个福相说明a的别名是r a和r是等价的。*号叫作指向地址的符号

object a={1};
object &r=a;
object *p=&r;

作为函数的参数

void test(object &r)
{
    r.value=1;
}
int main()
{
    object a={1};
    test(a);
    return 0;
}

档一个数据类型是指针的时候,他的赋值就必须用 -> 这个,当一个数是引用类型的时候,那么他的赋值,就是用这个

void test(object *p)
{
    p->value=1;
}

虽然有点幼稚,但是过了好久才把* 和 & 这两个符号给区分开。

#include <iostream>
using namespace std;

struct object
{

    int value;
};
int  test(object *p);
int  test2(object &r);
int main() {
    object a;
    a.value=1;
    cout << test(&a) << endl;
    cout << test2(a) << endl;
    return 0;
}
int  test(object *p)
{
    p->value=2;
    return p->value;
}
int  test2(object &r)
{
    r.value=2;
    return r.value;
}

这里写图片描述
如果用*p=2;这说明的是 p指向的2的地址。

作为函数的返回值

object the_object={123};
object *test()
{
return &the_object;
}
int main()
{
    object *p=test();
    p->value=456;
    return 0;
}

作为函数返回值
引用也可以作为函数的返回值,当你看到 object * test() 的时候,基本上函数的返回值 return &ab
这下面是我仿照写的
这里写图片描述

#include <iostream>
using namespace std;

struct word{
     int value;
};
word people={1};//实例化并且进行初始化
word *eat()
{
    return &people;
}
int main()
{
 word *p=eat();//这个地方相当于*p=&people 因为他的返回值类型是 &people
 cout<<p->value<<endl;
 return 0;
}

同样也可以用引用实现
这里写图片描述

#include <iostream>
using namespace std;

struct word{
     int value;
};
word people={1};
word &eat()
{
    return people;
}
int main()
{
    word &a=eat();
    a.value=2;
    cout<<a.value<<endl;
    return 0;
}

阿发你好 第15章

C风格字符串
字符串通常以3种形式存在:字符数组、动态字符串常量和字符串常量

字符串数组

char name[]="davidhan";

动态字符串常量

char *str=(char *)malloc(12);从堆中申请内存。

字符串常量

100 类型是int
100u 类型是 unsigned int 
3.1415926 类型是double 
3.14f 类型是float

并不是所有的char*都叫做字符串,实际上,只有当这块内存用于存储字符串的时候,我们才称他为字符串,如果只是把它用于存储于一些普通的数据,则不能不他称为字符串。

查看一个字符串的长度
使用string.h当中的strlen函数来计算

#include <iostream>
#include <string.h>
using namespace std;

int main()
{
   char src[]="davidhan";
   int n=strlen(src);
   cout<<n<<endl;
    return 0;
}

遍历字符串

遍历字符串,有两种方法:1、索引遍历;2、指针遍历。

利用最后是str[i]的最后的索引值为’/0’直接就退出循环。

#include <iostream>
#include <string.h>
using namespace std;
int show_string(const char *str)
{

    for(int i=0;str[i];i++)
    {
        cout<<str[i]<<endl;
    }
    return 0;
}

int main()
{
   char src[]="davidhan";
   show_string(src);//其实这个地方拿本质就是 char *p=src 然后使用p[i]索引这个里面的数据
    return 0;
}

指针是数组之间的关系

首先是我定义了一个数组 src[]="davidhan";
然后使用char *p=src
那么 p[0]就是d这个字母

然后是利用指针进行遍历

#include <iostream>
#include <string.h>
using namespace std;
int show_string(const char *str)
{
    const char *p=str; 这里就相当于一个指针
    while(*p)
    {
        char ch = *p+*;然后依次对这个指针进行遍历,并且对这个对这个数值进行输出。
        cout<<ch<<endl;
    }
    return 0;
}
int main()
{
   char src[]="davidhan";
   show_string(src);//这个地方相当于 char*p=src 然后使用另外的一个指针指向他 char *a=p,说明和p作用是相投的
    return 0;
}

从本质上看就跟这个代码是一样的

#include <iostream>
using namespace std;

int main() {
    int a=1;
    int* p=&a;
    int* b=p;//这个时候b和p的代价是相同的。
    cout<<*b<<endl;
    return 0;
}

然后输出就是davidhan
后来我自己又写了一个版本
这里写图片描述

#include <iostream>
using namespace std;
/*
这个函数为了对字符串数组进行遍历
*/
int bianli(const char *p)
{
    while(*p)
    {
        cout<<*p<<endl;
        *p++;
    }
    return 0;
}
int main()
{
    char a[]="davidhan";
    bianli(a);
    return 0;
}

字符串的复制

#include<string.h>
函数当中的strcpy()
#include <iostream>
#include <string.h>
using namespace std;

int main()
{
   char src[]="davidhan";
   char srccpy[128];
   strcpy(srccpy,src);

    return 0;
}

字符串的比较

在<string.h>中、用strcmp函数来比较这两个字符串
strcmp(a,b);如果a和b相等,那么返回0,当a>b的时候范围1a<b的时候返回-1

字符串的插入与删除
字符串的分割
使用数组还是指针?
1、数组的方式
优点:1、安全,不必维护指针
2、操作简单
缺点:1、浪费空间
2、不适用较长的字符串

指针之间的简单赋值,就是浅拷贝。也也即是b和p都指向的a 也就是a 的值改变了,p和b指向的值也会改变。

char *p=&a;//说明p指向的a
char *b=p;//b和p是完全等价的

如果是深拷贝的话,那么就是直接把当时值完全拿走。
深拷贝的例子

char* p2=(char*)malloc(strlen(p1)+1);//为了p2申请到的内存、
strcpy(p2,p1);//这个就是深拷贝

删除某个字符
插入某个字符


阿发你好 第16章 标准C语言库

标准C语言库也被称为:ANSI C 函数库

stdio.h

getchar()、putchar()//向控制台输入、输出一个字符
用法:

char ch=getchar();
putchar('A');

gets()、puts()//输入字符串、输出字符串
sprintf、sscanf()//

math.h

abs 取绝对值
ceil 向上取整
floor 向下取整
pow 求x的y次幂
sqrt 求平方根\
当在使用上面的函数的时候,一定要注意的事情:

double r1=sqrt((double)16);

time.h

这个用来计算某个算法的输出时间,但是目前存在得到问题,就是这个函数输出的是整数,当然你也可以吧弄在送餐上,说还有几秒到达目的地。

#include <iostream>
using namespace std;
#include <time.h>
int main()
{
    time_t start=11;
    time_t end=13;
    cout<<end-start<<endl;
    return 0;
}

输出的结果是2 其实就是用time_t来定义时间;
在这个头文件当中,time()函数,可以获得系统的当前时间

time_t now=time(NULL);
#include <iostream>
using namespace std;
#include <time.h>
int main()
{

    time_t during;
    time_t now=time(NULL);
    time_t end=time(NULL);
    during =end - now;
    cout<<during<<endl;
    return 0;
}

这个地方返回值的单位是秒。
localtime函数能够将time_t所表示的时间转化成年月日,时分秒形式
在time.h的文档已经定义好一个结构体 tm 然后可以调用这个结构体当中东西。
这里写图片描述
获取系统当前时间

#include <iostream>
using namespace std;
#include <time.h>
int main()
{

time_t t=time(NULL);
tm info = *localtime(&t);
cout<<info.tm_year+1900<<endl;
cout<<info.tm_mon+1<<endl;
cout<<info.tm_mday<<endl;
cout<<info.tm_hour<<endl;
cout<<info.tm_min<<endl;
cout<<info.tm_sec<<endl;
    return 0;
}

time_t是一个typedef

stdlib.h

atoi/atof 字符串转化成数字

int n=atoi("123");

rand/srand 随机数生成

for (int i=0; i<10 ; i++)
{
    int b=rand();
}

以及system里面的函数
system(“asdf”);相当于你在终端输入一些东西

在这种函数,那么输入的时候,直接就是char *p=a a如果是一个数组 那么就指向了这个数组

#include<string.h>
char *strcat(char *s1, char *s2);//拼接字符串
char *strchr(char *s, int c);//查找字符
int strcmp(const char *s1, const char *s2);//将两个字符串进行比较
char *strcpy(char *s1, const char *s2);//复制字符串
char *strstr(char *s1, const char *s2);//查找子串
size_t strlen(const char *s);//计算长度
int memcmp(const void * s1, const void *s2, size_t n);//按内存进行比较

stdio.h

1fopen:打开文件
2fwrite: 写入数据
3fclose: 关闭文件

阿发你好 第17章 文件操作

当你想要存储字符串的时候
一般存储的步骤:
1、fopen打开文件
2、fwrite 写入数据
3、fclose 关闭文件

#include <iostream>
using namespace std;
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
    const char * filename="C:\Users\Administrator.WIN7-20161129QO\Desktop\test.txt";
    FILE * fp=fopen(filename, "wb");
    if (fp==NULL)
    {
        cout<<"fail to open the file  "<<endl;
        return -1;
    }
    char buf[]="hello";
    int n=fwrite(buf ,1,5,fp);
    fclose(fp);
    return 0;
}

同样进行读取数据
1、fopen打开文件
2、fread 读取函数
3、fclose 关闭文件

#include <iostream>
using namespace std;
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
    const char * filename="C:\Users\Administrator.WIN7-20161129QO\Desktop\test.txt";
    FILE * fp=fopen(filename, "rb");
    if (fp==NULL)
    {
        cout<<"fail to open the file  "<<endl;
        return -1;
    }
    char buf[128]
    int n=fread(buf ,1,5,fp);
    fclose(fp);
    return 0;
}

数据的存储格式

其实就是看fwrite这个函数的定义

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

stream就是前面的fopen的返回值fpfp=fopen("") 这是进行存储的文件
buf 反正存储就是要有取地址符号

存储char类型的整数

char ch=12;
fwrite(&ch,1,1,fp);//存入
fread(&ch,1,1,fp);//读取

存储int型的整数

int n=12;
fwrite(&n,1,sizeof(int),fp);//存
fread(&n,1,sizeof(int),fp);//取

存取double类型的小数

double value=23.23;
fwrite(&value,1,sizeof(value),fp);
fread(&value,1,sizeof(value),fp);

存取结构体型数据

object obj={123};
fwrite(&obj,1,sizeof(value),fp);
fread(&obj,1,sizeof(value),fp);

存取字符串结构

char name[32]="adf";
fwrite(name,1,sizeof(name),fp);
fread(name,1,sizeof(name),fp);

其实可以, 就可以不断往这个里面写入数据。

int x=1;
char buf[128];
sprintf(buf,"x=%d",x);
fwrite(buf,1,strlen(buf),fp);

指针指向的对象

struct car
{
    char mark[32];
    int price;
}
fwrite(car->mark[32],1,32,fp);

Run-Length Encoding 存储

RLE是一种常见的编码技术、用于存储字符串的信息,RLE可以减少空间的浪费

fprintf是一种按行进行格式化写入

fgets是按行进行读取

文件的随机访问

顺序访问:按照顺序访问,不能随意跳跃
随机访问:可以任意调到一个任何位置进行访问
fseek随机访问
int fseek(FILE * stream,long offset,int mode);
文件的打开模式:
wb是写模式,rb是读模式


阿发你好 第18章 多文件项目以及编程过程

在C++项目中使用多个源文件和头文件

extern 声明全局函数

假设一个项目里面有A.cpp和B.cpp,我想实现在A.cpp当中调用B.cpp里定义的函数
要是早知道有这么灵活的技术该多好
其实本质上就多了一句extern double getarea(double r)

###---------------main.cpp---------------------###
#include <iostream>
extern double getarea(double r);
using namespace std;
int main()
{
    double r = 1;
    double dresult = getarea(r);
    cout << dresult << endl;
    getchar();
    return 0;
}
###---------------other.cpp---------------------###
double getarea(double r)
{
    return 3.14*r*r;
}

extern 声明全局变量

extern 也可以在A.cpp里面访问B.cpp里面的全局变量
只要在 你要哦用的函数里面声明一下就可以extern double other_r;

###---------------main.cpp---------------------###
#include <iostream>
extern double getarea(double r);
extern double other_r;
using namespace std;
int main()
{
    //double r = 1;
    double dresult = getarea(other_r);
    cout << dresult << endl;
    getchar();
    return 0;
}
###---------------other.cpp---------------------###
double getarea(double r)
{
    return 3.14*r*r;
}
double other_r = 1;

多文件项目的生成

C++项目生成过程主要分成2步:1、编译;2、链接
编译阶段:是将cpp转化成中间文件
链接阶段:是将中间文件转化成可执行文件

全量编译和增量编译

全量编译:将所有的CPP 文件都编译一遍
和增量编译:只对改变的量进行编译 (ROS里面采用的增量编译)

使用头文件

其中对于一个结构体而言

struct object
{
int value;
}
void fout(object *p)
{
double b= *p->value;
}
#include 称为一条预处理指令

预处理阶段的作用是;:在编译器处理处理每个Cpp文件之前,首先要将文件里面所有的预处理指令进行处理,生成一个中间文件,然后对这个中间文件进行编译

宏定义#define指定

所有以#为开头的行都是预处理指令

#define PI 3.14
#define 语法就是对他进行原文替换

我们更加推崇的做法是 用

const int a =1;
来替代
#define a 1

常见的几个宏定义

1、NULL 空指针

#define NULL 0
或者
#define NULL (void*) 0

2、 定义一个随机数

#define RAND_MAX 

这个随机数在不同的电脑上可能会有差异

条件编译指令 #if … #endif

#include <stdio.h>
#include <iostream>
using namespace std;
int main()
{
#if 1
    cout << "1 is cout" << endl;

#else
    cout << " else " << endl;
#endif
    getchar();
    return 0;
}
相当于
int main()
{
    cout << "1 is cout" << endl;
    getchar();
    return 0;
}

条件编译指令 #ifdef … #endif

#ifdef 表示如果对应的宏有定义,那么则相应的代码被编译
#else 来表示否则的情况
然后与用 #endif 来结束定义
同时也可以用#undef来去除定义
#ifndef 表示的意思和 #ifdef恰好相反
也就是说,当对应的宏不存在的时候,才能编译相应的代码
#include <stdio.h>
#include <iostream>
using namespace std;
#define ABC
int main()
{
#ifdef ABC
    cout << "1 is cout" << endl;

#else
    cout << " else " << endl;
#endif
    getchar();
    return 0;
}

相当于

cout << "1 is cout" << endl;
#include <stdio.h>
#include <iostream>
using namespace std;
#define ABC
int main()
{
#ifndef ABC
    cout << "1 is cout" << endl;

#else
    cout << " else " << endl;
#endif
    getchar();
    return 0;
}

相当于

cout << " else " << endl;

利用undef 来解除宏定义

#include <stdio.h>
#include <iostream>
using namespace std;
#define ABC
#undef  ABC
int main()
{
#ifdef ABC
    cout << "1 is cout" << endl;

#else
    cout << " else " << endl;
#endif
    getchar();
    return 0;
}

esle相当于没有这个宏定义的话。

cout << " else " << endl;

我们通常使用这两个兄弟来解决头文件的重复包含问题

#ifndef HEAD_H
#define HEAD_H


#endif

main函数的参数

main(int argc, char *argv[])

static

static 表示的是静态变量,当static修饰一个函数,称为静态函数,当static修饰一个变量的时候,称为静态变量。
static int number=0;当static修饰一个变量的时候,这个变量名字限制在本cpp中使用,不能其他cpp中使用。
static来修饰函数,这个函数就不能再别的cpp里面使用

从下面这段代码,我们可以知道 指针指向的,那么他们的数据类型必须是一样的。

#include <iostream>
using namespace std;
class object
{
public: 
    int price;
    int a;
};
int main()
{
    object obj;
    obj.a = 1;
    object *p = &obj;
    p->a = 0;
    cout << obj.a << endl;
    getchar();
}

感触:当你实例化一个对象,这个对象的类型是一个指针的话,那么要给这个指针分配内存,
在上面这种写法当中,这个指针直接指向了obj,利用的是栈里面的内存,当我们用malloc进行分配内存的额时候,是在堆里面申请的内存,需要你用free将刚刚申请的内存进行释放。
然后最后,当你判别不了的时候,就是函数的里面的形参等于实参 (函数里面的参数等于实际的参数)

#include <iostream>
#include <stdio.h>
using namespace std;
class object
{
public:
    int a;
    double b;
    void test(object *that )
    {
        cout << that ->a<<that->b<<endl;
    }
};
int main()
{
    object *obj=(object *) malloc(1000);
    obj->a = 1;
    obj->b = 2;
    obj->test(obj);
    free(obj);
    getchar();
}

this指针的使用

#include <iostream>
#include <iostream>
#include <stdio.h>
using namespace std;
class  object
{
public:
    int a = 1;
    int b = 2;
    void test()
    {
        cout << this->a << endl;
    }
};
int main()
{
    object obj;
    obj.a = 2;
    obj.test();
    getchar();
}

当你使用this指针的时候,完全不用考虑传递参数的事情了,this可以访问所有的类内成员变量和成员函数。

变量的命名覆盖
当一个被定义的时候,那么久找这个使用这个变两年的最近的地方,也就是就近原则。

成员函数
1、C++类的成员函数,首字母大写,尽量使用动词
成员变量
2、成员变量使用m_开头
当成员函数或者成员变量被const修饰的话
当一个成员函数被const修饰的时候,这该函数中不能修改类的成员变量的值。

    void test() const
    {
        this->a = 3;
        cout << this->a << endl;
    }

这个写法有问题的,也就是不能修a的值,不能修改由const 限制的成员变量的值。

    void test() 
    {
        this->a = 3;
        cout << this->a << endl;
    }

这样才对。

阿发你好第21章 构造与析构

构造函数的作用是 为了类内的成员赋予初值,没有返回值

class test
{
public:
    test()
    {
    this->a=1;
     }
   int a;
}

构造函数也可以被重载

构造函数的调用
构造函数和普通的成员函数不同,不显式调用,在创建的一个对象的时候,构造函数会被自动调用

#include <iostream>
#include <stdio.h>
using namespace std;
class  object
{
public:
    object()
    {
        a = 1;
    }
    int a ;
    int b ;
};
int main()
{
    object obj;
    cout<<obj.a <<endl;
    getchar();
}

这个里面直接通过构造函数,给a这个成员变量赋给了初值。然后这个还隐藏了this指针,this指针是可以省去的
析构函数
析构函数表示的构造函数销毁的过程,

class  object
{
public:
    object()
    {
        a = 1;
    }
    int a ;
    int b ;
    ~object()
    {
    }
};

构造函数只能有一个,不允许重载。
析构函数的调用

#include <iostream>
#include <stdio.h>
using namespace std;
class  object
{
public:
    object()
    {
        a = 1;
    }
    int a ;
    int b ;
    ~object()
    {
        cout << "hello" << endl;
    }
};
int main()
{
    {   object obj;
    cout << obj.a << endl;
        }
    getchar();
    return 0;
}

在析构函数当中,进行释放计算机内存,以及关闭文件
如何定义一个空指针

char *p;
p=NULL;

构造函数的初始化列表

#include <iostream>
using namespace std;
class object
{
    object()
    {
        this->x=1;
    }
public: 
    int x;
};
////////////////////等价于///////////////
#include <iostream>
using namespace std;
class object
{
    object() :x(0)
    {

    }
public: 
    int x;
};

构造函数的作用是对成员变量进行初始化,我们可以用初始化列表是来对成员变量进行初始化。

从下面的这段代码,我们可以看出,当多个成员变量的时候,我们可以用逗号进行隔开。另外,就是括号里面传递的参数也可以不是一个确定的值,可以是其他的东西。

#include <iostream>
class object
{
public :
    object(int a, int b) :a(a), b(b)
     {
    }
    int a;
    int b;
};

当成员本身也是一个类的时候。
给这个对象进行初始化

#include <iostream>
class object
{
public :
    object()
    {
    }

};
class circle
{
public :
    object obj;
    int r;
    circle(int r) :r(0), obj()
    {

    }
};

这里的object类的构造函数当中,本身没有传递任何参数
默认构造函数的两种表达形式
第一:没有任何参数

class object
{
public :
    object() 
    {
    }
    int a, b;
};

第二:所有的参数都有默认值,称为默认构造函数

class object
{
public :
    object(int a=1,int b=2)
    {
    //这个里面的a是形参,跟类里面那个a没有任何关系
    }
    int a, b;
};

默认是private

我觉得下面的这个例子好好体会就不错,

#include <iostream>
using namespace std;
class object
{
public :
    object() :a(1), b(2)
    {
    }
    int a, b;
};
class circle
{
public:
    circle():r(0), obj()
    {
        this->obj.a = 2;
    }
public :
    object obj;
    int r;

};
int main()
{
    circle c;
    cout << c.obj.a << endl;
    object obj2;
    cout << obj2.a << endl;
    getchar();
    return 0;
}

当时实例化c的时候,已经将用了他的构造函数了,再构造函数当中,已经将object.a的数值变成2,然我在第二个类里面,已经初始化列表,然后改变了这个东西。实例化了一个object
没有把饭使用初始化列表的情况
当成员没有默认构造函的时候
无名对象
这种写法只需要知道就可以。如果一个对象只是被临时使用一次,那么可以不用为这个对象进行赋值。

阿发你好第22章 动态创建对象

在c里面我们使用malloc 和 free 函数进行释放内存

int *p=(int *)malloc(100);
free(p);

在C++当中我们用new 和 delete 来申请和释放内存

int *p=new int;
*p=100;
delete p;

当你要创建某个类型的指针的时候

type *p =new type

当你要创建多个类型的对象的时候

type *p= new type[N];
delete []p;

new操作符 不仅分配了必要的内存,而且调用了相应的构造函数并且对相应构造函数进行初始化,使他成为一个对象。new还可以初始化几个参数。

#include <iostream>
using namespace std;
class Object02
{
public:
    int x;
    int y;
    Object02() :x(0), y(0)
    {
        cout << "gouzao " << endl;
    }
    Object02(int x, int y) :x(x), y(y)
    {

        cout << "gouzao 2" << endl;
        cout << x << endl;
        cout << y << endl;
    }
    ~Object02()
    {

        cout << "xiugou" << endl;
    }
};

int main()
{   
    Object02 *obj2 = new Object02(1,1);
    delete obj2;
    getchar();
    return  0;
}

当构造函数里面含有参数的时候,那么这个时候初始化列表只能是:x(x),y(y)来这样进行赋值,因为传入的参数,就是一直在x里面的参数。然后再new一个对象的时候,也是可以进行直接赋值的Object02 *obj2 = new Object02(1,1);
当创建对个对象的时候,不能指定构造函数,要求必须有一个是默认构造函数。

这个就是默认构造函数。

    Object02() 
    {
    }
#include <iostream>
using namespace std;
class Object02
{
public:
    int x;
    int y;
    Object02() :x(0), y(0)
    {
        cout << "gouzao " << endl;
    }
    Object02(int x, int y) :x(x), y(y)
    {

        cout << "gouzao 2" << endl;
        cout << x << endl;
        cout << y << endl;
    }
    ~Object02()
    {

        cout << "xiugou" << endl;
    }
};

int main()
{   
    Object02 *obj2 = new Object02[12];
    delete []obj2;
    getchar();
    return  0;
}

也就是所上面这个程序是没有办法正常运行,因为没有默认构造函数

#include <iostream>
using namespace std;
class Object02
{
public:
    int x;
    int y;
    Object02()
    {}

    Object02(int x, int y) :x(x), y(y)
    {

        cout << "gouzao 2" << endl;
        cout << x << endl;
        cout << y << endl;
    }
    ~Object02()
    {

        cout << "xiugou" << endl;
    }
};

int main()
{   
    Object02 *obj2 = new Object02[12];
    delete []obj2;
    getchar();
    return  0;
}

值得注意的地方是 new []和 []delete一定要配套使用 另外,new 和 delete 也要配套使用。

object *p=new object();//这个地方使用完之后直接调用他的构造函数。
delete p;
p=NULL

删除之后,直接将这个数值置为空指针。

阿发你好23章 继承

class A :public C
{

};

C是父类(基类),A为子类(派生类)

private 不可以被其他子类继承, 不可以被外部访问
public 可以被外部继承和访问
protected 不可以被外部访问,但可以被外部继承

成员函数的重写

#include <iostream>
using namespace std;
class Object02
{
public:
    void test()
    {
        cout << "test_in_fater" << endl;
    }
};

class object_son :public Object02
{
public:
    /*
        void test()
        {
        cout << "test_son" << endl;
        }


    */

};

int main()
{   
    object_son son;
    son.test();
    getchar();
    return 0;
}

成员函数的重写能够让我替换到原来函数的模块。并且不用改这么庞大的一个体系中的函数的名称
虚拟继承
在这个里面实例化一个父类的指针,然后new 的是son 这样的输出father里面的东西

#include <iostream>
using namespace std;
class father
{
public:
    void test()
    {
        cout << "father" << endl;
    }
};
class son : public father
{
public:
    void test()
    {
        cout << "son" << endl;
    }
};
int main()
{
    father *p = new son;
    p->test();
    getchar();
}

虚函数virtual
还是上面的例子,如果在father这个类里面得到 virtual void test(); 这样就 那么在 father *p = new son;调用的时候,就是调用son这个类里面的test函数。
在继承关系中的 父类的析构函数,应该被声明为virtual

#include <iostream>
using namespace std;
class father
{
public:
    virtual void test()
    {
        cout << "father" << endl;
    }
};
class son : public father
{
public:
    void test()
    {
        cout << "son" << endl;
    }
};
int main()
{
    father *p = new son;
    p->test();
    getchar();
}

在继承关系下,显式的指定构造函数

#include <iostream>
using namespace  std;
class father
{
public:
    father() :x(0), y(0)
    {
        cout << "x" << x << endl;
    }
    father(int x, int y) :x(x), y(y)
    {
        cout << "father_x:" << x << endl;
    }
    int x;
    int y;
};
class son :public father
{
public:
    int x;
    int y;
    son() :father(1, 2)
    {
        cout << "son_x:" << x << endl;
    }
};
int main()
{
    son s;
    getchar();
    return 0;
}

输出:

这里写图片描述
至少说明 在son() :father(1, 2) 这个里面调用了father里面的第2个 而不是默认的调用的第一个。这就指定调用父类里面的函数

多重继承
就是一个类里面可以同时继承多个父类
例如:

class son: public father1, public father2
{

};

继承函数与纯虚类
纯虚函数是一种特殊的虚函数
纯虚函数的语法
1、将成员函数声明为:virtual
2、后面=0
3、该函数不能有函数体

virtual void test()=0;

包含纯虚函数的类称为纯虚类。纯虚函数也叫做抽象函数,纯虚类也叫做抽象类,抽象类是不能被实例化,用来表示接口类。或者被用来做回调机制。它总是用来被人继承,并且进行重写。

阿发你好第24章 拷贝构造函数

拷贝构造函数 它在对象复制的时候被调用
下面这个地方就是拷贝构造函数,其实就是把这个类应用这个对象的。
拷贝构造函数的额参数是固定的。

#include <iostream>
using namespace std;
class Object
{
public:
    Object()
    {}
    Object(const Object& other)
    {
        cout << "coy" << endl;
    }
};
int main()
{
    Object a;
    //Object b(a);
    //或者写成
    Object b = a;
    getchar();
    return 0;
}

拷贝构造函数有两种进行赋值方式 第一种就是 type a(c) 第二种就是type a=c
动态创建对象

object a;
object *p=new object(a);

默认构造函数的规则
1、如果这个类是有父类的,那么先调用父类的拷贝构造函数
深拷贝

Text(const Text &other)
{

}
这里面就是深拷贝,反正有&就是深拷贝。

阿发你好 第25章 静态成员

static关键字用于声明全局变量和全局函数。

####-----head.h------####
#ifndef HEAD_H
#define HEAD_H

class Object01
{
public:
    static int number;
};

#endif


####---other.cpp--####

#include "head.h"
int Object01::number = 1;

###-----main.cpp--------###
#include "head.h"
#include <iostream>
using namespace std;
int main()
{
    Object01 a;
    cout << a.number << endl;
    getchar();
    return 0;
}

在使用的是时候,要加上相当于命名空间
全局函数

######-------head.h------####
#ifndef HEAD_H
#define HEAD_H

class Object01
{
public:
    static void number();
};

#endif
######------other.cpp-----###
#include "head.h"
#include <iostream>
void Object01::number()
{
    std::cout << "dfdf" << std::endl;
}
####---main()-----####
#include "head.h"
#include <iostream>
using namespace std;
int main()
{
    Object01 a;
     a.number() ;
    getchar();
    return 0;
}

在使用的时候,用这个也可以。不实例化对象也可以

Object01::number();

这种函数在使用的时候前面类名和冒号。
static 函数里面没有不能使用this指针。
只有类内的成员函数才能调用已经使用private修饰的的static函数

###-----head.h------###
#ifndef HEAD_H
#define HEAD_H

class Object01
{
public:
     void call();

private:
    static void number();
};

#endif
###-----other.cpp------###
#include "head.h"
#include <iostream>
void Object01::number()
{
    std::cout << "dfdf" << std::endl;
}
void Object01::call()
{
    Object01::number();
}
###-----main.cpp------###
#include "head.h"
#include <iostream>
using namespace std;
int main()
{
    Object01 a;
    a.call();
    getchar();
    return 0;
}

static可以自由访问类内的其他成员
普通的全局函数无法访问类内的私有成员的。但是staic函数可以访问类内额所有成员

###------head.h--------###
#ifndef HEAD_H
#define HEAD_H

class Object01
{
public:
    static void number(Object01 &obj);
private:
    int x;
    int y;
};

#endif
#####-----other.cpp---------######
#include "head.h"
#include <iostream>
void Object01::number(Object01 &obj)
{
    obj.x = 1;
    obj.y = 2;
    std::cout << obj.x <<std:: endl;
}
####---------main.cpp-------######
#include "head.h"
#include <iostream>
using namespace std;
int main()
{
    Object01 a;
    Object01::number(a);
    getchar();
    return 0;
}

阿发你好第26章 友元

友元定义:

class A
{
    friend class B;//声明B是A的友元
    friend void fun();//声明全局函数fun是A的友元
};

友元的作用可以无限制的访问A类的所有成员,不受public 和private的限制
在上面的这种关系当中B可以访问A的所有成员,但是A不能访问B的成员。这种友元的关系是单向的。

阿发你好第27章 重载操作符

是的自己定义的类型,可以像基本类型一样支持加减乘除等多种操作。

阿发你好第28章 内部类和命名空间

是的自己定义的类型,可以像基本类型一样支持加减乘除等多种操作。

class A
{
    class B
    {};
};

B称为A的内部类
在使用这个内部类的时候

int main()
{
A::B a;
}

内部类和普通的类没有任何区别

另外介绍一个枚举类型enum 他本质上相当于int类型,必须是整数
通常我们不给enum 定义类型名称,因此经常空缺,相当于一些宏定义的东西,只是方便与理解,中间用逗号进行隔开,最后一个不需要逗号

#include <iostream>
using namespace std;
enum
{
    LARGE=100,
    Mid=50,
    Small=0
};
int main()
{
    int a = LARGE;
    cout << a << endl;
    getchar();
    return 0;
}

typedef 是给已经有的类型设置一个别名
这是一个关键字,记得后面添加上分号

#include <iostream>
using namespace std;
typedef int A;
A main()
{
    A a = 1;
    cout << a << endl;
    getchar();
    return 0;
}

同样也可以在一个类里面能定义enum

#include <iostream>
using namespace std;
class A
{
public:
    enum B
    {
        X=1
    };

};
int main()
{
    A::B ab=A::X;
    getchar();
    return 0;
}

命名空间 目的是为了防止命名重复

    namespage ID
    {
    }
using A::B

表示A名空间下部分名字

阿发你好 第29章 模板

模板函数

template <typename T>
I number(T a)
{
return a
}

在使用的时候就是

#include <iostream>
using namespace std;
template <typename T>
T number(T a)
{
    return a;
}
int main()
{
    int a = number<int>(1);
    cout << a << endl;
    getchar();
    return 0;
}

是对上面的模板进行实例化 使用int代替了原来的typename

number<int>()

模板类

我在这个地方综合的模板类和模板函数,然后其实就是一种替换机制,还是在别人实现的时候,自己能够看懂代码。

#include <iostream>
using namespace std;
template <typename T>
T number(T a)
{
    return a;
}
template<typename A>
class OBJECT
{
public:
    A& front(A other )
    {
        cout << "dfdf" << endl;
        return other;
    }
};
int main()
{
    int a = number<int>(1);
    OBJECT<int> obj;
    int b=obj.front(1);
    cout << b << endl;
    getchar();
    return 0;
}

模板参数

#include <iostream>
using namespace std;
template <int N,typename T>
T number(int b,T a)
{
    int c = N;
    return N;
}
template<typename A>
class OBJECT
{
public:
    A& front(A other )
    {
        cout << "dfdf" << endl;
        return other;
    }
};
int main()
{
    double a = number<10,double>(10,0.1);
    OBJECT<int> obj;
    int b=obj.front(1);
    cout << a<< endl;
    getchar();
    return 0;
}

然后这种方式在实例化的时候,就是要在给两个值,跟前面的地方对应起来

template <int N,typename T>
 number<10,double>

阿发你好第30章

标准模板库STL
STL库当中线性数据结构

1vector 向量
2list 链表
3map 映射
4string char字符串
5queue 队列 先进先出
6stack 栈 先进后出
7set 集合
8、dqueue 双向链表

相关的头文件在在#include<vector>
引用命名空间std
调用

vector<int> arr; //传入模板参数为int
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> arr;
    arr.push_back(1);
    arr.push_back(2);
    arr.push_back(3);
    for (int i = 0; i < arr.size(); ++i)
    {
        cout << arr.at(i) << endl;
    }
    getchar();
    return 0;
}

arr.push_back 就是添加这元素进去 然后arr.at 就是表示这个元素。
然后后面再遍历这个数据,其实就可以了 其实你在定义的时候,最好定义成static 类型,或者在前面用一个外部声明,然后就可以吧这个地添加s数据地方整合了。感觉现在我可以写出更好存储数据的方法。

push_back 表示在尾部添加一个元素
pop_back 从尾部删除一个元素
clear 清除所有元素
at 索引访问位置的元素
front 返回头元素
back 返回尾部元素
size 返回元素个数
capacity 返回当前容量
resize 改变容器的大小
insert 在中间插入元素
erase 删除中间元素

iterator和 const iterator
iterator迭代器是vector的内部类,用于对vector内部元素的遍历
在实例化对象的时候

vector<int>::iterator iter;

vector的begin函数用于取得迭代器初始位置。end用于取得末尾的位置。

for(vector<int> iter=arr.beign();iter!=arr.end();++iter)
{

}

当你要迭代的vector 被const修饰的时候,你要使用const_iterator

void test(const vector<int> arr)
{
    for (vector<int>::const_iterator iter=arr.begin(); iter!=arr.end();iter++)
    {
    }
}

vector 不适合使用insert 和erase函数,如果需要进行再某个位置进行插入的话,建议使用list

iter.insert(arr,1,s);

STL当中的list
在头文件#include <list>当中,允许从两端插入,也允许从中间插入和删除,但是只支持顺序访问,不支持随机访问

#include<list>
#include<iostream>
using namespace std;
int main()
{
    list<int> my_list;
    m_list.push_back(1);
    my_list.pop();
    getchar();
    return 0;
}

string 提供字符串功能#include<string>对于vector不同的地方

1、append 通过添加字符串
2length 相当于返回size的大小。
3substr 取得一个子串

然后是他的构造函数

string str1("xiaoming");
string str2="xiaoming";
string str3="";//空字符串
string str4;//空字符串

可以用c_str()函数获取string内部的字符串指针

const char *p=str1.c_str();

关于append函数

stirng str;
str.append("davidhan");//附加一个字符串
str.append("abcdf",5);//附加一个字符串,只赋值前5个字符
str.append("abcdef",2,2);//附加起点,也就是从2个开始为起点,也就是复制bc
这前面都是双引号
str.append(3,'h');//这条语句只能赋值单个字符,并且使用的是单引号。

在string里面由于重载了 == 等运算符,因此可以直接里面这些运算符来计算

#include <string>
#include <iostream>
using namespace std;
int main()
{
    string str1="daivda";
    string str2("davidb");
    if(str1==str2)
    {
    }
    else if(str1>str2)
    {
    }
    else(str1<str2)
    {
    }
}

字符串的查找
利用find函数进行查找一个字符或者一个字符串,从左到右进行查找,直到第一次匹配的时候,返回的是匹配的位置,如果没有找到这个字符,返回-1

#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
    string str1;
    str1.append("abc");
   int pos=str1.find('c');
   cout<<pos<<endl;
    return 0;
}

这个地方输出的pos的值是2. 也就是说a的位置是0,b是1,C是2.
反正单个字符用'h' 多个字符串用"stirng"
然后找字符串的时候

int pos=find("ing",pos+3);

因为这里的ing 有3个字母;
默认的情况下是从左往右查找,如果想要想右往左查找,就用rfind函数

int pose=rfind("ing",pos-3);
find_first_of函数用于查找若干字符中的一个,只要其中任意一个字符匹配,就返回字符的位置

字符查找的顺序是从左到右
substr用于复制部子字符串,需要两个参数,第一个字符表示起始位置,第二个字符表示截取的长度。

string src("abcdef");
string str1=src.substr(4,2);//输出的ef:因为从0开始计数的
string str2=src.substr(3);//如果后面没有数值, 

string同样不适合用insert和删除操作
string 作为函数的参数

void test(const string &str);
void test(string& str);
void test(string str);

其实这本质上面就是传值和引用的事情。
map
map表示映射表,需要加入头文件#include<map>;映射表用来存储每一项数据的键key和值value.根据每一项的key 可以得到不同的value。map的优势就是快速。
map在定义的时候,需要制定key和value的类型。map<int,Object> objs;
由于这个类型名字太长了,可以用typedef进行替换 typedef map<int,Object> ObjectMap
那么最后就写成了 ObjectMap objs;
使用[ ]这种方式进行插入数据

objs[1]=a;
objs[2]=b;

查找
在map当中可以通过key来查找value的值

ObjecMap::iterator iter=objs.find(1);
if(iter!=objs.ends())
{
    Object& s=iter->second;
    cout<<s<<endl;
}
其实对于iter->second 就是第二个value的数值

遍历
stack
就是在栈

阿发你好 第31章节 异常

异常处理主要有3个关键字throw/try/catch
throw称为抛出
try … catch 监视一段代码是否有错误发生

if (a!=0)
    throw 100;
int main()
{
    try{
        throw 12.3;
    }
    catch(int ex)
    {
        cout<<"int error"<<endl;//由于跟上面额12.3类型不匹配,因此不能
    }
    catch(double ex)
    {
        cout<<"double error"<<endl;//double类型跟上面类型相互匹配,因此进行这条语句当中。
    }
    catch(...)
    {
        cout<<"the proble is error"<<endl;//任何类型都有问题
    }
}

最后尽量不要在构造函数和析构函数中进行抛出异常


阿达你好补充篇 1.1单步调试技术

编译器只是检查语法错误
通常当有错误提示的时候:只解决第一条语法错误
程序运行的结果和程序员预期的结果不一致,称之为bug,解决问题的过程叫做debug。

阿达你好补充篇 1.2描述错误

1、前提条件
在什么条件下,什么错误必然会发生
2、预期的结果
你希望程序出现什么样的结果?
3、实际输出的结果
程序实现的输出的结果是什么样子的?

阿发你好补充篇1.3定位错误

这里写图片描述

阿发你好补充篇1.4单步调试

如何开启调试窗口
观察局部变量的窗口
同时也可以将这个值用10进制来看
这里写图片描述
观察内存的窗口
这里写图片描述
观察全局变量窗口
这里写图片描述
在这些窗口中可以直接修改内存里面的值的。

阿发你好补充篇1.5观察变量的值

观察每个变量在内存当中的值,sizeof(int)
这里写图片描述
一个变量,初始化的时候没有赋值,这个时候还是有值的,cccccccc

阿发你好补充篇1.6单元测试

阿发你好补充篇 1.7程序崩溃的调试方法

这里写图片描述

阿发你好补充篇 1.8程序崩溃的原因分类

1、读取未赋值的变量
1.1一个变量未初始化、未赋值、就读取了它的值
2、函数栈溢出
2.1定义了一个体积太大的局部变量
2.2函数嵌套调用,层次过深
3、数组越界访问
当出现这个提示报错的时候,就是因为数组的越界访问
这里写图片描述
4、指针的目标对象不可用
4.1空指针
4.2野指针
4.2.1指针未赋值
4.2.2free/delete释放了对象
4.2.3不恰当的指针强制变换

阿发你好补充篇 2.6 位操作应用

位操作的使用场景,1、嵌入式 2、编解码
base64编码
这里写图片描述


阿发你好补充篇 3.1中文配置问题

中文问题:拉丁字符集

阿发你好补充篇 3.2中文字符集问题

阿发你好补充篇 3.3字符编码的转换

GBK/UTF8/UTF16 都是中文的编码
在visual studio 里面有wchar_t来表示一个宽字符,用来存放unicode 字符


阿发你好补充篇 4.1动态库简介

库两种:动态库(DLL),静态库(SL)
这里写图片描述
这里写图片描述
上面这是一个用于生成DLL的框架代码
这里写图片描述
创建DLL
1、取消预编译头文件
2、改为/MTd编译
3、修改DLL的名字
4、改库的名字

编译最后得到 .dll 和.lib文件
这里写图片描述
把这个地方改成不使用预编译头:
这里写图片描述
在这个地方修改lib的名称和输出路径
这里写图片描述
把这里改成MTd
这里写图片描述
在连接器里面的输出文件改成my.dll
这里写图片描述
然后再按F7编译一下,
这里写图片描述

在这个目录下找到我们所要的东西

这里写图片描述
然后在这个目录下找mylib
这里写图片描述

my.dll 包含所有的代码编译成的指令
my.lib:包含一个列表

如果使用自己定义的动态链接库:
将他们都放置在需要使用的工程目录下
这里写图片描述
这里写图片描述
这里写图片描述


#include <stdio.h>

#pragma  comment (lib,"my.lib")
//这一行主要是声明,这个函数Add需要从dll中导入
__declspec(dllimport) int Add(int a, int b);

int main()
{
    int result = Add(10,11);
    printf("%d", result);
    return 0;

}

为了暂停界面,我做了一些调整
这里写图片描述
一定要注意的是,这个地方是两个“_ _”
dll 动态链接库 作用是:隐藏源码,公开功能

如果我们使用只是使用了dll的话,操作系统必须找到对应的dll的时候,程序才能正确执行,一般情况下,DLL放在以下位置,操作系统才能找到对应的dll
1、当前执行文件目录,也就是工程目录
2、系统下c/windows/system32/和c/windows/system/
3、进程的当前目录中
4、windows目录中,例如:C:/windows/
5、环境变量所在的PATH目录中
这就说明了,为什么建议一些软件都安装到C盘问题比较小,应为动态链接库的问题。

阿发你好补充篇 4.2动态库的加载与卸载

dll 本身并不能独立运行,只有 .exe被运行的时候,dll才会被加载运行
在一个dll文件当中,至少有两个段
1、数据段:用于存放全局变量
2、代码段:用于存储指令(函数体)
结论:数据段被每个进程各自拷贝一份,虽然DLL当中使用的全局变量,但是各个进程之间不受影响。
小结:
1、.exe文件被加载运行时、DLL才被加载
2、dll加载以后,该文件处于被占用的状态,不能直接修改删除,直到它被卸载
3、所有的使用它的进程结束以后,该DLL才能被卸载
4、不同进程下不能比较变量的地址
在同一个进程里面,两个变量的地址相同,则他们指向的同一个变量
不同的进程里面,同一个变量的可以有两个不同的地址

阿发你好补充篇 4.3动态内存管理

在C语言的当中用malloc申请内存,用free进行释放内存
在C++当中用new进行申请内存,用delete进行释放内存
在dll中用malloc申请的内存,必须在dll中进行free进行释放

阿发你好补充篇 4.4使用头文件

在dll项目当中,将函数声明为__declspec(dllexport)
在APP项目中,将函数声明为__declspec(dllimport)
补充:条件编译指令#ifdef #endif
这里写图片描述


#ifndef _MYDLL_H
#define  _MYDLL_H

#ifdef MYDLL_EXPORTS//如果我们定义了MYDLL_EXPORTS这个宏
#define  MYDLL __declspec(dllexport)//那么我们就使用这个定义,将MYDLL定义为__dllexport
#else
#define MYDLL __declspec(dllimport)//如果我们没有定义这个宏的话,我们就把MYDLL定义为__dllimport
#endif
MYDLL int Add(int a, int b);//函数的声明

#endif

在demo当中
这里写图片描述

#include <stdio.h>
#define  MYDLL_EXPORTS
#include "mydll.h"
int Add(int a, int b)
{
    return a + b;
}

将my.h my.lib my.dll都拷贝到工程目录当中
现在都会直接隐藏了声明

进行了正常的运行
这里写图片描述

#include <stdio.h>
#include "mydll.h"
#pragma  comment (lib,"my.lib")
//这一行主要是声明,这个函数Add需要从dll中导入
//__declspec(dllimport) int Add(int a, int b);(声明部分都放在头文件当中,因此现在都是用头文件做声明部分)
#include <iostream>
int main()
{
    int result = Add(10,11);
    printf("%d\n", result);
    system("pause");
    return 0;
}

给别人项目的时候,只需要给被别人 .h .dll .lib文件就可以了

如何把自己的头文件用#inlude<mydll.h>来用?
只需要吧自己的头文件放到指定文件夹后进行设定和添加就可以了
这里写图片描述
在该目录下新建两个文件夹
这里写图片描述
在bin目录下放着两个文件
这里写图片描述
include目录下放这个文件
这里写图片描述
将include文件包含进去
这里写图片描述
将bin目录文件包含进去
这里写图片描述
运行(这个地方改了,照样可以运行)
这里写图片描述
遇到的问题:
这里写图片描述
将dll放在C盘下,就可以了

阿发你好补充篇 4.5导出类

在动态库里面导出一个class
导出class,其实本质上是要导出这个class对应的成员函数
这里写图片描述

阿发你好补充篇 4.6静态库

静态库就是static library,生成的时候只生成lib
这里写图片描述
这里写图片描述
这里写图片描述
下面提供一个框架
这里写图片描述
然后再中间的部分填写你要用的函数
这里写图片描述
为了方便使用,我们将输出的目录改成
这里写图片描述
按F7编译之后就有一个lib的文件
这里写图片描述
将刚刚建立的头文件拷贝到解决方案的目录下
这里写图片描述
然后在添加头文件
这里写图片描述
这里写图片描述
我觉得这样做完全可以吧原来定义add函数给隐藏的同时,还能保证用户的正常使用
这里写图片描述
这里写图片描述
这里写图片描述
静态库存在的问题:
如果这个静态库是VS2008编译的,那么使用的时候也必须用VS2008进行编译
另外在编译的时候,这个地方必须相同
这里写图片描述
静态库优点:使用静态库,呢么最后的执行程序执行的时候,对原来的库不再依赖
缺点,很多,所以一直都是使用动态库

阿发你好补充篇 4.7动态库的手工加载

在编译的时候,不指定dll,而在运行的时候用一个loadLibrary来加载dll、使用FreeLibrary 来卸载dll
这里写图片描述
这里写图片描述

阿发你好补充篇 4.8 动态库-vc的静态编译

默认是动态库,
这里写图片描述
使用静态库不用指定对应的DLL版本,就可以直接运行
这里写图片描述
动态编译不方便发布。


阿发你好第5章 5.1 函数指针

函数指针和回调机制
函数名和变量名一样,也对应着一个地址
这里写图片描述

void test(int a)
{

    printf("hello word!");
}
int main()
{void (*p) (int);//这里面定义的变量 P,P的类型是函数指针

p=&test;
}

当有两个输入的时候
这里写图片描述
函数指针,就是指向了函数的地址

如何调用函数指针,直接把他当做函数来使用就可以了。
这里写图片描述
这里写图片描述
这里写图片描述

阿发你好补充篇第5章 5.2 函数指针的使用

利用typedef给函数指针的类型起一个别名

typedef void (* MY_FUNCTION)(int);
MY_FUNCTION P;
P=&example;

也可以将一个函数指针类型做为一个参数。
这里写图片描述

函数指针作为成员变量
这里写图片描述

阿发你好补充篇第5章 5.3 回调机制

回调机制callback
这里写图片描述
这里写图片描述
应该使用多字符集
这里写图片描述
这里写图片描述
一般情况下,我们都是调用系统的函数,但是现在我们系统系统能够调用我们的函数,我们将函数的地址作为一个参数传给系统的API。
这里写图片描述

什么是回调机制?

阿发你好补充篇第5章 5.4 回调函数的上下文

透传:透明传输、不关心类型和与内容 void* 来表示。

阿发你好补充篇第5章 5.5 cpp中的回调函数的实现

在Cpp当中是用class的语法来实现回调的

阿发你好补充篇第6章 XML-文档格式

XML可扩展标记语言

<?xml version="1.0" encoding="GBK"?>//这是声明,encoding是文字编码格式:GBK
<root>//内容
    <host> afanihao.cn </host>//这是一个主机名
    <port> 8080 </port>//这是一个端口号
</root>

关于xml的教程 : 百度输入 W3C school
我们将成对出现的东西都叫做元素,例如root元素,host元素

阿发你好补充篇 6.2 生成XML

在Cpp当中声明xml的文档,所使用到的库 tinyxml,这是一个小巧的XML库,
sourceforge 是一个大型的开源库
?如何将一个源码的包放到一个visual studio的项目当中
1、将源码解压缩到工程的目录下面
这里写图片描述

这里写图片描述
新建一个筛选器
这里写图片描述
这里写图片描述
将文件夹里面所有的文件都包含进去
在使用的时候需要添加进去相应的头文件
这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

阿发你好补充篇 6.3 解析XML文档

阿发你好 补充篇 6.4 XML应用配置文件

XML的应用场景
1、用于保存配置文件
2、服务器的RESTful接口
3、WebService接口

阿发你好 补充篇 7.1 变量的原始值

变量总是有值的,即使没有给她赋初值。

阿发你好 补充篇 7.2 typedef的用法

typedef 用于给已有类型给定一个别名

typedeg unsigned int UNIT32;//这里UNIT32就是unsigned int 的别名

结构体的两种写法
这里写图片描述

阿发你好 补充篇 7.3 main函数之前的函数

构造函数就是类型和函数名相同的函数

阿发你好 补充篇 7.4 绝对路径和相对路径

相对路径。指的是程序工作的时候所在的目录
这个就是这个工程的当前工作路径
这里写图片描述
在哪里目录运行的程序,那个目录就是当前路径

阿发你好 补充篇 7.5 数组的长度

数组的长度
数组的调用
这里写图片描述
正确的观点是:由于数组名只代表首地址,所以长度信息需要用另一个变量来表示。
当把数组作为函数参量来传递的时候,有sizeof就无法知道了
这里写图片描述

阿发你好 补充篇 7.6 二维数组的用法

这里写图片描述
这里写图片描述
这里写图片描述

阿发你好 补充篇 8.1 常用的数据结构和算法

这里写图片描述
选择排序法
这里写图片描述

选择排序的函数
这里写图片描述
这里写图片描述

阿发你好 补充篇 8.2 遍历查找算法

这里写图片描述
这里写图片描述
并不是你写出来的代码越快越好
这里写图片描述
这里写图片描述
代码需要关注的三点:
可读性、可扩展性、可重用性

阿发你好 补充篇 8.3 二分查找算法

这里写图片描述
这里写图片描述

阿发你好 补充篇 8.4 静态链表查找算法

阿发你好 补充篇10.1字节编码

对象的序列化(serialization), 也叫做串行化,只指的,将一个内存对象转换成一串字节数据,并且可以恢复
反序列化,就是将一串字节数据转换成一个内存对象。
这里写图片描述

阿发你好 补充篇10.2大端小端

字节序
大端小端就是字节顺序的一个问题
这里写图片描述
基本上所有的系统的都是小端系统
这里写图片描述

阿发你好 补充篇10.3字节编码器

这里写图片描述

阿发你好 补充篇 11 .1 JSON

json是一种数据格式 全称是javaScript Object Notation 是一种文本的数据格式,类似于XML
这里写图片描述

阿发你好 补充篇 11 .2 JSON

阿发你好 线程篇 1.1

函数的调用是串行
线程 thread :线程技术用来实现并发的任务,可以让多个任务可以同时运行。也就是说,让每个任务在各自的线程中运行
线程的运行模式,main函数本身就是一条主线
使用OSAPI来创建了一个线程

#include <stdio.h>
#include "osapi/osapi.h"
class  MyTask :public OS_Thread
{
private:
    virtual int Routine()
    {
      //线程体:执行他的任务
        for (int i = 0; i < 100;++i)
        {
            printf("sdfadfadf\n");
        }
        return 0;
    }
};
int main()
{
    MyTask task;
    task.Run();
    getchar();
    return 0;
}

线程的调度
就是每个线程都要自觉的让出CPU,以便让别的线程也有机会被运行,让出的办法就是使用sleep函数
操作系统会吧时间分割成很小的时间段,让每个线程都有机会运行几个毫秒,轮流运行,总体感觉上是每个线程是同时运行的

#include <stdio.h>
#include "osapi/osapi.h"
class  Buddish :public OS_Thread
{
private:
    virtual int Routine()
    {
      //线程体:执行他的任务
        for (int i = 0; i < 100;++i)
        {
            printf("sdfadfadf\n");
            OS_Thread::Sleep(1);//休息一秒
        }
        return 0;
    }
};
class  funcion :public OS_Thread
{
private:
    virtual int Routine()
    {
        //线程体:执行他的任务
        for (int i = 0; i < 100; ++i)
        {
            printf("汉族\n");
            OS_Thread::Sleep(1);//休息一秒
        }
        return 0;
    }
};
int main()
{
    Buddish task1;
    task1.Run();

    funcion task2;
    task2.Run();

    getchar();
    return 0;
}

阿发你好 线程篇 1.2 进程与线程

进程:当运行Task1.exe被加载到内存,这个运行这的就是一个进程
每运行一次,就有个进程被运行
ctrl +shift + esc 可以直接打开任务管理器
一个进程含有三个线程(一个进程中含有多个线程)
操作系统决定某一个时刻那个线程被运行

时间片法一种普遍的调度算法
1、使用sleep,可以主动让自己的线程提前让出CPU
2、sleep时间到的时候,该线程并不是立刻被执行,而是进入候选队列
3、不同的操作系统对线程的方法可能不一样
4、线程是有优先级的,但是并不是所有的操作系统都是支持优先级的

这里写图片描述

阿发你好 线程篇 1.2 线程的创建和使用

这里写图片描述
在资源编辑器当中,可以看见一个进程当中包含多少个线程
这里写图片描述
一个进程当中的最大的线程数是有限的,一般是几千,但是在工程实践中一般也就是几十个

阿发你好 线程篇 1.4 线程的启动和回收

这里写图片描述
Routine这个函数就是线程的主函数
这里写图片描述

#include <stdio.h>
#include "osapi/osapi.h"
class  Buddish :public OS_Thread
{
public:
    bool m_quitflag;
private:
    virtual int Routine()
    {
      //线程体:执行他的任务
        for (int i = 0; !m_quitflag&&i < 100;++i)
        {
            printf("sdfadfadf\n");
            OS_Thread::Sleep(1);//休息一秒
        }
        printf("now finish it ...\n");
        return 0;
    }
};
class  funcion :public OS_Thread
{
private:
    virtual int Routine()
    {
        //线程体:执行他的任务
        for (int i = 0; i < 100; ++i)
        {
            printf("汉族\n");
            OS_Thread::Sleep(1);//休息一秒
        }
        return 0;
    }
};
int main()
{
    Buddish task1;
    task1.m_quitflag = false;
    task1.Run();

    getchar();

//  funcion task2;//说明这两个线程可以开始干活了
//  task2.Run();
    printf("----------------------主线程开始工作了----------------\n");
    task1.m_quitflag = true;
    task1.Join(&task1);//join这个函数是等待task1
    getchar();
    return 0;
}

join()函数的作用是:
1、等待线程的退出
2、回收这个线程的相关资源
一个线程不能自己回收自己,只能由别人来回首自己

阿发你好 线程篇 1.5线程间的共享数据,互斥锁

多个线程之间可以共享数据的方法:
1、采用全局变量,全局函数等全局对象
2、堆对象
互斥锁
当多个线程同时访问一块内存,就可能出现数据不完整的问题,互斥锁就是为了解决这个问题的
这里写图片描述
这里写图片描述

阿发你好 线程篇 1.6线程安全的函数

可重入的函数(reentrant)的函数,也叫作线程安全(threadsafe函数) 是指一个函数,在多个线程里同时调用(并发调用)的时候,其功能仍然正常

阿发你好 线程篇 1.7线程间通知机制-信号量

轮询机制
这里写图片描述
这里写图片描述
线程之间的通知机制叫做信号量:semaphore 用于实现线程间的通知机制
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

阿发你好 线程篇 1.8 OSAPI在 qt 中的使用

适合跨平台使用
这里写图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值