精华内容
下载资源
问答
  • #include using namespace std; int repeat(int m) { cout ; if (m>1) { repeat(--m);...递归到终 repeat()函数有return1 返回到repeat(--m) ...为什么不返回repeat(int m) 而是向下走 求详细的答案谢谢
  • 1.先创建个数组 jbyteArray data = env->NewByteArray(1024);// 我这里是byte型数组 1024是数组长度 2.为数组赋值 jbyte* pbuf = (jbyte*)malloc(1024); memcpy(pbuf, fbmpptr, 1024); // fbmpptr为存放内容...

    1.先创建一个数组

    jbyteArray data = env->NewByteArray(1024);// 我这里是byte型数组 1024是数组长度

    2.为数组赋值

    jbyte* pbuf = (jbyte*)malloc(1024);
    memcpy(pbuf, fbmpptr,
    1024); // fbmpptr为存放内容的缓冲区

    env->SetByteArrayRegion(data, 0, 1024, pbuf); 

    free(pbuf);

    展开全文
  • 我这里说明的问题,一般会出现在UCOS系统,如果是裸机应该不怎么会出现,只要程序不是太大,不是无休止的使用内存。 我遇到的现象是,在某个任务运行中进行多层函数调用时在return时无法返回,这种情况一般都是...

    我这里说明的问题,一般会出现在UCOS系统上,如果是裸机应该不怎么会出现,只要程序不是太大,不是无休止的使用内存。

    我遇到的现象是,在某个任务运行中进行多层函数调用时在return时无法返回,这种情况一般都是因为堆栈过小的问题。一般涉及工程中两个地方。

    一个是任务堆栈设置那里,一个是总堆栈设置那里(stm32平台一般是stm32xxxxx.s文件中),任务堆栈设置的位置每个工程可能都不一样,自行寻找修改即可,总堆栈在stm32平台中在下面代码红色所示的位置:

    <span style="color:#ff0000;">Stack_Size      EQU     0x00000D00</span>
    
                    AREA    STACK, NOINIT, READWRITE, ALIGN=3
    Stack_Mem       SPACE   Stack_Size
    __initial_sp
    
    
    ; <h> Heap Configuration
    ;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
    ; </h>
    
    Heap_Size       EQU     0x00000200
    
                    AREA    HEAP, NOINIT, READWRITE, ALIGN=3
    __heap_base
    Heap_Mem        SPACE   Heap_Size
    __heap_limit
    
    把总堆栈和任务堆栈同时调大或者够用的状态无法返回的问题即可解决。也不要太大,免得浪费内存。


    还有一个问题,如果在函数内部使用大的数组,如分配一个4K大小的数组,这样是不行的,当然还是对有操作系统的来说,裸机也有可能会出问题,当然取决于设置的栈大小,这是我的理解,没有做测试。

    在操作系统中不要这么使用,如果是大的数组,那么尽量设置为全局的形式,免得程序莫名其妙的崩溃(我花了好长时间来寻找这个问题~~~~(>_<)~~~~)。
    在使用UCOS这类操作系统时,对内存的使用以及指针的使用要务必格外小心仔细,要不然出现了莫名其妙的问题,很难去定位寻找,因为是多任务运行,这句话有问题,但是引起这句有问题的地方在哪却是未知。






    展开全文
  • !... 图邻接矩阵,DFS遍历,起始k=0。if找到,就输出路径,else如图,isgo不用管,一直是true. !... 然后遍历结果,查询1->5。...感觉等于说找到以后,要退出两层的DFS,但是只返回上一层那个点,然后又从那个点开始遍历**
  • C++教程(最全)

    万次阅读 多人点赞 2020-03-04 15:21:05
    C++简介

    1 C++简介

    1.1 起源

    -贝尔实验室20世纪80年代(1979)

    1.2 应用范围

    • 文字处理程序以及电子表格
    • 编译器
    • 操作系统
    • 大型游戏等

    1.3 C++和C

    • C语言是结构化和模块化的语言,面向过程。
    • C++保留了C语言原有的所有优点,增加了面向对象的机制,俗称“带类的C",1983年更名为C++

    2开发工具

    • 记事本(Notepad++)+命令行
    • Visual C++ 6.0:经典开发工具,与流行操作系统有冲突
    • VS 2015等:功能强大,体积也强大
    • Code::Blocks:开源免费开发工具,专业开发人员推荐使用
    • 其他开发工具:DeV C++、CLion、C-Free、Xcode、C4droid

    3 基本语法

    • 对象-对象具有状态的行为。对象是类的实例。
    • 类-类可以定义为对象行为、状态的模版。
    • 方法-从基本上讲,一个方法表示一种行为,一个类可以包含多种方法。
    • 变量

    3.1 注释

    //单行注释
    /*
    多行注释
    多行注释
    */
    

    3.2关键字

    asm else new this
    auto enum operator throw
    bool explicit private true
    break export protected try
    case extern public typedef
    catch false register typeid
    char float reinterpret_cast typename
    class for return union
    const friend short unsigned
    const_cast goto signed using
    continue if sizeof virtual
    default inline static void
    delete int static_cast volatile
    do long struct wchar_t
    double mutable switch while
    dynamic_cast namespace template

    3.3标识符

    • 标识符是用来标识变量、函数、类、模块,或任何其他用户自定义项目的名称。一个标识符以字母 A-Z 或 a-z 或下划线 _ 开始,后跟零个或多个字母、下划线和数字(0-9)。
    • 标识符内不允许出现标点字符,比如 @、& 和 %。C++ 是区分大小写的编程语言

    4 数据类型

    4.1基本数据类型

    七种基本的C++数据类型:bool、char、int、float、double、void、wchar_t
    类型修饰符:signed、unsigned、short、long
    注:一些基本类型可以使用一个或多个类型修饰符进行修饰,比如:signed short int简写为short、signed long int 简写为long。

    类型名 占用字节数 数值范围
    void 0
    bool 1 {true.false}
    wchar_t 2或4个字节
    char(signed char) 1 -128~+127
    short(signed short) 2 -32768~+32767
    int(signed int) 4 -2147483648~+2147483647
    long(signed long) 4 -2147483648~+2147483647
    long long(signed long long) 8 -9,223,372,036,854,775,808 ~9,223,372,036,854,775,807
    float 4 -.34*1038~3.4*1038
    double 8 -1.7*10308~1.7*10308
    unsigned char 1 0~255
    unsigned shrot 2 0~65525
    unsigned(unsigned int) 4 0~4294967295
    unsigned long 4 0~4294967295
    unsigned long long 8 0 ~ 18,446,744,073,709,551,615
    //x64处理器 64位window10 vs2015 
    #include <iostream>
    using namespace std;
    int main()
    {
    	bool b;
    	char c;short s; int i; long l; long long ll; float f; double d; long double ld;long float lf;
    	unsigned char uc; unsigned short us; unsigned int ui; unsigned long ul; unsigned long long ull;
    	cout << sizeof(bool) <<  endl;
    	cout << sizeof(char)<<" " << sizeof(short)<<" "<< sizeof(signed int) << " " << sizeof(long) << " " << sizeof(signed long long) << " " << sizeof(float) << " " << sizeof(double) << " " << sizeof(long float) << " " << sizeof(long double) << endl;
    	cout <<sizeof(unsigned char)<<" "<< sizeof(unsigned short) << " " << sizeof(unsigned int) << " " << sizeof(unsigned long) << " " << sizeof(unsigned long long) << endl;
    	cout << sizeof(unsigned) << endl;
    	
    	
    	cout << "hello World!!!" <<endl;
    	system("pause");
    	return 0;
    }
    

    输出结果

    4.2 数据类型在不同系统中所占空间大小

          这个与机器、操作系统、编译器有关。比如同样是在32bits的操作系统系,VC++的编译器下int类型为占4个字节;而tuborC下则是2个字节。
    原因:

    • c/c++规定int字长和机器字长相同
    • 操作系统字长和机器字长未必一致
    • 编译器根据操作系统字长来定义int字长
    类型 16位操作系统 32位操作系统 64位操作系统
    char 1 1 1
    char* 2 4 8
    short 2 2 2
    int 2 4 4
    long 4 4 8
    long long 8 8 8

    注:long类型在不同编译器中的占位不一样: 32位时,VC++和GCC都是4字节; 64位时,VC++是4字节,GCC是8字节。

    4.3 typedef声明

    //使用typedef为一个已有的类型取一个新的名字,语法如下:
    typedef type newname
    //eg:
    typedef int feet
    feet distance
    

    4.4 枚举类型

    C++中的一种派生数据类型,它是由用户定义的若干枚举常量的集合;枚举元素是一个整型,枚举型可以隐式的转换为int型,int型不能隐式的转换为枚举型。

    //枚举类型的语法:
    enum 枚举名{
    	标识符[=整型常数], 
         标识符[=整型常数], 
    ... 
        标识符[=整型常数]
    }枚举变量;
    

    如果枚举没有初始化, 即省掉"=整型常数"时, 则从第一个标识符开始;

    • 默认情况下,第一个名称的值为 0,第二个名称的值为 1,第三个名称的值为 2,以此类推。但是,您也可以给名称赋予一个特殊的值,只需要添加一个初始值即可。
      例如:
    enum course {math,chinese,english,physics,chemistry}c;
    c = english;
    cout<<c<<endl;  //2
    //english为1 physics为2 chemistry为3,chinese仍为1,math仍为0
    enum course {math,chinese,english=1,physics,chemistry};
    

    5 变量

    变量其实只不过是程序可操作的存储区的名称。C++ 中每个变量都有指定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上。

    5.1 变量的声明和定义

    • 变量声明向编译器保证变量以给定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。
    • 可以在 C++ 程序中多次声明一个变量,但变量只能在某个文件、函数或代码块中被定义一次。
    • 多个变量赋同一个值时,需要分别赋值。
    int x = y = z = 66;//错误
    int x = 3,y = 3,z = 3;
    int x, y ,z = 3;
    x = y = z;
    

    变量的声明(不分配内存):extern 数据类型 变量名;
    变量的定义:数据类型 变量名1,变量名2,...变量名n;

    // 变量声明
    extern int a, b;
    int main ()
    {
      // 变量定义
      int a, b;
      // 初始化
      a = 23;
      b = 25;
      return 0;
    }
    

    5.2 变量的作用域

    局部变量:在函数或一个代码块内部声明的变量,称为局部变量。它们只能被函数内部或者代码块内部的语句使用。
    全局变量:在所有函数外部定义的变量(通常是在程序的头部),称为全局变量。全局变量的值在程序的整个生命周期内都是有效的。

    • 局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值。
    • 当局部变量被定义时,系统不会对其初始化;定义全局变量时,系统会自动初始化值:int float double 0,char ’\0‘,指针 NULL
    int i = 66;
    int main ()
    {
      int i = 88;
      cout << i<<endl;//8
      return 0;
    }
    float f;
    double d;
    char c;
    int *p;
    int main()
    {
    	cout << i << f << d << c << p << endl;//000 00000000
    	return 0
    }
    

    6 运算符

    • 算术运算符:+ - * / % ++ --
    • 关系运算符:== != < > >= <=
    • 逻辑运算符:&& || !
    • 位运算符:& | ^ ~ << >>
    • 赋值运算符:= += -= *= /= %= <<= >>= &= ^= !=
    • 杂项运算符:
    sizeof            //返回变量的大小,eg:sizeof(a)返回4 a是整型  sizeof(int)
    Condition?X:Y     //三元运算符 Condition为true,值为X,否则值为Y
    ,                //逗号表达式,值为最后一个表达式的值
    .->            //用于引用类、结构和公用体的成员
    Cast            //强制类型转换符  eg:int(2.202)返回2
    &              //指针运算符 返回变量的地址
    *             //指针运算符  指向一个变量
    
    • 运算符优先级
    类别 运算符 结合性
    后缀 () [] -> . ++ - - 从左到右
    一元 + - ! ~ ++ - - (type)* & sizeof 从右到左
    乘除 * / % 从左到右
    加减 + - 从左到右
    移位 << >> 从左到右
    关系 < <= > >= 从左到右
    相等 == != 从左到右
    位与 AND & 从左到右
    位异或 XOR ^ 从左到右
    位或 OR | 从左到右
    逻辑与 AND && 从左到右
    逻辑或 OR || 从左到右
    条件 ?: 从右到左
    赋值 = += -= *= /= %=>>= <<= &= ^= =
    逗号 , 从左到右

    7 语法结构

    7.1 循环结构

    • while
    while(conditon)//0为false,非0为true
    {
    	statement(s);
    }
    
    • for
    for(init;conditon;increment)//0为false,非0或什么也不写为true
    {
    	statement(s);
    }
    

    1.init首先被执,且只会执行一次,也可以不写任何语句。
    2.然后会判断conditon,true执行循环主体,false跳过循环
    3.执行完循环主体,执行increment,跳到2

    int array[5] = { 11, 22, 33, 44, 55 };
    for (int x : array)
    {
    	cout << x << " ";
    }
    cout << endl;
    // auto 类型也是 C++11 新标准中的,用来自动获取变量的类型
    for (auto x : array)
    {
    	cout << x << " ";
    }
    
    • for each
      STL中的for增强循环。
    int a[4] = { 4,3,2,1 };
    for each (int var in a)
    {
    	cout << var << " ";
    }
    

    7.2 判断结构

    • if
    if(expr)
    {
    	statement;//如果expr为true将执行的语句块 
    }
    if(expr)
    {
       statement1;// 如果expr为true将执行的语句块
    }
    else
    {
       statement2;// 如果expr为false将执行的语句
    }
    if(expr1)
    {
       statement1;// 如果expr1为true将执行的语句块
    }
    elseif(expr2)
    {
       statement2;// 如果expr2为true将执行的语句块
    }
    ...
    else
    {
    	statementElse;// 当上面的表达式都为false执行的语句块
    }
    
    • switch
    switch(expression){
        case constant-expression  :
           statement(s);
           break; 
        case constant-expression  :
           statement(s);
           break; 
        // 您可以有任意数量的 case 语句
        default : // 可选的
           statement(s);
    }
    
    • 每个case后满的常量表达式必须各不相同。
    • case语句和default语句出现的顺序对执行结果没有影响。
    • 若case后没有break,执行完就不会判断,继续执行下一个case语句。直到遇到brerak。
    • default后面如果没有case,则break可以省略
    • 多个case可以用一组执行语句
    char c = 'A';
    	switch (c)
    	{
    	
    	case 'A':
    	case 'B':
    	case 'C':
    		cout << "及格了" << endl;
    		break;
    	default:
    		cout << "不及格" << endl;
    	
    	}
    

    7.3 三元运算符

    //如果 Exp1 为真,则计算 Exp2 的值,结果即为整个 ? 表达式的值。如果 Exp1 为假,则计算 Exp3 的值,结果即为整个 ? 表达式的值
    Exp1 ? Exp2 : Exp3;
    

    7.4 预处理命令

    预处理程序(删除程序注释,执行预处理命令等)–>编译器编译源程序

    • 宏定义:#define 标识符 字符串
    • 文件包含:#include<filename> 或者#include“filename”
    • 条件编译
    //如果标识符被#define定义过,执行程序段1,否则执行程序段2
    #ifdef 标识符
    	程序段1
    #else
    	程序段2
    #endif
    //如果标识符没有被#define定义过,执行程序段1,否则执行程序段2
    #ifndef 标识符
    	程序段1
    #else
    	程序段2
    #endif
    //如果表达式为true,执行程序段1,否则执行程序段2
    #if 表达式
    	程序段1
    #else
    	程序段2
    #endif
    

    8 数组

    一些具有相同数据类型或相同属性(类)的数据的集合,用数据名标识,用下标或序号区分各个数据。数组中的数据称为元素。

    8.1一维数组

    定义一维数组的形式:数据类型 数据名[常量表达式]
    初始化的形式:数据类型 数组名[常量表达式] = {初值表};
    为数组的某一个元素赋值:数组名[下标] =值(下标从0开始)
    数组的引用:数组名[下标]

    • 初始化数组时,可以只给部分数组元素赋值
    • 对全部元素数组赋值时,可以不指定数组长度,编译系统会根据初值个数确定数组的长度。
    • static型数组元素不赋初值,系统会自动默认为0。
    int arr1[4] = {1,2,3,4};
    int arr2[4] = { 1,2 };
    int arr[4] = {0];//所有元素为0
    static int arr3[3];
    int arr4[4];
    cout << "arr1:"<<arr1[0] << arr1[1] << arr1[2] << arr1[3] << endl;
    cout << "arr2:" << arr2[0] << arr2[1] << arr2[2] << arr2[3] << endl;
    cout << "arr3:" << arr3[0] << arr3[1] << arr3[2] << arr3[3] << endl;
    cout << "arr4:" << arr4[0] << arr4[1] << arr4[2] << arr4[3] << endl;
    

    在这里插入图片描述

    8.2二维数组

    定义一维数组的形式:数据类型 数据名[常量表达式1][常量表达式2]
    初始化的形式:数据类型 数组名[常量表达式1] [常量表达式2]= {初值表};
    为数组的某一个元素赋值:数组名[行下标][列下标] =值(下标从0开始)
    数组的引用:数组名[行下标][列下标]

    • 将所有数据写在一个花括号内,自动按照数组元素个数在内存中排列的顺序赋值
    • 可对部分元素赋值,其余元素的值自动取0.
    • 定义初始化数组时,可以省略第一维的长度,第二维不能省,系统会自动确认行数
    int arr1[2][3];
    int arr[2][3] = {0];//所有元素为0
    int arr2[2][3] = { {1,2,3},{4,5,6} };
    int arr3[2][3] = { 1,2,3 ,4,5,6 };
    int arr4[2][3] = { {1},{4,6} };
    int arr5[][3] = { 1,2,3 ,4,5,6 };
    

    在这里插入图片描述

    字符数组
    char类型的数组,在字符数组中最后一位为’\0’)时,可以看成时字符串。在C++中定义了string类,在Visual C++中定义了Cstring类。
    字符串中每一个字符占用一个字节,再加上最后一个空字符。如:

    //字符串长度为8个字节,最后一位是'\0'。
    char array[10] = "yuanrui";//yuanrui\0\0\0
    //也可以不用定义字符串长度,如:
    char arr[] = "yuanrui";//yuanrui\0
    

    8.3 指向数组的指针

    指针的概念会在后面详细讲解。

    double *p;
    double arr[10];
    p = arr;//p = &arr[0];
    *(p+3);//arr[3]
    

    8.4 数组与new(动态创建数组)

    一维数组:

    int* arr1 = new int[2];//delete []arr1;
    int* arr2 = new int[3]{ 1,2 };//delete []arr2
    

    二维数组

    int m=2, n=3;
    int** arr3 = new int*[2];//delete []arr3
    for (int i = 0; i < 10; ++i)
    { 
    	arr3[i] = new int[3]; // delete []arr3[i]
    }
    int* arr4 = new int[m*n];//数据按行存储 delete []arr3
    

    8.5 数组与函数

    数组->函数

    • 如果传递二维数组,形参必须制定第二维的长度。
      形式参数是一个指针:void function(int *param)
      形式参数是一个已定义大小的数组:void function(int param[10])
      形式参数是一个未定义大小的数组:void function(int param[])
      二维数组:void function(int a[][3],int size)

    函数返回数组

    • C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。
    int * function();
    int** function();
    

    8.6 获取数组的大小

    • 动态创建(new)的基本数据类型数组无法取得数组大小
    int a[3];
    //第一种方法
    cout<<sizeof(a)/sizeof(a[0])<<endl;
    //第二种方法
    cout << end(a) - begin(a) << endl;
    //二维数组
    int arr[5][3];
    int lines = sizeof(arr) / sizeof(arr[0][0]);
    int row = sizeof(arr) / sizeof(arr[0]);//行
    int col = lines / row;//列
    cout << row << "::"<<col << endl;
    cout << end(arr) - begin(arr) << endl;//5行
    

    9 函数

            函数是实现模块化程序设计思想的重要工具, C++程序中每一项操作基本都是由一个函数来实现的,C++程序中只能有一个主函数(main)

    9.1 函数声明与定义

    • 函数类型-函数的返回值类型;函数名-必须符合C++标识符命名规则,后面必须跟一对括号;函数体-实现函数功能的主题部分;参数列表-函数名后面的括号内,用于向函数传递数值或带回数值。
    • 函数声明中,参数名可以省略,参数类型和函数的类型不能省略。
    • 函数声明可以放在主调函数内部,放在调用语句之前;也可以放在主调函数外,如果位于所有定义函数之前,后面函数定义顺序任意,各个主调函数调用也不必再做声明
    • 当函数定义在前,函数调用灾后,可以不用函数声明。
      后两条总结一下就是:调用函数前,程序得知道有这个函数,声明就是提前让程序知道有这么的玩意
      函数声明:
    函数类型 函数名(参数列表);
    eg:
    int max(int a,int b);//声明函数时,a,b可以省略
    int max(int,int);
    void show();
    

    函数定义:

    函数类型 函数名(参数列表)
    {
    	函数体;
    }
    eg:
    int max(int a,int b)
    {
    	int z;
    	z = a>b?a:b;
    	return z;
    }
    

    9.2 函数的参数与返回值

    • 形参:函数定义后面括号里的参数,函数调用前不占内存。
    • 实参:函数调用括号里的参数,可以是常量,变量或表达式等。
      形参和实参必须个数相同、类型一致,顺序一致
      函数传递方式:传值,指针,引用
          关于指针和引用后面有详细介绍。
    //传值-修改函数内的形式参数对实际参数没有影响
    int add(int value)
    {
    	value++;
    	return value;
    }
    int main()
    {
    	int v = 10;
    	cout << "add() = " << add(v) << endl;//add() = 11
    		cout << "v = " << v << endl;//v = 10
    	return 0;
    }
    //指针-修改形式参数会影响实际参数
    int add(int* pValue)
    {
    	(*pValue)++;
    	return *pValue;
    }
    int main()
    {
    	int v = 10;
    	cout << "add() = " << add(&v) << endl;//add() = 11
    	cout << "v = " << v << endl;//v = 11
    	return 0;
    }
    //引用-修改形式参数会影响实际参数
    int add(int &value)
    {
    	value++;
    	return value;
    }
    int main()
    {
    	int v = 10;
    	cout << "add() = " << add(v) << endl;//add() = 11
    	cout << "v = " << v << endl;//v = 11
    	return 0;
    }
    

    有默认值参数的函数

    int sum(int a, int b=2)
    {
      return (a + b);
    }
     
    int main ()
    {
       cout << "Total value is :" << sum(100, 200);<< endl;//Total value is :300
       cout << "Total value is :" << sum(100);<< endl;//Total value is :102
       return 0;
    }
    

    函数的返回值

    • 返回值通过return给出,return后面跟表达式,且只能放回一个值;如果没有表达式,可以不写return;return后面的括号可有可无。
    • return语句中的表达式类型应与函数类型一致,否则自动转换类型(函数类型决定返回值类型)

    9.3 函数调用

    • 函数可以单独作为一个语句使用。有返回值的函数,可将函数调用作为语句的一部分,利用返回值参与运算。
      函数调用形式:参数传递–>函数体执行–>返回主调函数
    函数名(实参列表);
    show();
    

    函数的嵌套调用:

    int a()
    {
    	return 666;
    }
    int b(int sum)
    {
    	return sum+a()
    }
    int main()
    {
    	cout<<b(222)<<endl;//888
    	return 0;
    }
    

    函数的递归调用:直接递归调用和间接递归调用

    • 一个函数直接或间接递归调用该函数本身,称为函数的递归调用
    • 递归和回归:原问题=>子问题 子问题的解=>原问题的解
    //直接递归调用:求1+...n的值
    int total(int sum)
    {
    	if (sum == 1)
    	{
    		return 1;
    	}
    	return sum + total(sum - 1);
    }
    int main()
    {
    	cout << "total = " << total(10) << endl;//total = 55
    	system("pause");
    	return 0;
    }
    //间接递归调用
    int f2();
    int f1()
    {
    ...
      f2()
    }
    int f2()
    {
    	f1();
    }
    

    9.4 函数重载

        同一个函数名对应不同的函数实现,每一类实现对应着一个函数体,名字相同,功能相同,只是参数的类型或参数的个数不同。
    多个同名函数只是函数类型(函数返回值类型)不同时,它们不是重载函数

    int add(int a,int b)
    {
    	return a+b;
    }
    double add(double a,double b)
    {
    	return a+b;
    }
    int add(int a,int b,int c)
    {
    	return a+b+c;
    }
    

    9.5 内联(inline)函数

        c++在编译时可以讲调用的函数代码嵌入到主调函数中,这种嵌入到主调函数中的函数称为内联函数,又称为内嵌函数或内置函数。

    • 定义内联函数时,在函数定义和函数原型声明时都使用inline,也可以只在其中一处使用,其效果一样。
    • 内联函数在编译时用内联函数函数的函数体替换,所以不发生函数调用,不需要保护现场,恢复现场,节省了开销。
    • 内联函数增加了目标程序的代码量。因此,一般只将函数规模很小且使用频繁的函数声明为内联函数。
    • 当内联函数中实现过于复杂时,编译器会将它作为一个普通函数处理,所以内联函数内不能包含循环语句和switch语句。
      内联函数格式如下:
    inline 函数类型 函数名(形参列表)
    {
    	函数体;
    }
    inline int add(int a, int b)
    {
    	return a + b;
    }
    

    9.6 洞悉内联函数底层原理

    1.使用Visual Studio 2015创建一个C++Win32控制台程序,点击项目->项目属性设置内联函数优化
    在这里插入图片描述
    2.编写内联函数代码,设置断点,debug启动

    #include <iostream>
    #include <string>
    using namespace std;
    inline int add(int a, int b)
    {
    	return a + b;//断点1
    }
    int main()
    {
    	int result = add(12, 34);
    	cout << result << endl;//断点2
    	return 0;
    }
    

    3.调试->窗口->反汇编,然后就能看到编译后的汇编程序

    ...
    		int result = add(12, 34);
    00B620DE  mov         eax,0Ch  
    00B620E3  add         eax,22h  //对eax中和22h中值进行相加,赋值给eax
    00B620E6  mov         dword ptr [result],eax  
    		cout << result << endl;
    00B620E9  mov         esi,esp  
    00B620EB  push        offset std::endl<char,std::char_traits<char> > (0B610A5h)  
    00B620F0  mov         edi,esp  
    00B620F2  mov         eax,dword ptr [result]  
    00B620F5  push        eax  
    00B620F6  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0B6D098h)]  
    00B620FC  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B6D0A8h)]  
    00B62102  cmp         edi,esp  
    00B62104  call        __RTC_CheckEsp (0B611C7h)  
    00B62109  mov         ecx,eax  
    00B6210B  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B6D0ACh)]  
    00B62111  cmp         esi,esp  
    00B62113  call        __RTC_CheckEsp (0B611C7h)   
    		return 0;
    

    4.从汇编代码中可以代码编译后内联函数直接嵌入到主函数中,并且断点1不会执行到,下面是没使用内联函数(去掉inline关键字)的汇编代码:

    int result = add(12, 34);
    00291A4E  push        22h  
    00291A50  push        0Ch  
    00291A52  call        add (02914D8h)  //调用add函数
    00291A57  add         esp,8//移动堆栈指针esp,继续执行主函数
    00291A5A  mov         dword ptr [result],eax  
    		cout << result << endl;
    00291A5D  mov         esi,esp  
    00291A5F  push        offset std::endl<char,std::char_traits<char> > (02910A5h)  
    00291A64  mov         edi,esp  
    00291A66  mov         eax,dword ptr [result]  
    00291A69  push        eax  
    00291A6A  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (029D098h)]  
    		cout << result << endl;
    00291A70  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (029D0A8h)]  
    00291A76  cmp         edi,esp  
    00291A78  call        __RTC_CheckEsp (02911C7h)  
    00291A7D  mov         ecx,eax  
    00291A7F  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (029D0ACh)]  
    00291A85  cmp         esi,esp  
    00291A87  call        __RTC_CheckEsp (02911C7h)  
    		system("pause");
    00291A8C  mov         esi,esp  
    00291A8E  push        offset string "pause" (0299B30h)  
    00291A93  call        dword ptr [__imp__system (029D1DCh)]  
    00291A99  add         esp,4  
    00291A9C  cmp         esi,esp  
    00291A9E  call        __RTC_CheckEsp (02911C7h)  
    		return 0;
    

    从以上代码代码可以看出,在主函数中调用(call)了add函数。
    5.在内联函数中添加几个循环后,编译器就把内联函数当做普通函数看待了,代码如下:

    inline int add(int a, int b)
    {
    	
    	int sum = 0;
    	for (int i = 0; i < 100; i++)
    		a++;
    	for (int i = 0; i < 100; i++)
    	{
    		
    		for (int i = 0; i < 100; i++)
    		{
    			sum++;
    		}
    	}
    	
    	return a + b;
    }
    int main()
    {
    	int result = add(12, 34);
    	cout << result << endl;
    	return 0;
    }
    
    	int result = add(12, 34);
    00181A4E  push        22h  
    00181A50  push        0Ch  
    00181A52  call        add (01814ECh)  ///
    00181A57  add         esp,8  
    00181A5A  mov         dword ptr [result],eax  
    	cout << result << endl;
    00181A5D  mov         esi,esp  
    00181A5F  push        offset std::endl<char,std::char_traits<char> > (01810A5h)  
    00181A64  mov         edi,esp  
    00181A66  mov         eax,dword ptr [result]  
    00181A69  push        eax  
    00181A6A  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (018D098h)]  
    	cout << result << endl;
    00181A70  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (018D0A8h)]  
    00181A76  cmp         edi,esp  
    00181A78  call        __RTC_CheckEsp (01811C7h)  
    00181A7D  mov         ecx,eax  
    00181A7F  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (018D0ACh)]  
    00181A85  cmp         esi,esp  
    00181A87  call        __RTC_CheckEsp (01811C7h)  
    	return 0;
    00181AA3  xor         eax,eax  
    

    10 字符串(string)

    10.1 C风格的字符串(字符数组)

    C风格的字符串实际上是使用 null 字符 ‘\0’ 终止的一维字符数组。

    • 输入字符串长度一定小于已定义的字符数组长度,最后一位是/0终止符号;不然输出时无法知道在哪里结束。
      字符数组的定义和初始化
    char a[5]
    //字符个数不够,补0; 字符个数超过报错
    char str[7] = {'h','e','i','r','e','n'};
    char str[] = {'h','e','i','r','e','n'};
    cin>>str;//输入 输入字符串长度一定小于已定义的字符数组长度
    cout<<str;//输出
    

    字符串的处理函数

    strcat(char s1[],const char s2[]);//将s2接到s1上
    strcpy(char s1[],const char s2[]);//将s2复制到s1上
    strcmp(const char s1[],const char s2[]);//比较s1,s2 s1>s2返回1 相等返回1,否则返回-1
    strlen(char s[]);//计算字符串s的长度 字符串s的实际长度,不包括\0在内
    

    10.2 C++中的字符串(string)

    字符串的定义和初始化

    //定义
    string 变量;
    string str1;
    //赋值
    string str2 = "ShangHai";
    string str3 = str2;
    str3[3] = '2';//对某个字符赋值
    //字符串数组
    string 数组名[常量表达式]
    string arr[3];
    

    字符串的处理函数

    #include <iostream>
    #include <algorithm>
    #include <string>
    string str;//生成空字符串
    string s(str);//生成字符串为str的复制品
    string s(str, strbegin,strlen);//将字符串str中从下标strbegin开始、长度为strlen的部分作为字符串初值
    string s(cstr, char_len);//以C_string类型cstr的前char_len个字符串作为字符串s的初值
    string s(num ,c);//生成num个c字符的字符串
    string s(str, stridx);//将字符串str中从下标stridx开始到字符串结束的位置作为字符串初值
    size()length();//返回string对象的字符个数
    max_size();//返回string对象最多包含的字符数,超出会抛出length_error异常
    capacity();//重新分配内存之前,string对象能包含的最大字符数
    >,>=,<,<=,==,!=//支持string与C-string的比较(如 str<”hello”)。  使用>,>=,<,<=这些操作符的时候是根据“当前字符特性”将字符按字典顺序进行逐一得 比较,string (“aaaa”) <string(aaaaa)。    
    compare();//支持多参数处理,支持用索引值和长度定位子串来进行比较。返回一个整数来表示比较结果,返回值意义如下:0:相等 1:大于 -1:
      
    push_back() 
    insert( size_type index, size_type count, CharT ch );//在index位置插入count个字符ch
    insert( size_type index, const CharT* s );//index位置插入一个常量字符串
    insert( size_type index, const CharT* s, size_type n);//index位置插入常量字符串
    insert( size_type index, const basic_string& str );//index位置插入常量string中的n个字符
    insert( size_type index, const basic_string& str, size_type index_str, size_type n);//index位置插入常量str的从index_str开始的n个字符
    insert( size_type index, const basic_string& str,size_type index_str, size_type count = npos);//index位置插入常量str从index_str开始的count个字符,count可以表示的最大值为npos.这个函数不构成重载 npos表示一个常数,表示size_t的最大值,string的find函数如果未找到指定字符,返回的就是一个npos
    iterator insert( iterator pos, CharT ch );
    iterator insert( const_iterator pos, CharT ch );
    void insert( iterator pos, size_type n, CharT ch );//迭代器指向的pos位置插入n个字符ch
    iterator insert( const_iterator pos, size_type count, CharT ch );//迭代器指向的pos位置插入count个字符ch
    void insert( iterator pos, InputIt first, InputIt last );
    iterator insert( const_iterator pos, InputIt first, InputIt last );
    append()+ 操作符
    //访问string每个字符串
    string s1("yuanrui"); // 调用一次构造函数
    // 方法一: 下标法
    for( int i = 0; i < s1.size() ; i++ )
         cout<<s1[i];
    // 方法二:正向迭代器
    for( string::iterator iter = s1.begin();; iter < s1.end() ; iter++)
         cout<<*iter;
     // 方法三:反向迭代器
    for(string::reverse_iterator riter = s1.rbegin(); ; riter < s1.rend() ; riter++)
         cout<<*riter;
     iterator erase(iterator p);//删除字符串中p所指的字符
    iterator erase(iterator first, iterator last);//删除字符串中迭代器区间[first,last)上所有字符
    string& erase(size_t pos = 0, size_t len = npos);//删除字符串中从索引位置pos开始的len个字符
    void clear();//删除字符串中所有字符
    string& replace(size_t pos, size_t n, const char *s);//将当前字符串从pos索引开始的n个字符,替换成字符串s
    string& replace(size_t pos, size_t n, size_t n1, char c); //将当前字符串从pos索引开始的n个字符,替换成n1个字符c
    string& replace(iterator i1, iterator i2, const char* s);//将当前字符串[i1,i2)区间中的字符串替换为字符串s
    //tolower()和toupper()函数 或者 STL中的transform算法
    string s = "ABCDEFG";
    for( int i = 0; i < s.size(); i++ )
         s[i] = tolower(s[i]);
    transform(s.begin(),s.end(),s.begin(),::tolower);
    size_t find (constchar* s, size_t pos = 0) const;//在当前字符串的pos索引位置开始,查找子串s,返回找到的位置索引,-1表示查找不到子串
    size_t find (charc, size_t pos = 0) const;//在当前字符串的pos索引位置开始,查找字符c,返回找到的位置索引,-1表示查找不到字符
    size_t rfind (constchar* s, size_t pos = npos) const;//在当前字符串的pos索引位置开始,反向查找子串s,返回找到的位置索引,-1表示查找不到子串
    size_t rfind (charc, size_t pos = npos) const;//在当前字符串的pos索引位置开始,反向查找字符c,返回找到的位置索引,-1表示查找不到字符
    size_tfind_first_of (const char* s, size_t pos = 0) const;//在当前字符串的pos索引位置开始,查找子串s的字符,返回找到的位置索引,-1表示查找不到字符
    size_tfind_first_not_of (const char* s, size_t pos = 0) const;//在当前字符串的pos索引位置开始,查找第一个不位于子串s的字符,返回找到的位置索引,-1表示查找不到字符
    size_t find_last_of(const char* s, size_t pos = npos) const;//在当前字符串的pos索引位置开始,查找最后一个位于子串s的字符,返回找到的位置索引,-1表示查找不到字符
    size_tfind_last_not_of (const char* s, size_t pos = npos) const;//在当前字符串的pos索引位置开始,查找最后一个不位于子串s的字符,返回找到的位置索引,-1表示查找不到子串
    sort(s.begin(),s.end());
    substr(pos,n);//返回字符串从下标pos开始n个字符
    strtok()
    char str[] = "I,am,a,student; hello world!";
    const char *split = ",; !";
    char *p2 = strtok(str,split);
    while( p2 != NULL )
    {
        cout<<p2<<endl;
        p2 = strtok(NULL,split);
    }
    

    11 指针和引用

    11.1 指针

    指针是一个变量,其值为另一个变量的地址。即内存位置的直接地址。
    声明的一般形式:

    • 数据类型是指针变量所指向的变量的数据类型,*表示其后的变量为指针变量
    数据类型 *指针变量名;
    int    *ip;    //整型的指针 
    double *dp;    //double 型的指针 
    float  *fp;    //浮点型的指针 
    char   *ch;    //字符型的指针 
    

    指针变量的初始化:

    • &是取地址运算符,&变量名表示变量的地址。
    • 变量的数据类型必须于指针变量的数据类型一致。
    • 为了安全起见,有时会把指针初始化为空指针(NULL或0)
    数据类型 *指针变量名 = &变量名;
    *指针变量名 = &变量名;
    int a;
    int *p = &a;
    int *p2;
    p2 = &a;
    

    指针变量的引用:

    • & 取地址符 * 指针运算符(间接运算符),其后是指针变量,表示该指针变量所指向的变量。
    • & *的优先级是相同的,结合方式都是自左向右。比如 &*p等价于&(*p)。
    int x = 3;
    int y;
    int *p;
    p = &x;
    y = *p;//y = a
    

    指针运算(地址运算)

    • 算术运算(移动指针运算):加减,自增自减。
    • p+n运算得到的地址是p+n*sizeof(数据类型)。
    • 两个相同数据类型的指针可以进行加减运算,一般用于数组的操作中。
    • 关系运算:指针指向同一串连续存储单元才有意义,比如数组。与0比较,判断是不是空指针。
    • 赋值运算:变量地址赋值给指针变量,数组元素地址赋值给指针变量,指针变量赋值给其他指针变量。
    int arr[10],len;
    int *p1 = &arr[2],*p2 = &arr[5];
     len = p2-p1;//arr[2] 和arr[5]之间的元素个数 3
    

    new和delete运算符

    • new-为变量分配内存空间;
    • 可以通过判断new返回的指针的值,判断空间是否分配成功。
    • delete-释放空间
    指针变量 = new 数据类型(初值);
    delete 指针变量;
    delete[] 指针变量;//释放为多个变量分配的地址
    int *ip;
    ip= new int(1);
    delete ip;
    int *ip;
     ip= new int[10];
     for (int i = 0; i < 10;i++)
     {
    	 ip[i] = i;
     }
     delete[] ip;
    int a[3][4] = {0};
    

    指针与数组

    • 数组名是数组的首地址,eg:arr为arr[0]的地址。
    • 访问数组元素:arr[i],(arr+i),(p+i),p[i]
    • 二维数组:arr+i == &arr[i],arr[i] == &arr[i][0] ,*(arr[i]+j) == arr[i][j]
    • 指针访问二维数组:指向二维数组元素,指向一维数组
    • 数组指针:数据类型 (*指针变量名) [m]
    int arr[10];
    int *p1 = arr;// *p1 = &arr[0];
    int a[3][5] = { 0 };
    int(*ap)[5];
    ap = a;
    ap+1;//表示下一个一维数组
    

    指针与字符串

    • 字符串数组名:char ch[] = "heiren";char *p = ch;
    • 字符串:char *p = "heiren";
    • 指针赋值运算:char * p;p = "Heiren";
      指针与函数,指针可以作为函数的参数,也可以作为函数的返回值。

    11.2 引用

    引用可以看做是数据的一个别名,通过这个别名和原来的名字都能够找到这份数据,类似于window中的快捷方式。

    • 引用不占内存空间,必须在定义的同时初始化,且不能再引用其他数据。
    • 引用在定义时需要添加&,在使用时不能添加&,使用时添加&表示取地址
      引用型变量声明:数据类型 &引用名 = 变量名;
    int a;
    int &b = a;//a和b表示相同的变量,具有相同的地址。
    

    引用可以作为函数参数,也可以作为函数返回值。

    void swap(int &r1, int &r2) {
        int temp = r1;
        r1 = r2;
        r2 = temp;
    }
    int &add1(int &r) {
    	r += 1;
    	return r;
    }
    int main()
    {
    	int a = 12;
    	int b = add1(a);
    	cout << a << "   "<<b << endl;//13  13
    	return 0;
    }
    

    将引用作为函数返回值时不能返回局部数据的引用,因为当函数调用完成后局部数据就会被销毁。
    函数在栈上运行,函数掉用完,后面的函数调用会覆盖之前函数的局部数据。

    int &add1(int &r) {
    	r += 1;
    	int res = r;
    	return res;
    }
    void test()
    {
    	int xx = 123;
    	int yy = 66;
    }
    int main()
    {
    	int a = 12;
    	int &b = add1(a);
    	int &c = add1(a);
    	test();//函数调用,覆盖之前函数的局部数据
    	cout << a << "   "<<b <<" "<< c<<endl;//14   -858993460 -858993460
    	return 0;
    }
    

    12 自定义数据类型

    12.1 结构体

    结构体可以包含不同数据类型的结构。
    定义结构体的一般形式

    struct 结构体类型名
    {
    	成员类型1 成员名1;
    	成员类型2 成员名2;
    	... ...
    	成员类型n 成员名n;
    };
    

    结构体变量名的定义和初始化:

    //定义结构体同时声明结构体变量名
    struct 结构体类型名
    {
    	成员类型1 成员名1;
    	成员类型2 成员名2;
    	... ...
    	成员类型n 成员名n;
    }变量名1,变量名2,...变量名n;
    //先定义结构体
    [struct] 结构体类型名 变量名;
    //直接定义
    struct 
    {
    	成员类型1 成员名1;
    	成员类型2 成员名2;
    	... ...
    	成员类型n 成员名n;
    }变量名1,变量名2,...变量名n;
    struct  person
    {
    	int year;
    	int age;
    	string name;
    }p1 = {2019,24,"heiren"}, p1 = { 2020,24,"heiren" };
    struct  person
    {
    	int year;
    	int age;
    	string name;
    };
    struct person p1 = { 2019,24,"heiren" }, p1 = { 2020,24,"heiren" };
    struct 
    {
    	int year;
    	int age;
    	string name;
    }p1 = {2019,24,"heiren"}, p1 = { 2020,24,"heiren" };
    

    结构体变量的使用:

    • 具有相同类型的结构体变量可以进行赋值运算,但是不能输入输出
    • 对结构体变量的成员引用:结构体变量名.成员名
    • 指向结构体的指针变量引用格式:指针变量名->成员名;
      结构体数组的定义,初始化和使用与结构体变量、基本类型数组相似
    struct person
    {
    	int year;
    	int age;
    	string name;
    }p[2] ={ {2019,24,"heiren"}, { 2020,24,"heiren" }};//可以不指定数组元素个数
    p[1].age;
    

    结构体作为函数传递有三种:值传递,引用传递,指针传递

    12.2 结构体大小和字节对齐

        现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐.
        为什么需要字节对齐?各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。
    三个个概念:

    • 自身对齐值:数据类型本身的对齐值,结构体或类的的自身对齐值是其成员中最大的那个值,例如char类型的自身对齐值是1,short类型是2;
    • 指定对齐值:编译器或程序员指定的对齐值,32位单片机的指定对齐值默认是4;
    • 有效对齐值:自身对齐值和指定对齐值中较小的那个。
      字节对齐的三个准则
    • 结构体变量的首地址能够被其有效对齐值的大小所整除
    • 结构体的总大小为结构体有效对齐值的整数倍。
    • 结构体每个成员相对于结构体首地址的偏移量都是有效对齐值的整数倍。
      可以通过#pragma pack(n)来设定变量以n字节对齐方式
      举个例子
    //指定对齐值=8
    struct st 
    {
    	// 空结构体大小1
    	char c1;//1
    	char c2;//2
    	int i1;//8  int  起始地址按照字节对齐的原理应该是它长度4的整数倍
    	char c3;//12
    	short s4;//12 short 起始地址按照字节对齐的原理应该是它长度2的整数倍 12 + 2 = 12
    	double d;//24 double 起始地址按照字节对齐的原理应该是它长度8的整数倍 12->16 + 8 = 24
    	char c4;//32   24 + 4 = 28 结构体的总大小为8的整数倍 28->32
    	int i2;//32  28+4 = 32
    	int i3;//40   
    	short s5;//40
    };
    cout << sizeof(st) << endl;//40
    //指定对齐值=4
    #pragma  pack(4)
    struct st
    {
    	//1 空结构体大小1
    	char c1;//1
    	char c2;//2
    	int i1;//8
    	char c3;//12
    	short s4;//12
    	double d;//20
    	char c4;//24
    	int i2;//28
    	int i3;//32
    	short s5;//36
    }s;
    cout << sizeof(st) << endl;//36
    

    字节对齐

    12.3 公用体(union)

    几个不同的变量共享同一个地址开始的内存空间。

    • 成员类型可以是基本数据类型,也可以是构造数据类型。
    • 公用体变量初始化时,只能对第一个成员赋值。
    • 公用体变量所占的内存长度等于最长的成员长度。
    • 公用体变量在一个时刻只能一个成员发挥作用,赋值时,成员之间会互相覆盖,最后一次被赋值的成员起作用。
      定义
    union 共同体类型名
    {
        成员类型1 成员名1;
    	成员类型2 成员名2;
    	... ...
    	成员类型n 成员名n;
    };
    

    初始化

    union data
    {
    	int i;
    	float f;
    	char c;
    }x = {123};
    union data
    {
    	float f;
    	int i;
    	char c;
    };
    data x = {12.3};
    union 
    {
    	char c;
    	int i;
    	float f;
    }x = {’y‘};
    

    引用

    共同体变量名.成员名;
    union data
    {
    	int i;
    	float f;
    	char c;
    }x = {12};
    int main()
    {
    	cout << x.i << " " << x.f << " " << x.c << endl;//12 1.68156e-44 
    	x.c = 'c';
    	cout << x.i <<" "<< x.f << " " << x.c << endl;//99 1.38729e-43 c
    	return 0;
    }
    

    12.4 枚举(enum)和typedef声明

    枚举已经在前面的章节介绍过,这里就不在赘述了。
    typedef-为已存在的数据类型定义一个新的类型名称,不能定义变量
    typedef声明格式:typedef 类型名称 类型标识符;

    typedef char *CP;
    typedef int INTEGER;
    

    13 面向对象

    13.1 类

    • 类也是一种数据类型。

    类的声明:

    class 类名
    {
    	public:
    		公有数据成员;
    		公有成员函数;
    	private:
    		私有数据成员;
    		私有成员函数;
    	protected:
    		保护数据成员;
    		保护成员函数;
    };
    

    成员函数的定义:类内,类外,类外内联函数

    //类外
    返回类型 类名:成员函数名(参数列表)
    {
    	函数体;
    }
    //内联函数:类外
    inline 返回类型 类名:成员函数名(参数列表)
    {
    	函数体;
    }
    

    内联函数的代码会直接嵌入到主调函数中,可以节省调用时间,如果成员函数在类内定义,自动为内联函数。

    13.2 类成员的访问权限以及类的封装

    • 和Java、C#不同的是,C++中public、private、protected只能修饰类的成员,不能修饰类,C++中的类没有共有私有之分
    • 类内部没有访问权限的限制,都可以互相访问。
    • 在C++中用class定义的类中,其成员的默认存取权限是private。
    类外 派生类 类内
    public Y Y Y
    protected N Y Y
    private N N Y

    13.3 对象

    //1.声明类同时定义对象
    class 类名
    {
    	类体;
    }对象名列表;
    //2.先声明类,再定义对象
    类名 对象名(参数列表);//参数列表为空时,()可以不写
    //3. 不出现类名,直接定义对象
    class 
    {
    	类体;
    }对象名列表;
    //4.在堆上创建对象
    Person p(123, "yar");//在栈上创建对象
    Person *pp = new Person(234,"yar");//在堆上创建对象
    

    注:不可以在定义类的同时对其数据成员进行初始化,因为类不是一个实体,不合法但是能编译运行
    对象成员的引用:对象名.数据成员名 或者 对象名.成员函数名(参数列表)

    13.4 构造函数

    是一种特殊的成员函数,主要功能是为对象分配存储空间,以及为类成员变量赋初值

    • 构造函数名必须与类名相同
    • 没有任何返回值和返回类型
    • 创建对象自动调用,不需要用户来调用,且只掉用一次
    • 类没有定义任何构造函数,编译系统会自动为这个类生成一个默认的无参构造函数
      构造函数定义
    //1.类中定义 2.类中声明,类外定义
    [类名::]构造函数名(参数列表)
    {
    	函数体
    }
    

    创建对象

    类名 对象名(参数列表);//参数列表为空时,()可以不写
    

    带默认参数的构造函数

    class Person
    {
    public:
    	Person(int = 0,string = "张三");
    	void show();
    private:
    	int age;
    	string name;
    };
    Person::Person(int a, string s)
    {
    	cout<<a<<" "<<s<<endl;
    	age = a;
    	name = s;
    }
    void Person::show()
    {
    	cout << "age="<<age << endl;
    	cout << "name=" <<name << endl;
    }
    int main()
    {
         Person p; //0 张三
    	Person p2(12);//12 张三
    	Person p3(123, "yar");//123 yar
    	return 0;
    }
    

    带参数初始化表的构造函数

    类名::构造函数名(参数列表):参数初始化表
    {
    	函数体;
    }
    参数初始化列表的一般形式:
    参数名1(初值1),参数名2(初值2),...,参数名n(初值n)
    class Person
    {
    public:
    	Person(int = 0,string = "张三");
    	void show();
    private:
    	int age;
    	string name;
    };
    Person::Person(int a, string s):age(a),name(s)
    {
    	cout << a << " " << s << endl;
    }
    

    构造函数重载:构造函数名字相同,参数个数和参数类型不一样。

    class Person
    {
    public:
    	Person();
    	Person(int = 0,string = "张三");
    	Person(double,string);
    	void show();
    private:
    	int age;
    	double height;
    	string name;
    };
    ...
    

    拷贝构造函数

    类名::类名(类名&对象名)
    {
    	函数体;
    }
    class Person
    {
    public:
    	Person(Person &p);//声明拷贝构造函数
    	Person(int = 0,string = "张三");
    	void show();
    private:
    	int age;
    	string name;
    };
    Person::Person(Person &p)//定义拷贝构造函数
    {
    	cout << "拷贝构造函数" << endl;
    	age = 0;
    	name = "ABC";
    }
    Person::Person(int a, string s):age(a),name(s)
    {
    	cout << a << " " << s << endl;
    }
    int main()
    {
    	Person p(123, "yar");
    	Person p2(p);
    	p2.show();
    	return 0;
    }
    //输出
    123 yar
    拷贝构造函数
    age=0
    name=ABC
    

    13.5 析构函数

    是一种特殊的成员函数,当对象的生命周期结束时,用来释放分配给对象的内存空间爱你,并做一些清理的工作。

    • 析构函数名与类名必须相同。
    • 析构函数名前面必须加一个波浪号~。
    • 没有参数,没有返回值,不能重载。
    • 一个类中只能有一个析构函数。
    • 没有定义析构函数,编译系统会自动为和这个类生成一个默认的析构函数。
      析构函数的定义:
    //1.类中定义 2.类中声明,类外定义
    [类名::]~析构函数名()
    {
    	函数体;
    }
    

    13.6 对象指针

    对象指针的声明和使用

    类名 *对象指针名;
    对象指针 = &对象名;
    //访问对象成员
    对象指针->数据成员名
    对象指针->成员函数名(参数列表)
    Person p(123, "yar");
    Person* pp = &p;
    Person* pp2 = new Person(234,"yar")
    pp->show();
    

    指向对象成员的指针

    数据成员类型 *指针变量名 = &对象名.数据成员名;
    函数类型 (类名::*指针变量名)(参数列表);
    指针变量名=&类名::成员函数名;
    (对象名.*指针变量名)(参数列表);
    Person p(123, "yar");
    void(Person::*pfun)();
    pfun = &Person::show;
    (p.*pfun)();
    

    this指针
    每个成员函数都有一个特殊的指针this,它始终指向当前被调用的成员函数操作的对象

    class Person
    {
    public:
    	Person(int = 0,string = "张三");
    	void show();
    private:
    	int age;
    	string name;
    };
    Person::Person(int a, string s):age(a),name(s)
    {
    	cout << a << " " << s << endl;
    }
    void Person::show()
    {
    	cout << "age="<<this->age << endl;
    	cout << "name=" <<this->name << endl;
    }
    

    13.7 静态成员

    以关键字static开头的成员为静态成员,多个类共享。

    • static 成员变量属于类,不属于某个具体的对象
    • 静态成员函数只能访问类中静态数据成员
      静态数据成员
    //类内声明,类外定义
    class xxx
    {
    	static 数据类型 静态数据成员名;
    }
    数据类型 类名::静态数据成员名=初值
    //访问
    类名::静态数据成员名;
    对象名.静态数据成员名;
    对象指针名->静态数据成员名;
    

    静态成员函数

    //类内声明,类外定义
    class xxx
    {
    	static 返回值类型 静态成员函数名(参数列表);
    }
    返回值类型 类名::静态成员函数名(参数列表)
    {
    	函数体;
    }
    //访问
    类名::静态成员函数名(参数列表);
    对象名.静态成员函数名(参数列表);
    对象指针名->静态成员函数名(参数列表);
    

    13.8 友元

    借助友元(friend),可以使得其他类中得成员函数以及全局范围内得函数访问当前类得private成员。
    友元函数

    • 友元函数不是类的成员函数,所以没有this指针,必须通过参数传递对象。
    • 友元函数中不能直接引用对象成员的名字,只能通过形参传递进来的对象或对象指针来引用该对象的成员。
    //1.将非成员函数声明为友元函数
    class Person
    {
    public:
    	Person(int = 0,string = "张三");
    	friend void show(Person *pper);//将show声明为友元函数
    private:
    	int age;
    	string name;
    };
    Person::Person(int a, string s):age(a),name(s)
    {
    	cout << a << " " << s << endl;
    }
    void show(Person *pper)
    {
    	cout << "age="<< pper->age << endl;
    	cout << "name=" << pper->name << endl;
    }
    int main()
    {;
    	Person *pp = new Person(234,"yar");
    	show(pp);
    	system("pause");
    	return 0;
    }
    //2.将其他类的成员函数声明为友元函数
    //person中的成员函数可以访问MobilePhone中的私有成员变量
    class MobilePhone;//提前声明
    //声明Person类
    class Person
    {
    public:
    	Person(int = 0,string = "张三");
    	void show(MobilePhone *mp);
    private:
    	int age;
    	string name;
    };
    //声明MobilePhone类
    class MobilePhone
    {
    public:
    	MobilePhone();
    	friend void Person::show(MobilePhone *mp);
    private:
    	int year;
    	int memory;
    	string name;
    };
    MobilePhone::MobilePhone()
    {
    	year = 1;
    	memory = 4;
    	name = "iphone 6s";
    }
    Person::Person(int a, string s):age(a),name(s)
    {
    	cout << a << " " << s << endl;
    }
    void Person::show(MobilePhone *mp)
    {
    	cout << mp->year << "年  " << mp->memory << "G " << mp->name << endl;
    }
    int main()
    {
    	Person *pp = new Person(234,"yar");
    	MobilePhone *mp = new MobilePhone;
    	pp->show(mp);
    	system("pause");
    	return 0;
    }
    

    友元类
    当一个类为另一个类的友元时,称这个类为友元类。 友元类的所有成员函数都是另一个类中的友元成员
    语法形式:friend [class] 友元类名

    • 类之间的友元关系不能传递
    • 类之间的友元关系是单向的
    • 友元关系不能被继承
    class HardDisk
    {
    public:
    	HardDisk();
    	friend class Computer;
    private:
    	int capacity;
    	int speed;
    	string brand;
    };
    HardDisk::HardDisk():capacity(128),speed(0),brand("三星"){
    }
    class Computer
    {
    public:
    	Computer(HardDisk hd);
    	void start();
    private:
    	string userName;
    	string name;
    	int ram;
    	string cpu;
    	int osType;
    	HardDisk hardDisk;
    	
    };
    Computer::Computer(HardDisk hd):userName("yar"),name("YAR-PC"),ram(16),cpu("i7-4710"),osType(64)
    {
    	cout << "正在创建computer..." << endl;
    	this->hardDisk = hd;
    	this->hardDisk.speed = 5400;
    	cout << "硬盘转动...speed = " << this->hardDisk.speed << "转/分钟" << endl;
    	
    }
    void Computer::start()
    {
    	
    	cout << hardDisk.brand << " " << hardDisk.capacity << "G" << hardDisk.speed << "转/分钟" << endl;
    	cout << "笔记本开始运行..." << endl;
    }
    int main()
    {
    	HardDisk hd;
    	Computer cp(hd);
    	cp.start();
    	system("pause");
    	return 0;
    }
    

    13.9 类(class)与结构体(struct)的区别

    • 引入C语言的结构体,是为了保证和c程序的兼容性。
    • c语言中的结构体不允许定义函数成员,且没有访问控制权限的属性。
    • c++为结构体引入了成员函数,访问控制权限,继承,多态等面向对象特性。
    • c语言中,空结构体的大小为0,而C++中空结构体大小为1。
    • class中成员默认是private,struct中的成员默认是public。
    • class继承默认是private继承,而struct继承默认是public继承。
    • class可以使用模版,而struct不能。
      举个例子:
    //结构体默认权限为public
    struct person
    {
    	void show();
    	string name;
    	int age;
    };
    int main()
    {
    	person p;
    	p.name = "heiren";
    	p.age = 666;
    	p.show();
    	cout <<"name="<< p.name <<" age="<< p.age << endl;
    	system("pause");
    	return 0;
    }
    

    将struct改为class,运行报错。

    14 继承和派生

    14.1 继承和派生概述

    继承就是再一个已有类的基础上建立一个新类,已有的类称基类或父类,新建立的类称为派生类和子类;派生和继承是一个概念,角度不同而已,继承是儿子继承父亲的产业,派生是父亲把产业传承给儿子。

    • 一个基类可以派生出多个派生类,一个派生类可以继承多个基类
      派生类的声明:
    //继承方式为可选项,默认为private,还有public,protected
    class 派生类名:[继承方式]基类名
    {
    	派生类新增加的成员声明;
    };
    

    继承方式:

    • public-基类的public成员和protected成员的访问属性保持不变,私有成员不可见。
    • private-基类的public成员和protected成员成为private成员,只能被派生类的成员函数直接访问,私有成员不可见。
    • protected-基类的public成员和protected成员成为protected成员,只能被派生类的成员函数直接访问,私有成员不可见。
    继承方式/基类成员 public成员 protected成员 private成员
    public public protected 不可见
    protected protected protected 不可见
    private private private 不可见
    • 利用using关键字可以改变基类成员再派生类中的访问权限;using只能修改基类中public和protected成员的访问权限。
    class Base
    {
    public:
    	void show();
    protected:
    	int aa;
    	double dd;
    };
    void Base::show(){
    }
    class Person:public Base
    {
    public:
    	using Base::aa;//将基类的protected成员变成public
    	using Base::dd;//将基类的protected成员变成public
    private:
    	using Base::show;//将基类的public成员变成private
    	string name;
    };
    int main()
    {
    	Person *p = new Person();
    	p->aa = 12;
    	p->dd = 12.3;
    	p->show();//出错
    	delete p;
    	return 0;
    }
    

    派生类的构造函数和析构函数

    • 先执行基类的构造函数,随后执行派生类的构造函数
    • 先执行派生类的析构函数,再执行基类的析构函数。
    • 派生类的构造函数:派生类名(总参数列表):基类名(基类参数列表),子对象名1(参数列表){构造函数体;}
    class Base
    {
    public:
    	Base(int, double);
    	~Base();
    private:
    	int aa;
    	double dd;
    };
    Base::Base(int a, double d) :aa(a), dd(d)
    {
    	cout << "Base Class 构造函数!!!" << endl;
    }
    Base::~Base()
    {
    	cout << "Base Class 析构函数!!!" << endl;
    }
    class Person:public Base
    {
    public:
    	Person(int,double,string);
    	~Person();
    private:
    	string name;
    };
    Person::Person(int a,double d,string str):Base(a,d),name(str)
    {
    	cout << "Person Class 构造函数!!!" << endl;
    }
    Person::~Person()
    {
    	cout << "Person Class 析构函数!!!" << endl;
    }
    int main()
    {
    	cout << "创建Person对象..." << endl;
    	Person *p = new Person(1,2,"yar");
    	cout << "删除Person对象...." << endl;
    	delete p;
    	system("pause");
    	return 0;
    }
    

    输出结果

    14.2 多继承

    一个派生类同时继承多个基类的行为。

    多继承容易让代码逻辑复杂、思路混乱,一直备受争议,中小型项目中较少使用,后来的 Java、C#、PHP 等干脆取消了多继承。

    多重继承派生类声明的一般形式:

    class 派生类名:继承方式1 基类1,继承方式2 基类2
    {
    	派生类主体;
    };
    

    多重继承派生类的构造函数:

    派生类名(总参数列表):基类名1(基类参数列表1),基类名2(基类参数列表2),
    子对象名1,...(参数列表)
    {
    	构造函数体;
    }`
    

    二义性问题:多个基类中有同名成员,出现访问不唯一的问题。

    • 1.类名::同名成员名;
    • 2.派生类定义同名成员,访问的就是派生类同名成员。

    14.3 虚基类

    c++引入虚基类使得派生类再继承间接共同基类时只保留一份同名成员。

    • 虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class)。
    • 派生类的 同名成员 比虚基类的 优先级更高
      虚基类的声明:class 派生类名:virtual 继承方式 基类名
    class  A//虚基类
    {
    protected:
    	int a;
    };
    class B: virtual public A
    {
    protected:
    	int b;
    };
    class C:virtual public A
    {
    protected:
    	int c;
    };
    class D:public B,public C
    {
    protected:
    	int d;
    	void show()
    	{
    		b = 123;
    		c = 23;
    		a = 1;
    	}
    };
    
    • 如果 B 或 C 其中的一个类定义了a,也不会有二义性,派生类的a 比虚基类的a 优先级更高。
    • 如果 B 和 C 中都定义了 a,那么D直接访问a 将产生二义性问题。
      应用:c++中的iostream , istream , ostream,base_io

    15 多态和虚函数

    15.1 向上转型

    数据类型的转换,编译器会将小数部分直接丢掉(不是四舍五入)

    int a = 66.9;
    printf("%d\n", a);//66
    float b = 66;
    printf("%f\n", b);//66.000000
    
    • 只能将将派生类赋值给基类(C++中称为向上转型): 派生类对象赋值给基类对象、将派生类指针赋值给基类指针、将派生类引用赋值给基类引用
    • 派生类对象赋值给基类对象,舍弃派生类新增的成员;派生类指针赋值给基类指针,没有拷贝对象的成员,也没有修改对象本身的数据,仅仅是改变了指针的指向;派生类引用赋值给基类引用,和指针的一样。、

    上转型后通过基类的对象、指针、引用只能访问从基类继承过去的成员(包括成员变量和成员函数),不能访问派生类新增的成员

    15.2 多态

    不同的对象可以使用同一个函数名调用不同内容的函数。

    • 静态多态性-在程序编译时系统就决定调用哪个函数,比如函数重载和静态多态性
    • 动态多态性-在程序运行过程中动态确定调用那个函数,通过虚函数实现的。

    15.3 虚函数

    实现程序多态性的一个重要手段,使用基类对象指针访问派生类对象的同名函数。

    • 将基类中的函数声明为虚函数,派生类中的同名函数自动为虚函数。
    • 声明形式:virtual 函数类型 函数名 (参数列表);
    • 构造函数不能声明为虚函数,析构函数可以声明为虚函数。
    class  A
    {
    public:
    	virtual void show()
    	{
    		cout << "A show" << endl;
    	}
    };
    class B:  public A
    {
    public:
    	void show()
    	{
    		cout << "B show" << endl;
    	}
    };
    int main()
    {
    	
    	B b;
    	b.show();//B show
    	A *pA = &b;
    	pA->show();//B show 如果show方法前没用virtual声明为虚函数,这里会输出A show
    	
    	system("pause");
    	return 0;
    }
    

    15.4 纯虚函数

    在基类中不执行具体的操作,只为派生类提供统一结构的虚函数,将其声明为虚函数。

    class  A
    {
    public:
    	virtual void show() = 0;
    };
    class B:  public A
    {
    public:
    	void show()
    	{
    		cout << "B show" << endl;
    	}
    };
    

    抽象类:包含纯虚函数的类称为抽象类。由于纯虚函数不能被调用,所以不能利用抽象类创建对象,又称抽象基类。

    16 运算符重载

          所谓重载,就是赋予新的含义。函数重载(Function Overloading)可以让一个函数名有多种功能,在不同情况下进行不同的操作。运算符重载(Operator Overloading)也是一个道理,同一个运算符可以有不同的功能。

    • 运算符重载是通过函数实现的,它本质上是函数重载。

    允许重载的运算符

    运算符名称 运算符
    双目算术运算符 +、-、*、、、%
    关系运算符 ==、!=、<、>、<=、>=
    逻辑运算符 ||、&&、!
    单目运算符 +、-、*(指针)、&(取地址)
    自增自减运算符 ++、–
    位运算符 |、&、-、……、<<、>>
    赋值运算符 =、+=、-=、*=、/=、%=、&=、!=、^=、<<= 、>>=
    空间分配和释放 new、delete、new[]、delete[]
    其他运算符 ()(函数调用) 、->(成员访问)、->*(成员指针访问)、,(逗号)、
    • 不允许重载的运算符
    运算符名称 运算符
    成员访问运算符 .
    成员指针访问运算符 . *
    域运算符 ::
    长度运算符 sizeof()
    条件运算符 ?:

    16.1 定义

    重载运算符遵循的规则:

    • 不可以自己定义新的运算符,只能对已有的C++运算符重载。
    • 不能改变运算符运算对象的个数。
    • 不能改变运算符的优先级和结合性
    • 应与标准类型运算功能相似,避免影响可读性。
      一般格式:
    函数类型 operator运算符(参数列表)
    {
    	函数体
    }
    //举个栗子:定义一个向量类,通过运算符重载,可以用+进行运算。
    class Vector3
    {
    public:
    	Vector3();
    	Vector3(double x,double y,double z);
    public:
    	Vector3 operator+(const Vector3 &A)const;
    	void display()const;
    private:
    	double m_x;
    	double m_y;
    	double m_z;
    };
    Vector3::Vector3() :m_x(0.0), m_y(0.0), m_z(0.0) {}
    Vector3::Vector3(double x, double y,double z) : m_x(x), m_y(y), m_z(z) {}
    //运算符重载
    Vector3 Vector3::operator+(const Vector3 &A) const
    {
    	Vector3 B;
    	B.m_x = this->m_x + A.m_x;
    	B.m_y = this->m_y + A.m_y;
    	B.m_z = this->m_z + A.m_z;
    	return B;
    }
    void  Vector3::display()const
    {
    	cout<<"(" << m_x << "," << m_y << "," << m_z << ")" << endl;
    }
    

    16.2 形式

    运算符重载的形式有两种:重载函数作为类的成员,重载函数作为类的友元函数
    根据运算符操作数的不同:双目运算符作为类成员函数,单目运算符作为类的成员函数,双目运算符作为类的友员函数,单目运算符作为类的友元函数。

    • 双目运算符作为友元函数时需要制定两个参数。
    • 运算符重载函数作为类成员函数可以显式调用。
    class Vector3
    {
    public:
    	Vector3();
    	Vector3(double x,double y,double z);
    public:
    	Vector3 operator+(const Vector3 &A)const;
    	Vector3 operator++();
    	friend Vector3 operator-(const Vector3 &v1, const Vector3 &v2);
    	friend Vector3 operator--(Vector3 &v);
    	void display()const;
    private:
    	double m_x;
    	double m_y;
    	double m_z;
    };
    Vector3::Vector3() :m_x(0.0), m_y(0.0), m_z(0.0) {}
    Vector3::Vector3(double x, double y,double z) : m_x(x), m_y(y), m_z(z) {}
    //运算符重载
    Vector3 Vector3::operator+(const Vector3 &A) const
    {
    	Vector3 B;
    	B.m_x = this->m_x + A.m_x;
    	B.m_y = this->m_y + A.m_y;
    	B.m_z = this->m_z + A.m_z;
    	return B;
    }
    Vector3 Vector3::operator++()
    {
    	this->m_x ++;
    	this->m_y ++;
    	this->m_z ++;
    	return *this;
    }
    void  Vector3::display()const
    {
    	cout<<"(" << m_x << "," << m_y << "," << m_z << ")" << endl;
    }
    Vector3 operator-(const Vector3 &v1,const Vector3 &v2)
    {
    	Vector3 B(v1.m_x - v2.m_x, v1.m_y - v2.m_y, v1.m_z - v2.m_z);
    	return B;
    }
    Vector3 operator--( Vector3 &v)
    {
    	v.m_x--;
    	v.m_y--;
    	v.m_z --;
    	return v;
    }
    int main()
    {
    	Vector3 v1(1, 2, 3);
    	Vector3 v2(2, 3, 2);
    	
    	++v1;//v1.operator++(); 作为类成员函数可以显式调用
    	v1.display();
    	--v2;
    	v2.display();
    	Vector3 v3 = v1 + v2;// v1.operator+(v2);作为类成员函数可以显式调用
    	v3.display();
    	Vector3 v4 = v1 - v2;
    	v4.display();
    	return 0;
    }
    

    16.3 常用运算符的重载

    1.自增自减:

    //前置运算符 ++a --a
    operator++()
    operator--()
    operator++(Vector3 &v)
    operator--(Vector3 &v)
    //后置运算符 a-- a++
    operator++(int)
    operator--(int)
    operator++(Vector3 &v,int)
    operator--(Vector3 &v,int)
    

    2.赋值运算符:

    String& String::operator=(String &s)
    {
      if(this!=&s)
      {
      		delete[] str;
      		int length = strlen(s.str);
      		str = new char[length+1];
      		strcpy(str,s.str);
      }
      return (*this)
    }
    

    3.输入\输出运算符重载

     friend ostream &operator<<( ostream &output, 
                                           const Vector3 &v )
          { 
             output << "F : " <<v.m_x<< " I : " << v.m_y<<v.m_z;
             return output;            
          }
     
          friend istream &operator>>( istream  &input, Vector3 &v )
          { 
             input >> v.m_x>> v.m_y>>v.m_z;
             return input;            
          }
    

    16.4 实现类型转换

    • 不指定函数类型和参数,返回值的类型由类型名来确定。
    • 类型转换函数只能作为成员函数,不能作为友元函数。
      类型转换函数的一般形式:
    operator 类型名()
    {
    	转换语句;
    }
    class Vector3
    {
    public:
    	Vector3();
    	Vector3(double x,double y,double z);
    public:
    	Vector3 operator+(const Vector3 &A)const;
    	Vector3 operator++();
    	friend Vector3 operator-(const Vector3 &v1, const Vector3 &v2);
    	friend Vector3 operator--(Vector3 &v,int);
    	operator double()
    	{
    		return m_x + m_y + m_z;
    	}
    	void display()const;
    private:
    	double m_x;
    	double m_y;
    	double m_z;
    };
    int main()
    {
    	Vector3 v1(1, 2, 3);
    	double d = v1;
    	cout << d << endl;//6
    	return 0;
    }
    

    17 IO流

    流-一连串连续不断的数据集合。

    17.1 流类和对象

    • 输入流-从输入设备流向内存的流。
    • 输出流-从内存流出设备的流。
    • 内存缓冲区-用来存放流中的数据。

    输入输出流程:键盘输入=》键盘缓冲区=(回车触发)》程序的输入缓冲区=》‘>>’提取数据
                            输出缓冲区=(缓冲满或endl)》‘<<’送到 显示器显示
    输入/输出流类:
    iostream:ios ,istream,ostream,iostream
    fstream:ifstream,ofstream,fstream
    strstream:istrstream,ostrstream,strstream

    • istream 是用于输入的流类,cin 就是该类的对象。
    • ostream 是用于输出的流类,cout 就是该类的对象。
    • ifstream 是用于从文件读取数据的类。
    • ofstream 是用于向文件写入数据的类。
    • iostream 是既能用于输入,又能用于输出的类。
    • fstream 是既能从文件读取数据,又能向文件写入数据的类。
    • istrstream 输入字符串类
    • ostrstream 输出字符串类
    • strstream 输入输出字符串流类
      在这里插入图片描述

    17.2 标准输入输出流

    C++的输入/输出流库(iostream)中定义了4个标准流对象:cin(标准输入流-键盘),cout(标准输出流-屏幕),cerr(标准错误流-屏幕),clog(标准错误流-屏幕)

    • cerr 不使用缓冲区,直接向显示器输出信息;而输出到 clog 中的信息会先被存放到缓冲区,缓冲区满或者刷新时才输出到屏幕。
    • cout 是 ostream 类的对象,ostream 类的无参构造函数和复制构造函数都是私有的,所以无法定义 ostream 类的对象。
    • 使用>>提取数据时,系统会跳过空格,制表符,换行符等空白字符。所以一组变量输入值时,可用这些隔开。
    • 输入字符串,也是跳过空白字符,会在串尾加上字符串结束标志\0。
    int  x;
    double y;
    cin>>x>>y;
    //输入 22 66.0  两个数之间可以用空格、制表符和回车分隔数据
    char str[10];
    cin>>str;//hei ren  字符串中只有hei\0
    

    输入流中的成员函数

    • get函数:cin.get(),cin.get(ch)(成功返回非0值,否则返回0),cin.get(字符数组(或字符指针),字符个数n,终止字符)
    char c = cin.get();//获取一个字符
    while ((c = cin.get()) != EOF)//循环读取,直到换行
    {
    	cout << c;
    }
    
    
    char ch;
    cin.get(ch);
    while (cin.get(ch))//读取成功循环
    {
    	cout << ch;
    }
    
    char arr[5];
    cin.get(arr, 5, '\n');//输入 heiren  结果 heir\0
    
    • getline函数:cin.getline(字符数组(或字符指针),字符个数n,终止标志字符)
      读取字符知道终止字符或者读取n-1个字符,赋值给指定字符数组(或字符指针)
    char arr0[30],arr1[30],arr2[40];
    cin>>arr0;//遇到空格、制表符或回车结束  "Heiren"
    cin.getline(arr1,30);//字符数最多为29个,遇到回车结束 " Hello World"
    cin.getline(arr2,40,'*');//最多为39个,遇到*结束 "yar"
    //输入 Heiren Hello World
    //yar*123
    
    • cin.peek() 不会跳过输入流中的空格、回车符。在输入流已经结束的情况下,cin.peek() 返回 EOF。
    • ignore(int n =1, int delim = EOF)
    int n;
     cin.ignore(5, 'Y');//跳过前5个字符或Y之前的字符,‘Y’优先
     cin >> n;
     //输入1234567  ->  67    1234567Y345->345
    
    //输入2020.2.23
    int year,month,day;
    cin >> year ;
    cin.ignore() >> month ; //用ignore跳过 '.'
     cin.ignore() >> day;
    cin.ignore();   //跳过行末 '\n'
    
     cout<< setfill('0') << setw(2) << month ;//设置填充字符'\0',输出宽度2
     cout << "-" << setw(2) << day << "-" << setw(4) << year << endl;
    
    • putback(char c),可以将一个字符插入输入流的最前面。

    输出流对象

    • 插入endl-输出所有数据,插入换行符,清空缓冲区
    • \n-输出换行,不清空缓冲区
    • cout.put(参数) 输出单个字符(可以时字符也可以是ASII码)

    格式化输出
    iomanip 中定义的流操作算子:

    • *不是算子的一部分,星号表示在没有使用任何算子的情况下,就等效于使用了该算子,例如,在默认情况下,整数是用十进制形式输出的,等效于使用了 dec 算子
    流操纵算子 作 用
    *dec 以十进制形式输出整数 常用
    hex 以十六进制形式输出整数
    oct 以八进制形式输出整数
    fixed 以普通小数形式输出浮点数
    scientific 以科学计数法形式输出浮点数
    left 左对齐,即在宽度不足时将填充字符添加到右边
    *right 右对齐,即在宽度不足时将填充字符添加到左边
    setbase(b) 设置输出整数时的进制,b=8、10 或 16
    setw(w) 指定输出宽度为 w 个字符,或输人字符串时读入 w 个字符
    setfill© 在指定输出宽度的情况下,输出的宽度不足时用字符 c 填充(默认情况是用空格填充)
    setprecision(n) 设置输出浮点数的精度为 n。在使用非 fixed 且非 scientific 方式输出的情况下,n 即为有效数字最多的位数,如果有效数字位数超过 n,则小数部分四舍五人,或自动变为科学计 数法输出并保留一共 n 位有效数字。在使用 fixed 方式和 scientific 方式输出的情况下,n 是小数点后面应保留的位数。
    setiosflags(flag) 将某个输出格式标志置为 1
    resetiosflags(flag) 将某个输出格式标志置为 0
    boolapha 把 true 和 false 输出为字符串 不常用
    *noboolalpha 把 true 和 false 输出为 0、1
    showbase 输出表示数值的进制的前缀
    *noshowbase 不输出表示数值的进制.的前缀
    showpoint 总是输出小数点
    *noshowpoint 只有当小数部分存在时才显示小数点
    showpos 在非负数值中显示 +
    *noshowpos 在非负数值中不显示 +
    *skipws 输入时跳过空白字符
    noskipws 输入时不跳过空白字符
    uppercase 十六进制数中使用 A~E。若输出前缀,则前缀输出 0X,科学计数法中输出 E
    *nouppercase 十六进制数中使用 a~e。若输出前缀,则前缀输出 0x,科学计数法中输出 e。
    internal 数值的符号(正负号)在指定宽度内左对齐,数值右对 齐,中间由填充字符填充。
    • 流操作算子使用方法:cout << hex << 12 << "," << 24;//c,18

    setiosflags() 算子
    setiosflags() 算子实际上是一个库函数,它以一些标志作为参数,这些标志可以是在 iostream 头文件中定义的以下几种取值,它们的含义和同名算子一样。

    标 志 作 用
    ios::left 输出数据在本域宽范围内向左对齐
    ios::right 输出数据在本域宽范围内向右对齐
    ios::internal 数值的符号位在域宽内左对齐,数值右对齐,中间由填充字符填充
    ios::dec 设置整数的基数为 10
    ios::oct 设置整数的基数为 8
    ios::hex 设置整数的基数为 16
    ios::showbase 强制输出整数的基数(八进制数以 0 开头,十六进制数以 0x 打头)
    ios::showpoint 强制输出浮点数的小点和尾数 0
    ios::uppercase 在以科学记数法格式 E 和以十六进制输出字母时以大写表示
    ios::showpos 对正数显示“+”号
    ios::scientific 浮点数以科学记数法格式输出
    ios::fixed 浮点数以定点格式(小数形式)输出
    ios::unitbuf 每次输出之后刷新所有的流
    ios::stdio 每次输出之后清除 stdout, stderr
    • 多个标志可以用|运算符连接,表示同时设置。例如:
    cout << setiosflags(ios::scientific|ios::showpos) << 12.34;//+1.234000e+001
    
    • 如果两个相互矛盾的标志同时被设置,结果可能就是两个标志都不起作用,应该用 resetiosflags 清除原先的标志
    cout << setiosflags(ios::fixed) << 12.34 << endl;
    cout << resetiosflags(ios::fixed) << setiosflags(ios::scientific | ios::showpos) << 12.34 << endl;
    

    ostream 类中的成员函数:

    成员函数 作用相同的流操纵算子 说明
    precision(n) setprecision(n) 设置输出浮点数的精度为 n。
    width(w) setw(w) 指定输出宽度为 w 个字符。
    fill© setfill © 在指定输出宽度的情况下,输出的宽度不足时用字符 c 填充(默认情况是用空格填充)。
    setf(flag) setiosflags(flag) 将某个输出格式标志置为 1。
    unsetf(flag) resetiosflags(flag) 将某个输出格式标志置为 0。
    • setf 和 unsetf 函数用到的flag,与 setiosflags 和 resetiosflags 用到的完全相同。
    cout.setf(ios::scientific);
    cout.precision(8);
    cout << 12.23 << endl;//1.22300000e+001
    

    18 文件操作

    文件-指存储在外部介质上的数据集合,文件按照数据的组织形式不一样,分为两种:ASCII文件(文本/字符),二进制文件(内部格式/字节)

    • ASCII文件输出还是二进制文件,数据形式一样,对于数值数据,输出不同

    18.1 文件类和对象

    C++ 标准类库中有三个类可以用于文件操作,它们统称为文件流类。这三个类是:

    • ifstream:输入流类,用于从文件中读取数据。
    • ofstream:输出流类,用于向文件中写人数据。
    • fstream:输入/输出流类,既可用于从文件中读取数据,又可用于 向文件中写人数据。
      文件流对象定义:
    #include <fstream>
    ifstream in;
    ofstream out;
    fstream inout;
    

    18.2 打开文件

    • 打开文件的目的:建立对象与文件的关联,指明文件使用方式
    • 打开文件的两种方式:open函数和构造函数

    open函数:void open(const char* szFileName, int mode);

    模式标记 适用对象 作用
    ios::in ifstream fstream 打开文件用于读取数据。如果文件不存在,则打开出错。
    ios::out ofstream fstream 打开文件用于写入数据。如果文件不存在,则新建该文件;如 果文件原来就存在,则打开时清除原来的内容。
    ios::app ofstream fstream 打开文件,用于在其尾部添加数据。如果文件不存在,则新建该文件。
    ios::ate ifstream 打开一个已有的文件,并将文件读指针指向文件末尾(读写指 的概念后面解释)。如果文件不存在,则打开出错。
    ios:: trunc ofstream 单独使用时与 ios:: out 相同。
    ios::binary ifstream ofstream fstream 以二进制方式打开文件。若不指定此模式,则以文本模式打开。
    ios::in | ios::out fstream 打开已存在的文件,既可读取其内容,也可向其写入数据。文件刚打开时,原有内容保持不变。如果文件不存在,则打开出错。
    ios::in | ios::out ofstream 打开已存在的文件,可以向其写入数据。文件刚打开时,原有内容保持不变。如果文件不存在,则打开出错。
    ios::in | ios::out | ios::trunc fstream 打开文件,既可读取其内容,也可向其写入数据。如果文件本来就存在,则打开时清除原来的内容;如果文件不存在,则新建该文件。

    ios::binary 可以和其他模式标记组合使用,例如:

    • ios::in | ios::binary表示用二进制模式,以读取的方式打开文件。
    • ios::out | ios::binary表示用二进制模式,以写入的方式打开文件。

    流类的构造函数
    eg:ifstream::ifstream (const char* szFileName, int mode = ios::in, int);

    #include <iostream>
    #include <fstream>
    using namespace std;
    int main()
    {
        ifstream inFile("c:\\tmp\\test.txt", ios::in);
        if (inFile)
            inFile.close();
        else
            cout << "test.txt doesn't exist" << endl;
        ofstream oFile("test1.txt", ios::out);
        if (!oFile)
            cout << "error 1";
        else
            oFile.close();
        fstream oFile2("tmp\\test2.txt", ios::out | ios::in);
        if (!oFile2)
            cout << "error 2";
        else
            oFile.close();
        return 0;
    }
    

    18.3 文本文件的读写

    对于文本文件,可以使用 cin、cout 读写。
    eg:编写一个程序,将文件 i.txt 中的整数倒序输出到 o.txt。(12 34 56 78 90 -> 90 78 56 34 12)

    #include <iostream>
    #include <fstream>
    using namespace std;
    int arr[100];
    int main()
    {
    
    	int num = 0;
    	ifstream inFile("i.txt", ios::in);//文本模式打开
    	if (!inFile)
    		return 0;//打开失败
    	ofstream outFile("o.txt",ios::out);
    	if (!outFile)
    	{
    		outFile.close();
    		return 0;
    	}
    	int x;
    	while (inFile >> x)
    		arr[num++] = x;
    	for (int i = num - 1; i >= 0; i--)
    		outFile << arr[i] << " ";
    	inFile.close();
    	outFile.close();
    	return 0;
    }
    
    

    18.4 二进制文件的读写

    • 用文本方式存储信息不但浪费空间,而且不便于检索。
    • 二进制文件中,信息都占用 sizeof(对象名) 个字节;文本文件中类的成员数据所占用的字节数不同,占用空间一般比二进制的大。

    ostream::write 成员函数:ostream & write(char* buffer, int count);

    class Person
    {
    public:
    	char m_name[20];
    	int m_age;
    };
    int main()
    {
    
    	Person p;
    	ofstream outFile("o.bin", ios::out | ios::binary);
    	while (cin >> p.m_name >> p.m_age)
    		outFile.write((char*)&p, sizeof(p));//强制类型转换
    	outFile.close();
    	//heiren 烫烫烫烫烫烫啼  
    	return 0;
    }
    

    istream::read 成员函数:istream & read(char* buffer, int count);

    Person p;
    ifstream inFile("o.bin", ios::in | ios::binary); //二进制读方式打开
    if (!inFile)
    	return 0;//打开文件失败
    while (inFile.read((char *)&p, sizeof(p)))
    	cout << p.m_name << " " << p.m_age << endl;
    inFile.close();
    

    文件流类的 put 和 get 成员函数

    #include <iostream>
    #include <fstream>
    using namespace std;
    int main()
    {
        
        ifstream inFile("a.txt", ios::binary | ios::in); 
        if (!inFile)
            return 0;
        ofstream outFile("b.txt", ios::binary | ios::out);
        if (!outFile) 
        {
            inFile.close();
            return 0;
        }
        char c;
        while (inFile.get(c))  //每读一个字符
            outFile.put(c);  //写一个字符
        outFile.close();
        inFile.close();
        return 0;
    }
    

    一个字节一个字节地读写,不如一次读写一片内存区域快。每次读写的字节数最好是 512 的整数倍

    18.5 移动和获取文件读写指针

    • ifstream 类和 fstream 类有 seekg 成员函数,可以设置文件读指针的位置;
    • ofstream 类和 fstream 类有 seekp 成员函数,可以设置文件写指针的位置。
    • ifstream 类和 fstream 类还有 tellg 成员函数,能够返回文件读指针的位置;
    • ofstream 类和 fstream 类还有 tellp 成员函数,能够返回文件写指针的位置。

    函数原型

    ostream & seekp (int offset, int mode);
    istream & seekg (int offset, int mode);
    //mode有三种:ios::beg-开头往后offset(>=0)字节 ios::cur-当前往前(<=0)/后(>=0)offset字节 ios::end-末尾往前(<=0)offect字节
    int tellg();
    int tellp();
    //seekg 函数将文件读指针定位到文件尾部,再用 tellg 函数获取文件读指针的位置,此位置即为文件长度
    

    举个栗子:折半查找文件,name等于“Heiren”

    #include <iostream>
    #include <fstream>
    //#include <vector>
    //#include<cstring>
    using namespace std;
    
    class Person
    {
    public:
    	char m_name[20];
    	int m_age;
    };
    int main()
    {
    
    
    	Person p;
    	ifstream ioFile("p.bin", ios::in | ios::out);//用既读又写的方式打开
    	if (!ioFile) 
    		return 0;
    	ioFile.seekg(0, ios::end); //定位读指针到文件尾部,以便用以后tellg 获取文件长度
    	int L = 0, R; // L是折半查找范围内第一个记录的序号
    				  // R是折半查找范围内最后一个记录的序号
    	R = ioFile.tellg() / sizeof(Person) - 1;
    	do {
    		int mid = (L + R) / 2; 
    		ioFile.seekg(mid *sizeof(Person), ios::beg); 
    		ioFile.read((char *)&p, sizeof(p));
    		int tmp = strcmp(p.m_name, "Heiren");
    		if (tmp == 0)
    		{ 
    			cout << p.m_name << " " << p.m_age;
    			break;
    		}
    		else if (tmp > 0) 
    			R = mid - 1;
    		else  
    			L = mid + 1;
    	} while (L <= R);
    	ioFile.close();
    
    	system("pause");
    	return 0;
    }
    

    18.6 文本文件和二进制文件打开方式的区别

    • UNIX/Linux 平台中,用文本方式或二进制方式打开文件没有任何区别。
    • 在 UNIX/Linux 平台中,文本文件以\n(ASCII 码为 0x0a)作为换行符号;而在 Windows 平台中,文本文件以连在一起的\r\n(\r的 ASCII 码是 0x0d)作为换行符号。
    • 在 Windows 平台中,如果以文本方式打开文件,当读取文件时,系统会将文件中所有的\r\n转换成一个字符\n,如果文件中有连续的两个字节是 0x0d0a,则系统会丢弃前面的 0x0d 这个字节,只读入 0x0a。当写入文件时,系统会将\n转换成\r\n写入。

    用二进制方式打开文件总是最保险的。

    19 泛型和模板

    • 泛型程序设计在实现时不指定具体要操作的数据的类型的程序设计方法的一种算法,指的是算法只要实现一遍,就能适用于多种数据类型,优势在于代码复用,减少重复代码的编写。
    • 模板是泛型的基础,是创建泛型类或函数的蓝图或公式。

    19.1 函数模板

    函数模板的一般形式:

    template<class T>template<typename T>
    函数类型 函数名(参数列表)
    {
    	函数体;
    }
    template<class T1,class T2,...>//class可以换成typename
    函数类型 函数名(参数列表)
    {
    	函数体;
    }
    //举个栗子
    template<class T> T max(T a, T b)
    {
    	return a > b ? a : b;
    }
    int main()
    {
    	cout <<"max value is "<< max(12,34) << endl;//34
    	cout << "max value is " << max(12.4, 13.6) << endl;//13.6
    	cout << "max value is " << max(12.4, 13) << endl;//error 没有与参数列表匹配的 函数模板 "max" 实例参数类型为:(double, int)
    	return 0;
    }
    

    19.2 类模板

    • 声明了类模板,就可以将类型参数用于类的成员函数和成员变量了。换句话说,原来使用 int、float、char 等内置类型的地方,都可以用类型参数来代替。
      类模板的一般形式:
    template<class T>//class可以换成typename 模板头
    class 类名
    {
    	函数定义;
    };
    //多个类型参数和函数模板类似,逗号隔开
    

    当类中的成员函数在类的声明之外定义时,它必须定义为函数模板,带上模板头,定义形式如下:

    template<class T>//class可以换成typename
    函数类型 类名<T>::函数名(形参列表)
    {
    	函数体;
    }
    

    举个栗子:

    template<typename T1, typename T2>  // 模板头  没有分号
    class Point {
    public:
    	Point(T1 x, T2 y) : x(x), y(y) { }
    public:
    	T1 getX() const;  //成员函数后加const,声明该函数内部不会改变成员变量的值
    	void setX(T1 x);  
    	T2 getY() const;  
    	void setY(T2 y);  
    private:
    	T1 x;  
    	T2 y;  
    };
    template<typename T1, typename T2>  //模板头
    T1 Point<T1, T2>::getX() const  {
    	return x;
    }
    template<typename T1, typename T2>
    void Point<T1, T2>::setX(T1 x) {
    	x = x;
    }
    template<typename T1, typename T2>
    T2 Point<T1, T2>::getY() const {
    	return y;
    }
    template<typename T1, typename T2>
    void Point<T1, T2>::setY(T2 y) {
    	y = y;
    }
    int main()
    {
    	Point<int, double> p1(66, 20.5);
    	Point<int, char*> p2(10, "东经33度");
    	Point<char*, char*> *p3 = new Point<char*, char*>("西经12度", "北纬66度");
    	cout << "x=" << p1.getX() << ", y=" << p1.getY() << endl;
    	cout << "x=" << p2.getX() << ", y=" << p2.getY() << endl;
    	cout << "x=" << p3->getX() << ", y=" << p3->getY() << endl;
    	return 0;
    }
    

    19.3 typename 和 class 的区别

          在模板引入 c++ 后,采用class来定义模板参数类型,后来为了避免 class 在声明类和模板的使用可能给人带来混淆,所以引入了 typename 这个关键字。

    • 模板定义语法中关键字 class 与 typename 的作用完全一样。
    • 不同的是typename 还有另外一个作用为:使用嵌套依赖类型(nested depended name)
    class MyClass
    {
    public:
    		typedef int LengthType;
    		LengthType getLength() const
    		{
    			return this->length;
    		}
    		void setLength(LengthType length)
    		{
    			this->length = length;
    		}
    		
    private:
    	LengthType length;
    };
    template<class T>
    void MyMethod(T myclass)
    {
    	//告诉 c++ 编译器,typename 后面的字符串为一个类型名称,而不是成员函数或者成员变量
    	typedef typename T::LengthType LengthType; //
    	LengthType length = myclass.getLength();
    	cout << "length = " <<length<< endl;
    	
    }
    int main()
    {
    	MyClass my;
    	my.setLength(666);
    	MyMethod(my);//length = 666
    	return 0;
    }
    

    19.4 强弱类型语言和c++模板的那点猫腻

    计算机编程语言可以根据在 "定义变量时是否要显式地指明数据类型"可以分为强类型语言和弱类型语言。

    • 强类型语言-在定义变量时需要显式地指明数据类型,为变量指明某种数据类型后就不能赋予其他类型的数据了,除非经过强制类型转换或隐式类型转换。典型的强类型语言有 C/C++、Java、C# 等。
    int a = 123;  //不转换
    a = 12.89;  //隐式转换 12(舍去小数部分)
    a = (int)"heiren,HelloWorld";  //强制转换(得到字符串的地址) 不同类型之间转换需要强制
    
    //Java 对类型转换的要求比 C/C++ 更为严格,隐式转换只允许由低向高转,由高向低转必须强制转换。
    int a = 100;  //不转换
    a = (int)12.34;  //强制转换(直接舍去小数部分,得到12)
    
    • 弱类型语言-在定义变量时不需要显式地指明数据类型,编译器(解释器)会根据赋给变量的数据自动推导出类型,并且可以赋给变量不同类型的数据。典型的弱类型语言有 JavaScript、Python、PHP、Ruby、Shell、Perl 等。
    var a = 100;  //赋给整数
    a = 12.34;  //赋给小数
    a = "heiren,HelloWorld";  //赋给字符串
    a = new Array("JavaScript","React","JSON");  //赋给数组
    

          强类型语言在编译期间就能检测某个变量的操作是否正确,因为变量的类型始终哦都市确定的,加快了程序的运行;对于弱类型的语言,变量的类型可以随时改变,编译器在编译期间能确定变量的类型,只有等到程序运行后、赋值后才能确定变量当前是什么类型,所以传统的编译对弱类型语言意义不大。

    • 解释型语言-弱类型往往是解释型语言,边执行边编译
    • 编译型语言-先编译后执行。

            强类型语言较为严谨,在编译时就能发现很多错误,适合开发大型的、系统级的、工业级的项目;而弱类型语言较为灵活,编码效率高,部署容易,学习成本低,在 Web 开发中大显身手。另外,强类型语言的 IDE 一般都比较强大,代码感知能力好,提示信息丰富;而弱类型语言一般都是在编辑器中直接书写代码。

            C++模板退出的动力来源是对数据结构的封装:数据结构关注的是数据的存储以及对其的增删改查操作,C++开发者们想封装这些结构,但是这些结构中数据成分的类型无法提前预测,于是模板诞生了。

    STL(Standard Template Library,标准模板库)就是c++对数据结构封装后的称呼。

    20 命名空间和异常处理

    20.1 命名空间

        命名空间实际上是由用户自己命名的一块内存区域,用户可以根据需要指定一个有名字的空间区域,每个命名空间都有一个作用域,将一些全局实体放在该命名空间中,就与其他全局实体分割开来。
    命名空间定义的一般形式:

    namespace [命名空间名]//名省略时,表示无名的命名空间
    {
    	命名空间成员;
    }
    

    命名空间成员的引用:命名空间名::命名空间成员名
    使用命名空间别名:namespace 别名 = 命名空间名
    使用using声明命名空间成员的格式:using 命名空间名::命名空间成员名;
    使用using声明命名空间的全部成员:using namespace 命名空间名;

    • using声明后,在using语句所在的作用域中使用该命名空间成员时,不必再用命名空间名加以限定。
    • 标准C++库的所有标识符(包括函数、类、对象和类模板)都是在一个名为std的命名空间中定义的。
    • 无名的命名空间,只在本文件的作用域内有效。

    20.2 异常处理

        异常就是程序在执行过程中,由于使用环境变化和用户操作等原因产生的错误,从而影响程序的运行。

    • 程序中常见的错误:语法错误,运行错误
    • 异常处理机制的组成:检查异常(try)、抛出异常(throw)、捕获并处理异常(catch)

    异常处理语句:

    • 被检查的语句必须放在try后面的{}中,否则不起作用,{}不能省略。
    • 进行异常处理的语句必须放在catch后面的{}中,catch后()中的异常信息类型不能省略,变量名可以省略。
    • catch语句块不能单独使用,必须和try语句块作为整体出现。
    • 在try-catch结构中,只能有一个try,但可以有多个catch.
    • catch(…)通常放在最后,可以捕获任何类型的异常信息。
    try
    {
    	被检查的语句(可以有多个throw语句);
    }
    catch(异常信息类型 [变量名])
    {
     
    }
    

    throw语句:thow 变量或表达式;

    • throw放在try中,也可以单独使用,向上一层函数寻找try-catch,没有找到系统就会调用系统函数terminate,使程序终止运行。
    try
    {
    	throw 123;
    }
    catch (int a)
    {
    	cout << a << endl;//123
    }
    

    c++中没有finally
    类的异常处理,当在try中定义了类对象,在try中抛出异常前创建的对象将被自动释放。
    异常规范-描述了一个函数允许抛出那些异常类型。

    • 异常规范应同时出现在函数声明和函数定义中。
    • 如果没有异常规范,可以抛出任何类型的异常。
      异常规范的一般形式:函数类型 函数名(参数类型)throw ([异常类型1,异常类型2,...])
    float fun(float float)throw(int,float,double);
    

    C++标准异常
    在这里插入图片描述

    异常 描述
    std::exception 该异常是所有标准 C++ 异常的父类。
    std::bad_alloc 该异常可以通过 new 抛出。
    std::bad_cast 该异常可以通过 dynamic_cast 抛出。
    std::bad_exception 这在处理 C++ 程序中无法预期的异常时非常有用。
    std::bad_typeid 该异常可以通过 typeid 抛出。
    std::logic_error 理论上可以通过读取代码来检测到的异常。
    std::domain_error 当使用了一个无效的数学域时,会抛出该异常。
    std::invalid_argument 当使用了无效的参数时,会抛出该异常。
    std::length_error 当创建了太长的 std::string 时,会抛出该异常。
    std::out_of_range 该异常可以通过方法抛出,例如 std::vector 和 std::bitset<>::operator
    std::runtime_error 理论上不可以通过读取代码来检测到的异常。
    std::overflow_error 当发生数学上溢时,会抛出该异常。
    std::range_error 当尝试存储超出范围的值时,会抛出该异常。
    std::underflow_error 当发生数学下溢时,会抛出该异常。

    21 STL

    C++标准模板库(Standard Template Library,STL)是泛型程序设计最成功的实例。STL是一些常用数据结构和算法的模板的集合,由Alex Stepanov主持开发,于1998年被加入C++标准。

    • C++ 标准模板库的核心包括三大组件:容器,算法,迭代器

    21.1 容器

    顺序容器:可变长动态数组Vector、双端队列deque、双向链表list
    关联容器:set、multliset、map、multimap

    • 关联容器内的元素是排序的,所以查找时具有非常好的性能。
      容器适配起:栈stack、队列queu、优先队列priority_queue
      所有容器具有的函数:
    int size();
    bool empty();
    

    顺序容器和关联容器函数:

    begin()
    end()
    rbegin()
    erase(...)
    clear()
    

    顺序容器独有的函数:

    front()
    back()
    push_back();
    pop_back();
    insert(...);
    

    21.2 迭代器

    迭代器是一种检查容器内元素并遍历元素的数据类型。C++更趋向于使用迭代器而不是下标操作,因为标准库为每一种标准容器(如vector)定义了一种迭代器类型,而只用少数容器(如vector)支持下标操作访问容器元素。按照定义方式分为以下四种。
    1.正向迭代器:容器类名::iterator 迭代器名;
    2.常量正向迭代器:容器类名::const_iterator 迭代器名;
    3.反向迭代器:容器类名::reverse_iterator 迭代器名;
    4.常量反向迭代器:容器类名::const_reverse_iterator 迭代器名

    21.3 算法

        STL 提供能在各种容器中通用的算法(大约有70种),如插入、删除、查找、排序等。算法就是函数模板。算法通过迭代器来操纵容器中的元素。
        STL 中的大部分常用算法都在头文件 algorithm 中定义。此外,头文件 numeric 中也有一些算法。
        许多算法操作的是容器上的一个区间(也可以是整个容器),因此需要两个参数,一个是区间起点元素的迭代器,另一个是区间终点元素的后面一个元素的迭代器。
    会改变其所作用的容器。例如:

    • copy:将一个容器的内容复制到另一个容器。
    • remove:在容器中删除一个元素。
    • random_shuffle:随机打乱容器中的元素。
    • fill:用某个值填充容器。

    不会改变其所作用的容器。例如:

    • find:在容器中查找元素。
    • count_if:统计容器中符合某种条件的元素的个数。
    #include <vector>
    #include <algorithm>
    #include <iostream>
    using namespace std;
    int main()  {
       vector<int> v;
    	v.push_back(1);
    	v.push_back(2);
    	v.push_back(3);
    	v.push_back(4); //1,2,3,4
    	vector<int>::iterator p;
    	p = find(v.begin(), v.end(), 3); //在v中查找3 若找不到,find返回 v.end()
    	if (p != v.end())
    		cout << "1) " << *p << endl; //找到了
    	p = find(v.begin(), v.end(), 9);
    	if (p == v.end())
    		cout << "not found " << endl; //没找到
    
    	p = find(v.begin() + 1, v.end() - 1, 4); //在2,3 这两个元素中查找4
    	cout << "2) " << *p << endl; //没找到,指向下一个元素4
    
    	int arr[10] = { 10,20,30,40 };
    	int * pp = find(arr, arr + 4, 20);
    	if (pp == arr + 4)
    		cout << "not found" << endl;
    	else
    		cout << "3) " << *pp << endl;
    	return 0;
    
    }
    

    22 总结

        终于写完了,因为新冠疫情,在家隔离了一个多月,闲来无事,写写博客来总结一下自己之前学的知识;零零散散大约花了一周左右的时间,该篇文章面向C++的基础知识,有许多详细,深层的没有写进去。后面有时间会写模板,STL,C++11新特性,Boost库等的文章,刚开始写博客,文章中有错误或有遗漏的知识点,请大佬们指出。

    展开全文
  • CString a, b; a=“D:\\test\\test1"; 用b表示a的上一级目录怎么搞? 结果为b="D:\\test";
  • 《黑马》——C++基础入门

    万次阅读 多人点赞 2020-09-05 18:32:22
    ** 前言: ** 配套视频: ...seid=16795623907667609637 ...编写C++程序总共分为4个步骤 创建项目 创建文件 编写代码 运行程序 1.1.1 创建项目 ​ Visual Studio是我们用来编写C++程序的主要工具,我们先将它打开

    前言:

    配套视频:
    https://www.bilibili.com/video/BV1et411b73Z
    为方便学习,在此发布C++基础入门部分配套讲义,原作者为黑马程序

    C++基础入门

    1 C++初识

    1.1 第一个C++程序

    编写一个C++程序总共分为4个步骤

    • 创建项目
    • 创建文件
    • 编写代码
    • 运行程序

    1.1.1 创建项目

    ​ Visual Studio是我们用来编写C++程序的主要工具,我们先将它打开

    1.1.2 创建文件

    1.1.3 编写代码

    #include<iostream>
    using namespace std;
    
    int main() {
    
    	cout << "Hello world" << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    1.1.4 运行程序

    1.2 注释

    作用:在代码中加一些说明和解释,方便自己或其他程序员程序员阅读代码

    两种格式

    1. 单行注释// 描述信息
      • 通常放在一行代码的上方,或者一条语句的末尾,对该行代码说明
    2. 多行注释/* 描述信息 */
      • 通常放在一段代码的上方,对该段代码做整体说明

    提示:编译器在编译代码时,会忽略注释的内容

    1.3 变量

    作用:给一段指定的内存空间起名,方便操作这段内存

    语法数据类型 变量名 = 初始值;

    示例:

    #include<iostream>
    using namespace std;
    
    int main() {
    
    	//变量的定义
    	//语法:数据类型  变量名 = 初始值
    
    	int a = 10;
    
    	cout << "a = " << a << endl;
    	
    	system("pause");
    
    	return 0;
    }
    

    注意:C++在创建变量时,必须给变量一个初始值,否则会报错

    1.4 常量

    作用:用于记录程序中不可更改的数据

    C++定义常量两种方式

    1. #define 宏常量: #define 常量名 常量值

      • 通常在文件上方定义,表示一个常量
    2. const修饰的变量 const 数据类型 常量名 = 常量值

      • 通常在变量定义前加关键字const,修饰该变量为常量,不可修改

    示例:

    //1、宏常量
    #define day 7
    
    int main() {
    
    	cout << "一周里总共有 " << day << " 天" << endl;
    	//day = 8;  //报错,宏常量不可以修改
    
    	//2、const修饰变量
    	const int month = 12;
    	cout << "一年里总共有 " << month << " 个月份" << endl;
    	//month = 24; //报错,常量是不可以修改的
    	
    	
    	system("pause");
    
    	return 0;
    }
    

    1.5 关键字

    **作用:**关键字是C++中预先保留的单词(标识符)

    • 在定义变量或者常量时候,不要用关键字

    C++关键字如下:

    asm do if return typedef
    auto double inline short typeid
    bool dynamic_cast int signed typename
    break else long sizeof union
    case enum mutable static unsigned
    catch explicit namespace static_cast using
    char export new struct virtual
    class extern operator switch void
    const false private template volatile
    const_cast float protected this wchar_t
    continue for public throw while
    default friend register true
    delete goto reinterpret_cast try

    提示:在给变量或者常量起名称时候,不要用C++得关键字,否则会产生歧义。

    1.6 标识符命名规则

    作用:C++规定给标识符(变量、常量)命名时,有一套自己的规则

    • 标识符不能是关键字
    • 标识符只能由字母、数字、下划线组成
    • 第一个字符必须为字母或下划线
    • 标识符中字母区分大小写

    建议:给标识符命名时,争取做到见名知意的效果,方便自己和他人的阅读

    2 数据类型

    C++规定在创建一个变量或者常量时,必须要指定出相应的数据类型,否则无法给变量分配内存

    2.1 整型

    作用:整型变量表示的是整数类型的数据

    C++中能够表示整型的类型有以下几种方式,区别在于所占内存空间不同

    数据类型 占用空间 取值范围
    short(短整型) 2字节 (-2^15 ~ 2^15-1)
    int(整型) 4字节 (-2^31 ~ 2^31-1)
    long(长整形) Windows为4字节,Linux为4字节(32位),8字节(64位) (-2^31 ~ 2^31-1)
    long long(长长整形) 8字节 (-2^63 ~ 2^63-1)

    2.2 sizeof关键字

    **作用:**利用sizeof关键字可以统计数据类型所占内存大小

    语法: sizeof( 数据类型 / 变量)

    示例:

    int main() {
    
    	cout << "short 类型所占内存空间为: " << sizeof(short) << endl;
    
    	cout << "int 类型所占内存空间为: " << sizeof(int) << endl;
    
    	cout << "long 类型所占内存空间为: " << sizeof(long) << endl;
    
    	cout << "long long 类型所占内存空间为: " << sizeof(long long) << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    整型结论short < int <= long <= long long

    2.3 实型(浮点型)

    作用:用于表示小数

    浮点型变量分为两种:

    1. 单精度float
    2. 双精度double

    两者的区别在于表示的有效数字范围不同。

    数据类型 占用空间 有效数字范围
    float 4字节 7位有效数字
    double 8字节 15~16位有效数字

    示例:

    int main() {
    
    	float f1 = 3.14f;
    	double d1 = 3.14;
    
    	cout << f1 << endl;
    	cout << d1<< endl;
    
    	cout << "float  sizeof = " << sizeof(f1) << endl;
    	cout << "double sizeof = " << sizeof(d1) << endl;
    
    	//科学计数法
    	float f2 = 3e2; // 3 * 10 ^ 2 
    	cout << "f2 = " << f2 << endl;
    
    	float f3 = 3e-2;  // 3 * 0.1 ^ 2
    	cout << "f3 = " << f3 << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    2.4 字符型

    **作用:**字符型变量用于显示单个字符

    语法:char ch = 'a';

    注意1:在显示字符型变量时,用单引号将字符括起来,不要用双引号

    注意2:单引号内只能有一个字符,不可以是字符串

    • C和C++中字符型变量只占用1个字节
    • 字符型变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码放入到存储单元

    示例:

    int main() {
    	
    	char ch = 'a';
    	cout << ch << endl;
    	cout << sizeof(char) << endl;
    
    	//ch = "abcde"; //错误,不可以用双引号
    	//ch = 'abcde'; //错误,单引号内只能引用一个字符
    
    	cout << (int)ch << endl;  //查看字符a对应的ASCII码
    	ch = 97; //可以直接用ASCII给字符型变量赋值
    	cout << ch << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    ASCII码表格:

    ASCII 控制字符 ASCII 字符 ASCII 字符 ASCII 字符
    0 NUT 32 (space) 64 @ 96
    1 SOH 33 ! 65 A 97 a
    2 STX 34 " 66 B 98 b
    3 ETX 35 # 67 C 99 c
    4 EOT 36 $ 68 D 100 d
    5 ENQ 37 % 69 E 101 e
    6 ACK 38 & 70 F 102 f
    7 BEL 39 , 71 G 103 g
    8 BS 40 ( 72 H 104 h
    9 HT 41 ) 73 I 105 i
    10 LF 42 * 74 J 106 j
    11 VT 43 + 75 K 107 k
    12 FF 44 , 76 L 108 l
    13 CR 45 - 77 M 109 m
    14 SO 46 . 78 N 110 n
    15 SI 47 / 79 O 111 o
    16 DLE 48 0 80 P 112 p
    17 DCI 49 1 81 Q 113 q
    18 DC2 50 2 82 R 114 r
    19 DC3 51 3 83 S 115 s
    20 DC4 52 4 84 T 116 t
    21 NAK 53 5 85 U 117 u
    22 SYN 54 6 86 V 118 v
    23 TB 55 7 87 W 119 w
    24 CAN 56 8 88 X 120 x
    25 EM 57 9 89 Y 121 y
    26 SUB 58 : 90 Z 122 z
    27 ESC 59 ; 91 [ 123 {
    28 FS 60 < 92 / 124 |
    29 GS 61 = 93 ] 125 }
    30 RS 62 > 94 ^ 126 `
    31 US 63 ? 95 _ 127 DEL

    ASCII 码大致由以下两部分组成:

    • ASCII 非打印控制字符: ASCII 表上的数字 0-31 分配给了控制字符,用于控制像打印机等一些外围设备。
    • ASCII 打印字符:数字 32-126 分配给了能在键盘上找到的字符,当查看或打印文档时就会出现。

    2.5 转义字符

    **作用:**用于表示一些不能显示出来的ASCII字符

    现阶段我们常用的转义字符有:\n \\ \t

    转义字符 含义 ASCII码值(十进制)
    \a 警报 007
    \b 退格(BS) ,将当前位置移到前一列 008
    \f 换页(FF),将当前位置移到下页开头 012
    \n 换行(LF) ,将当前位置移到下一行开头 010
    \r 回车(CR) ,将当前位置移到本行开头 013
    \t 水平制表(HT) (跳到下一个TAB位置) 009
    \v 垂直制表(VT) 011
    \\ 代表一个反斜线字符"" 092
    代表一个单引号(撇号)字符 039
    " 代表一个双引号字符 034
    ? 代表一个问号 063
    \0 数字0 000
    \ddd 8进制转义字符,d范围0~7 3位8进制
    \xhh 16进制转义字符,h范围09,af,A~F 3位16进制

    示例:

    int main() {
    	
    	
    	cout << "\\" << endl;
    	cout << "\tHello" << endl;
    	cout << "\n" << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    2.6 字符串型

    作用:用于表示一串字符

    两种风格

    1. C风格字符串char 变量名[] = "字符串值"

      示例:

      int main() {
      
      	char str1[] = "hello world";
      	cout << str1 << endl;
          
      	system("pause");
      
      	return 0;
      }
      

    注意:C风格的字符串要用双引号括起来

    1. C++风格字符串string 变量名 = "字符串值"

      示例:

      int main() {
      
      	string str = "hello world";
      	cout << str << endl;
      	
      	system("pause");
      
      	return 0;
      }
      

    注意:C++风格字符串,需要加入头文件==#include<string>==

    2.7 布尔类型 bool

    **作用:**布尔数据类型代表真或假的值

    bool类型只有两个值:

    • true — 真(本质是1)
    • false — 假(本质是0)

    bool类型占1个字节大小

    示例:

    int main() {
    
    	bool flag = true;
    	cout << flag << endl; // 1
    
    	flag = false;
    	cout << flag << endl; // 0
    
    	cout << "size of bool = " << sizeof(bool) << endl; //1
    	
    	system("pause");
    
    	return 0;
    }
    

    2.8 数据的输入

    作用:用于从键盘获取数据

    **关键字:**cin

    语法: cin >> 变量

    示例:

    int main(){
    
    	//整型输入
    	int a = 0;
    	cout << "请输入整型变量:" << endl;
    	cin >> a;
    	cout << a << endl;
    
    	//浮点型输入
    	double d = 0;
    	cout << "请输入浮点型变量:" << endl;
    	cin >> d;
    	cout << d << endl;
    
    	//字符型输入
    	char ch = 0;
    	cout << "请输入字符型变量:" << endl;
    	cin >> ch;
    	cout << ch << endl;
    
    	//字符串型输入
    	string str;
    	cout << "请输入字符串型变量:" << endl;
    	cin >> str;
    	cout << str << endl;
    
    	//布尔类型输入
    	bool flag = true;
    	cout << "请输入布尔型变量:" << endl;
    	cin >> flag;
    	cout << flag << endl;
    	system("pause");
    	return EXIT_SUCCESS;
    }
    

    3 运算符

    **作用:**用于执行代码的运算

    本章我们主要讲解以下几类运算符:

    运算符类型 作用
    算术运算符 用于处理四则运算
    赋值运算符 用于将表达式的值赋给变量
    比较运算符 用于表达式的比较,并返回一个真值或假值
    逻辑运算符 用于根据表达式的值返回真值或假值

    3.1 算术运算符

    作用:用于处理四则运算

    算术运算符包括以下符号:

    运算符 术语 示例 结果
    + 正号 +3 3
    - 负号 -3 -3
    + 10 + 5 15
    - 10 - 5 5
    * 10 * 5 50
    / 10 / 5 2
    % 取模(取余) 10 % 3 1
    ++ 前置递增 a=2; b=++a; a=3; b=3;
    ++ 后置递增 a=2; b=a++; a=3; b=2;
    前置递减 a=2; b=–a; a=1; b=1;
    后置递减 a=2; b=a–; a=1; b=2;

    示例1:

    //加减乘除
    int main() {
    
    	int a1 = 10;
    	int b1 = 3;
    
    	cout << a1 + b1 << endl;
    	cout << a1 - b1 << endl;
    	cout << a1 * b1 << endl;
    	cout << a1 / b1 << endl;  //两个整数相除结果依然是整数
    
    	int a2 = 10;
    	int b2 = 20;
    	cout << a2 / b2 << endl; 
    
    	int a3 = 10;
    	int b3 = 0;
    	//cout << a3 / b3 << endl; //报错,除数不可以为0
    
    
    	//两个小数可以相除
    	double d1 = 0.5;
    	double d2 = 0.25;
    	cout << d1 / d2 << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    总结:在除法运算中,除数不能为0

    示例2:

    //取模
    int main() {
    
    	int a1 = 10;
    	int b1 = 3;
    
    	cout << 10 % 3 << endl;
    
    	int a2 = 10;
    	int b2 = 20;
    
    	cout << a2 % b2 << endl;
    
    	int a3 = 10;
    	int b3 = 0;
    
    	//cout << a3 % b3 << endl; //取模运算时,除数也不能为0
    
    	//两个小数不可以取模
    	double d1 = 3.14;
    	double d2 = 1.1;
    
    	//cout << d1 % d2 << endl;
    
    	system("pause");
    
    	return 0;
    }
    
    

    总结:只有整型变量可以进行取模运算

    示例3:

    //递增
    int main() {
    
    	//后置递增
    	int a = 10;
    	a++; //等价于a = a + 1
    	cout << a << endl; // 11
    
    	//前置递增
    	int b = 10;
    	++b;
    	cout << b << endl; // 11
    
    	//区别
    	//前置递增先对变量进行++,再计算表达式
    	int a2 = 10;
    	int b2 = ++a2 * 10;
    	cout << b2 << endl;
    
    	//后置递增先计算表达式,后对变量进行++
    	int a3 = 10;
    	int b3 = a3++ * 10;
    	cout << b3 << endl;
    
    	system("pause");
    
    	return 0;
    }
    
    

    总结:前置递增先对变量进行++,再计算表达式,后置递增相反

    3.2 赋值运算符

    **作用:**用于将表达式的值赋给变量

    赋值运算符包括以下几个符号:

    运算符 术语 示例 结果
    = 赋值 a=2; b=3; a=2; b=3;
    += 加等于 a=0; a+=2; a=2;
    -= 减等于 a=5; a-=3; a=2;
    *= 乘等于 a=2; a*=2; a=4;
    /= 除等于 a=4; a/=2; a=2;
    %= 模等于 a=3; a%2; a=1;

    示例:

    int main() {
    
    	//赋值运算符
    
    	// =
    	int a = 10;
    	a = 100;
    	cout << "a = " << a << endl;
    
    	// +=
    	a = 10;
    	a += 2; // a = a + 2;
    	cout << "a = " << a << endl;
    
    	// -=
    	a = 10;
    	a -= 2; // a = a - 2
    	cout << "a = " << a << endl;
    
    	// *=
    	a = 10;
    	a *= 2; // a = a * 2
    	cout << "a = " << a << endl;
    
    	// /=
    	a = 10;
    	a /= 2;  // a = a / 2;
    	cout << "a = " << a << endl;
    
    	// %=
    	a = 10;
    	a %= 2;  // a = a % 2;
    	cout << "a = " << a << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    3.3 比较运算符

    **作用:**用于表达式的比较,并返回一个真值或假值

    比较运算符有以下符号:

    运算符 术语 示例 结果
    == 相等于 4 == 3 0
    != 不等于 4 != 3 1
    < 小于 4 < 3 0
    > 大于 4 > 3 1
    <= 小于等于 4 <= 3 0
    >= 大于等于 4 >= 1 1

    示例:

    int main() {
    
    	int a = 10;
    	int b = 20;
    
    	cout << (a == b) << endl; // 0 
    
    	cout << (a != b) << endl; // 1
    
    	cout << (a > b) << endl; // 0
    
    	cout << (a < b) << endl; // 1
    
    	cout << (a >= b) << endl; // 0
    
    	cout << (a <= b) << endl; // 1
    	
    	system("pause");
    
    	return 0;
    }
    

    注意:C和C++ 语言的比较运算中, “真”用数字“1”来表示, “假”用数字“0”来表示。

    3.4 逻辑运算符

    **作用:**用于根据表达式的值返回真值或假值

    逻辑运算符有以下符号:

    运算符 术语 示例 结果
    ! !a 如果a为假,则!a为真; 如果a为真,则!a为假。
    && a && b 如果a和b都为真,则结果为真,否则为假。
    || a || b 如果a和b有一个为真,则结果为真,二者都为假时,结果为假。

    **示例1:**逻辑非

    //逻辑运算符  --- 非
    int main() {
    
    	int a = 10;
    
    	cout << !a << endl; // 0
    
    	cout << !!a << endl; // 1
    
    	system("pause");
    
    	return 0;
    }
    

    总结: 真变假,假变真

    **示例2:**逻辑与

    //逻辑运算符  --- 与
    int main() {
    
    	int a = 10;
    	int b = 10;
    
    	cout << (a && b) << endl;// 1
    
    	a = 10;
    	b = 0;
    
    	cout << (a && b) << endl;// 0 
    
    	a = 0;
    	b = 0;
    
    	cout << (a && b) << endl;// 0
    
    	system("pause");
    
    	return 0;
    }
    
    

    总结:逻辑运算符总结: 同真为真,其余为假

    **示例3:**逻辑或

    //逻辑运算符  --- 或
    int main() {
    
    	int a = 10;
    	int b = 10;
    
    	cout << (a || b) << endl;// 1
    
    	a = 10;
    	b = 0;
    
    	cout << (a || b) << endl;// 1 
    
    	a = 0;
    	b = 0;
    
    	cout << (a || b) << endl;// 0
    
    	system("pause");
    
    	return 0;
    }
    

    逻辑运算符总结: 同假为假,其余为真

    4 程序流程结构

    C/C++支持最基本的三种程序运行结构:顺序结构、选择结构、循环结构

    • 顺序结构:程序按顺序执行,不发生跳转
    • 选择结构:依据条件是否满足,有选择的执行相应功能
    • 循环结构:依据条件是否满足,循环多次执行某段代码

    4.1 选择结构

    4.1.1 if语句

    **作用:**执行满足条件的语句

    if语句的三种形式

    • 单行格式if语句

    • 多行格式if语句

    • 多条件的if语句

    1. 单行格式if语句:if(条件){ 条件满足执行的语句 }

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vzgo2ykW-1599301407524)(assets/clip_image002.png)]

      示例:

      int main() {
      
      	//选择结构-单行if语句
      	//输入一个分数,如果分数大于600分,视为考上一本大学,并在屏幕上打印
      
      	int score = 0;
      	cout << "请输入一个分数:" << endl;
      	cin >> score;
      
      	cout << "您输入的分数为: " << score << endl;
      
      	//if语句
      	//注意事项,在if判断语句后面,不要加分号
      	if (score > 600)
      	{
      		cout << "我考上了一本大学!!!" << endl;
      	}
      
      	system("pause");
      
      	return 0;
      }
      

    注意:if条件表达式后不要加分号

    1. 多行格式if语句:if(条件){ 条件满足执行的语句 }else{ 条件不满足执行的语句 };

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-37UcReby-1599301407525)(assets/clip_image002-1541662519170.png)]

    示例:

    int main() {
    
    	int score = 0;
    
    	cout << "请输入考试分数:" << endl;
    
    	cin >> score;
    
    	if (score > 600)
    	{
    		cout << "我考上了一本大学" << endl;
    	}
    	else
    	{
    		cout << "我未考上一本大学" << endl;
    	}
    
    	system("pause");
    
    	return 0;
    }
    
    1. 多条件的if语句:if(条件1){ 条件1满足执行的语句 }else if(条件2){条件2满足执行的语句}... else{ 都不满足执行的语句}

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1OMyrQYG-1599301407526)(assets/clip_image002-1541662566808.png)]

    示例:

    	int main() {
    
    	int score = 0;
    
    	cout << "请输入考试分数:" << endl;
    
    	cin >> score;
    
    	if (score > 600)
    	{
    		cout << "我考上了一本大学" << endl;
    	}
    	else if (score > 500)
    	{
    		cout << "我考上了二本大学" << endl;
    	}
    	else if (score > 400)
    	{
    		cout << "我考上了三本大学" << endl;
    	}
    	else
    	{
    		cout << "我未考上本科" << endl;
    	}
    
    	system("pause");
    
    	return 0;
    }
    

    嵌套if语句:在if语句中,可以嵌套使用if语句,达到更精确的条件判断

    案例需求:

    • 提示用户输入一个高考考试分数,根据分数做如下判断
    • 分数如果大于600分视为考上一本,大于500分考上二本,大于400考上三本,其余视为未考上本科;
    • 在一本分数中,如果大于700分,考入北大,大于650分,考入清华,大于600考入人大。

    示例:

    int main() {
    
    	int score = 0;
    
    	cout << "请输入考试分数:" << endl;
    
    	cin >> score;
    
    	if (score > 600)
    	{
    		cout << "我考上了一本大学" << endl;
    		if (score > 700)
    		{
    			cout << "我考上了北大" << endl;
    		}
    		else if (score > 650)
    		{
    			cout << "我考上了清华" << endl;
    		}
    		else
    		{
    			cout << "我考上了人大" << endl;
    		}
    		
    	}
    	else if (score > 500)
    	{
    		cout << "我考上了二本大学" << endl;
    	}
    	else if (score > 400)
    	{
    		cout << "我考上了三本大学" << endl;
    	}
    	else
    	{
    		cout << "我未考上本科" << endl;
    	}
    
    	system("pause");
    
    	return 0;
    }
    

    练习案例: 三只小猪称体重

    有三只小猪ABC,请分别输入三只小猪的体重,并且判断哪只小猪最重?[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CQ0ZUuNG-1599301407527)(assets/三只小猪.jpg)]

    4.1.2 三目运算符

    作用: 通过三目运算符实现简单的判断

    语法:表达式1 ? 表达式2 :表达式3

    解释:

    如果表达式1的值为真,执行表达式2,并返回表达式2的结果;

    如果表达式1的值为假,执行表达式3,并返回表达式3的结果。

    示例:

    int main() {
    
    	int a = 10;
    	int b = 20;
    	int c = 0;
    
    	c = a > b ? a : b;
    	cout << "c = " << c << endl;
    
    	//C++中三目运算符返回的是变量,可以继续赋值
    
    	(a > b ? a : b) = 100;
    
    	cout << "a = " << a << endl;
    	cout << "b = " << b << endl;
    	cout << "c = " << c << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    总结:和if语句比较,三目运算符优点是短小整洁,缺点是如果用嵌套,结构不清晰

    4.1.3 switch语句

    **作用:**执行多条件分支语句

    语法:

    switch(表达式)
    
    {
    
    	case 结果1:执行语句;break;
    
    	case 结果2:执行语句;break;
    
    	...
    
    	default:执行语句;break;
    
    }
    
    

    示例:

    int main() {
    
    	//请给电影评分 
    	//10 ~ 9   经典   
    	// 8 ~ 7   非常好
    	// 6 ~ 5   一般
    	// 5分以下 烂片
    
    	int score = 0;
    	cout << "请给电影打分" << endl;
    	cin >> score;
    
    	switch (score)
    	{
    	case 10:
    	case 9:
    		cout << "经典" << endl;
    		break;
    	case 8:
    		cout << "非常好" << endl;
    		break;
    	case 7:
    	case 6:
    		cout << "一般" << endl;
    		break;
    	default:
    		cout << "烂片" << endl;
    		break;
    	}
    
    	system("pause");
    
    	return 0;
    }
    

    注意1:switch语句中表达式类型只能是整型或者字符型

    注意2:case里如果没有break,那么程序会一直向下执行

    总结:与if语句比,对于多条件判断时,switch的结构清晰,执行效率高,缺点是switch不可以判断区间

    4.2 循环结构

    4.2.1 while循环语句

    **作用:**满足循环条件,执行循环语句

    语法:while(循环条件){ 循环语句 }

    解释:只要循环条件的结果为真,就执行循环语句

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iityGr12-1599301407528)(assets/clip_image002-1541668640382.png)]

    示例:

    int main() {
    
    	int num = 0;
    	while (num < 10)
    	{
    		cout << "num = " << num << endl;
    		num++;
    	}
    	
    	system("pause");
    
    	return 0;
    }
    

    注意:在执行循环语句时候,程序必须提供跳出循环的出口,否则出现死循环

    while循环练习案例:猜数字

    **案例描述:**系统随机生成一个1到100之间的数字,玩家进行猜测,如果猜错,提示玩家数字过大或过小,如果猜对恭喜玩家胜利,并且退出游戏。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JaNbi9Gk-1599301407529)(assets/猜数字.jpg)]

    4.2.2 do…while循环语句

    作用: 满足循环条件,执行循环语句

    语法: do{ 循环语句 } while(循环条件);

    **注意:**与while的区别在于do…while会先执行一次循环语句,再判断循环条件

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r1P72pnk-1599301407530)(assets/clip_image002-1541671163478.png)]

    示例:

    int main() {
    
    	int num = 0;
    
    	do
    	{
    		cout << num << endl;
    		num++;
    
    	} while (num < 10);
    	
    	
    	system("pause");
    
    	return 0;
    }
    

    总结:与while循环区别在于,do…while先执行一次循环语句,再判断循环条件

    练习案例:水仙花数

    **案例描述:**水仙花数是指一个 3 位数,它的每个位上的数字的 3次幂之和等于它本身

    例如:1^3 + 5^3+ 3^3 = 153

    请利用do…while语句,求出所有3位数中的水仙花数

    4.2.3 for循环语句

    作用: 满足循环条件,执行循环语句

    语法:for(起始表达式;条件表达式;末尾循环体) { 循环语句; }

    示例:

    int main() {
    
    	for (int i = 0; i < 10; i++)
    	{
    		cout << i << endl;
    	}
    	
    	system("pause");
    
    	return 0;
    }
    

    详解:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-roSwB0El-1599301407530)(assets/1541673704101.png)]

    注意:for循环中的表达式,要用分号进行分隔

    总结:while , do…while, for都是开发中常用的循环语句,for循环结构比较清晰,比较常用

    练习案例:敲桌子

    案例描述:从1开始数到数字100, 如果数字个位含有7,或者数字十位含有7,或者该数字是7的倍数,我们打印敲桌子,其余数字直接打印输出。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-29Xz5fOb-1599301407531)(assets/timg.gif)]

    4.2.4 嵌套循环

    作用: 在循环体中再嵌套一层循环,解决一些实际问题

    例如我们想在屏幕中打印如下图片,就需要利用嵌套循环

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kp4qPIq9-1599301407532)(assets/1541676003486.png)]

    示例:

    int main() {
    
    	//外层循环执行1次,内层循环执行1轮
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			cout << "*" << " ";
    		}
    		cout << endl;
    	}
    
    	system("pause");
    
    	return 0;
    }
    

    **练习案例:**乘法口诀表

    案例描述:利用嵌套循环,实现九九乘法表

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nggL56CD-1599301407533)(assets/0006018857256120_b.jpg)]

    4.3 跳转语句

    4.3.1 break语句

    作用: 用于跳出选择结构或者循环结构

    break使用的时机:

    • 出现在switch条件语句中,作用是终止case并跳出switch
    • 出现在循环语句中,作用是跳出当前的循环语句
    • 出现在嵌套循环中,跳出最近的内层循环语句

    示例1:

    int main() {
    	//1、在switch 语句中使用break
    	cout << "请选择您挑战副本的难度:" << endl;
    	cout << "1、普通" << endl;
    	cout << "2、中等" << endl;
    	cout << "3、困难" << endl;
    
    	int num = 0;
    
    	cin >> num;
    
    	switch (num)
    	{
    	case 1:
    		cout << "您选择的是普通难度" << endl;
    		break;
    	case 2:
    		cout << "您选择的是中等难度" << endl;
    		break;
    	case 3:
    		cout << "您选择的是困难难度" << endl;
    		break;
    	}
    
    	system("pause");
    
    	return 0;
    }
    

    示例2:

    int main() {
    	//2、在循环语句中用break
    	for (int i = 0; i < 10; i++)
    	{
    		if (i == 5)
    		{
    			break; //跳出循环语句
    		}
    		cout << i << endl;
    	}
    
    	system("pause");
    
    	return 0;
    }
    

    示例3:

    int main() {
    	//在嵌套循环语句中使用break,退出内层循环
    	for (int i = 0; i < 10; i++)
    	{
    		for (int j = 0; j < 10; j++)
    		{
    			if (j == 5)
    			{
    				break;
    			}
    			cout << "*" << " ";
    		}
    		cout << endl;
    	}
    	
    	system("pause");
    
    	return 0;
    }
    

    4.3.2 continue语句

    **作用:**在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环

    示例:

    int main() {
    
    	for (int i = 0; i < 100; i++)
    	{
    		if (i % 2 == 0)
    		{
    			continue;
    		}
    		cout << i << endl;
    	}
    	
    	system("pause");
    
    	return 0;
    }
    

    注意:continue并没有使整个循环终止,而break会跳出循环

    4.3.3 goto语句

    **作用:**可以无条件跳转语句

    语法: goto 标记;

    **解释:**如果标记的名称存在,执行到goto语句时,会跳转到标记的位置

    示例:

    int main() {
    
    	cout << "1" << endl;
    
    	goto FLAG;
    
    	cout << "2" << endl;
    	cout << "3" << endl;
    	cout << "4" << endl;
    
    	FLAG:
    
    	cout << "5" << endl;
    	
    	system("pause");
    
    	return 0;
    }
    

    注意:在程序中不建议使用goto语句,以免造成程序流程混乱

    5 数组

    5.1 概述

    所谓数组,就是一个集合,里面存放了相同类型的数据元素

    **特点1:**数组中的每个数据元素都是相同的数据类型

    **特点2:**数组是由连续的内存位置组成的

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cOenFQXL-1599301407534)(assets/1541748375356.png)]

    5.2 一维数组

    5.2.1 一维数组定义方式

    一维数组定义的三种方式:

    1. 数据类型 数组名[ 数组长度 ];
    2. 数据类型 数组名[ 数组长度 ] = { 值1,值2 ...};
    3. 数据类型 数组名[ ] = { 值1,值2 ...};

    示例

    int main() {
    
    	//定义方式1
    	//数据类型 数组名[元素个数];
    	int score[10];
    
    	//利用下标赋值
    	score[0] = 100;
    	score[1] = 99;
    	score[2] = 85;
    
    	//利用下标输出
    	cout << score[0] << endl;
    	cout << score[1] << endl;
    	cout << score[2] << endl;
    
    
    	//第二种定义方式
    	//数据类型 数组名[元素个数] =  {值1,值2 ,值3 ...};
    	//如果{}内不足10个数据,剩余数据用0补全
    	int score2[10] = { 100, 90,80,70,60,50,40,30,20,10 };
    	
    	//逐个输出
    	//cout << score2[0] << endl;
    	//cout << score2[1] << endl;
    
    	//一个一个输出太麻烦,因此可以利用循环进行输出
    	for (int i = 0; i < 10; i++)
    	{
    		cout << score2[i] << endl;
    	}
    
    	//定义方式3
    	//数据类型 数组名[] =  {值1,值2 ,值3 ...};
    	int score3[] = { 100,90,80,70,60,50,40,30,20,10 };
    
    	for (int i = 0; i < 10; i++)
    	{
    		cout << score3[i] << endl;
    	}
    
    	system("pause");
    
    	return 0;
    }
    

    总结1:数组名的命名规范与变量名命名规范一致,不要和变量重名

    总结2:数组中下标是从0开始索引

    5.2.2 一维数组数组名

    一维数组名称的用途

    1. 可以统计整个数组在内存中的长度
    2. 可以获取数组在内存中的首地址

    示例:

    int main() {
    
    	//数组名用途
    	//1、可以获取整个数组占用内存空间大小
    	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    
    	cout << "整个数组所占内存空间为: " << sizeof(arr) << endl;
    	cout << "每个元素所占内存空间为: " << sizeof(arr[0]) << endl;
    	cout << "数组的元素个数为: " << sizeof(arr) / sizeof(arr[0]) << endl;
    
    	//2、可以通过数组名获取到数组首地址
    	cout << "数组首地址为: " << (int)arr << endl;
    	cout << "数组中第一个元素地址为: " << (int)&arr[0] << endl;
    	cout << "数组中第二个元素地址为: " << (int)&arr[1] << endl;
    
    	//arr = 100; 错误,数组名是常量,因此不可以赋值
    
    
    	system("pause");
    
    	return 0;
    }
    

    注意:数组名是常量,不可以赋值

    总结1:直接打印数组名,可以查看数组所占内存的首地址

    总结2:对数组名进行sizeof,可以获取整个数组占内存空间的大小

    练习案例1:五只小猪称体重

    案例描述:

    在一个数组中记录了五只小猪的体重,如:int arr[5] = {300,350,200,400,250};

    找出并打印最重的小猪体重。

    **练习案例2:**数组元素逆置

    **案例描述:**请声明一个5个元素的数组,并且将元素逆置.

    (如原数组元素为:1,3,2,5,4;逆置后输出结果为:4,5,2,3,1);

    5.2.3 冒泡排序

    作用: 最常用的排序算法,对数组内元素进行排序

    1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
    2. 对每一对相邻元素做同样的工作,执行完毕后,找到第一个最大值。
    3. 重复以上的步骤,每次比较次数-1,直到不需要比较

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2khLjoZH-1599301407534)(assets/1541905327273.png)]

    示例: 将数组 { 4,2,8,0,5,7,1,3,9 } 进行升序排序

    int main() {
    
    	int arr[9] = { 4,2,8,0,5,7,1,3,9 };
    
    	for (int i = 0; i < 9 - 1; i++)
    	{
    		for (int j = 0; j < 9 - 1 - i; j++)
    		{
    			if (arr[j] > arr[j + 1])
    			{
    				int temp = arr[j];
    				arr[j] = arr[j + 1];
    				arr[j + 1] = temp;
    			}
    		}
    	}
    
    	for (int i = 0; i < 9; i++)
    	{
    		cout << arr[i] << endl;
    	}
        
    	system("pause");
    
    	return 0;
    }
    

    5.3 二维数组

    二维数组就是在一维数组上,多加一个维度。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ql8rmRUc-1599301407535)(assets/1541905559138.png)]

    5.3.1 二维数组定义方式

    二维数组定义的四种方式:

    1. 数据类型 数组名[ 行数 ][ 列数 ];
    2. 数据类型 数组名[ 行数 ][ 列数 ] = { {数据1,数据2 } ,{数据3,数据4 } };
    3. 数据类型 数组名[ 行数 ][ 列数 ] = { 数据1,数据2,数据3,数据4};
    4. 数据类型 数组名[ ][ 列数 ] = { 数据1,数据2,数据3,数据4};

    建议:以上4种定义方式,利用第二种更加直观,提高代码的可读性

    示例:

    int main() {
    
    	//方式1  
    	//数组类型 数组名 [行数][列数]
    	int arr[2][3];
    	arr[0][0] = 1;
    	arr[0][1] = 2;
    	arr[0][2] = 3;
    	arr[1][0] = 4;
    	arr[1][1] = 5;
    	arr[1][2] = 6;
    
    	for (int i = 0; i < 2; i++)
    	{
    		for (int j = 0; j < 3; j++)
    		{
    			cout << arr[i][j] << " ";
    		}
    		cout << endl;
    	}
    
    	//方式2 
    	//数据类型 数组名[行数][列数] = { {数据1,数据2 } ,{数据3,数据4 } };
    	int arr2[2][3] =
    	{
    		{1,2,3},
    		{4,5,6}
    	};
    
    	//方式3
    	//数据类型 数组名[行数][列数] = { 数据1,数据2 ,数据3,数据4  };
    	int arr3[2][3] = { 1,2,3,4,5,6 }; 
    
    	//方式4 
    	//数据类型 数组名[][列数] = { 数据1,数据2 ,数据3,数据4  };
    	int arr4[][3] = { 1,2,3,4,5,6 };
    	
    	system("pause");
    
    	return 0;
    }
    

    总结:在定义二维数组时,如果初始化了数据,可以省略行数

    5.3.2 二维数组数组名

    • 查看二维数组所占内存空间
    • 获取二维数组首地址

    示例:

    int main() {
    
    	//二维数组数组名
    	int arr[2][3] =
    	{
    		{1,2,3},
    		{4,5,6}
    	};
    
    	cout << "二维数组大小: " << sizeof(arr) << endl;
    	cout << "二维数组一行大小: " << sizeof(arr[0]) << endl;
    	cout << "二维数组元素大小: " << sizeof(arr[0][0]) << endl;
    
    	cout << "二维数组行数: " << sizeof(arr) / sizeof(arr[0]) << endl;
    	cout << "二维数组列数: " << sizeof(arr[0]) / sizeof(arr[0][0]) << endl;
    
    	//地址
    	cout << "二维数组首地址:" << arr << endl;
    	cout << "二维数组第一行地址:" << arr[0] << endl;
    	cout << "二维数组第二行地址:" << arr[1] << endl;
    
    	cout << "二维数组第一个元素地址:" << &arr[0][0] << endl;
    	cout << "二维数组第二个元素地址:" << &arr[0][1] << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    总结1:二维数组名就是这个数组的首地址

    总结2:对二维数组名进行sizeof时,可以获取整个二维数组占用的内存空间大小

    5.3.3 二维数组应用案例

    考试成绩统计:

    案例描述:有三名同学(张三,李四,王五),在一次考试中的成绩分别如下表,请分别输出三名同学的总成绩

    语文 数学 英语
    张三 100 100 100
    李四 90 50 100
    王五 60 70 80

    参考答案:

    int main() {
    
    	int scores[3][3] =
    	{
    		{100,100,100},
    		{90,50,100},
    		{60,70,80},
    	};
    
    	string names[3] = { "张三","李四","王五" };
    
    	for (int i = 0; i < 3; i++)
    	{
    		int sum = 0;
    		for (int j = 0; j < 3; j++)
    		{
    			sum += scores[i][j];
    		}
    		cout << names[i] << "同学总成绩为: " << sum << endl;
    	}
    
    	system("pause");
    
    	return 0;
    }
    

    6 函数

    6.1 概述

    **作用:**将一段经常使用的代码封装起来,减少重复代码

    一个较大的程序,一般分为若干个程序块,每个模块实现特定的功能。

    6.2 函数的定义

    函数的定义一般主要有5个步骤:

    1、返回值类型

    2、函数名

    3、参数表列

    4、函数体语句

    5、return 表达式

    语法:

    返回值类型 函数名 (参数列表)
    {
    
           函数体语句
    
           return表达式
    
    }
    
    • 返回值类型 :一个函数可以返回一个值。在函数定义中
    • 函数名:给函数起个名称
    • 参数列表:使用该函数时,传入的数据
    • 函数体语句:花括号内的代码,函数内需要执行的语句
    • return表达式: 和返回值类型挂钩,函数执行完后,返回相应的数据

    **示例:**定义一个加法函数,实现两个数相加

    //函数定义
    int add(int num1, int num2)
    {
    	int sum = num1 + num2;
    	return sum;
    }
    

    6.3 函数的调用

    **功能:**使用定义好的函数

    语法:函数名(参数)

    示例:

    //函数定义
    int add(int num1, int num2) //定义中的num1,num2称为形式参数,简称形参
    {
    	int sum = num1 + num2;
    	return sum;
    }
    
    int main() {
    
    	int a = 10;
    	int b = 10;
    	//调用add函数
    	int sum = add(a, b);//调用时的a,b称为实际参数,简称实参
    	cout << "sum = " << sum << endl;
    
    	a = 100;
    	b = 100;
    
    	sum = add(a, b);
    	cout << "sum = " << sum << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    总结:函数定义里小括号内称为形参,函数调用时传入的参数称为实参

    6.4 值传递

    • 所谓值传递,就是函数调用时实参将数值传入给形参
    • 值传递时,如果形参发生,并不会影响实参

    示例:

    void swap(int num1, int num2)
    {
    	cout << "交换前:" << endl;
    	cout << "num1 = " << num1 << endl;
    	cout << "num2 = " << num2 << endl;
    
    	int temp = num1;
    	num1 = num2;
    	num2 = temp;
    
    	cout << "交换后:" << endl;
    	cout << "num1 = " << num1 << endl;
    	cout << "num2 = " << num2 << endl;
    
    	//return ; 当函数声明时候,不需要返回值,可以不写return
    }
    
    int main() {
    
    	int a = 10;
    	int b = 20;
    
    	swap(a, b);
    
    	cout << "mian中的 a = " << a << endl;
    	cout << "mian中的 b = " << b << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    总结: 值传递时,形参是修饰不了实参的

    6.5 函数的常见样式

    常见的函数样式有4种

    1. 无参无返
    2. 有参无返
    3. 无参有返
    4. 有参有返

    示例:

    //函数常见样式
    //1、 无参无返
    void test01()
    {
    	//void a = 10; //无类型不可以创建变量,原因无法分配内存
    	cout << "this is test01" << endl;
    	//test01(); 函数调用
    }
    
    //2、 有参无返
    void test02(int a)
    {
    	cout << "this is test02" << endl;
    	cout << "a = " << a << endl;
    }
    
    //3、无参有返
    int test03()
    {
    	cout << "this is test03 " << endl;
    	return 10;
    }
    
    //4、有参有返
    int test04(int a, int b)
    {
    	cout << "this is test04 " << endl;
    	int sum = a + b;
    	return sum;
    }
    

    6.6 函数的声明

    作用: 告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。

    • 函数的声明可以多次,但是函数的定义只能有一次

    示例:

    //声明可以多次,定义只能一次
    //声明
    int max(int a, int b);
    int max(int a, int b);
    //定义
    int max(int a, int b)
    {
    	return a > b ? a : b;
    }
    
    int main() {
    
    	int a = 100;
    	int b = 200;
    
    	cout << max(a, b) << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    6.7 函数的分文件编写

    **作用:**让代码结构更加清晰

    函数分文件编写一般有4个步骤

    1. 创建后缀名为.h的头文件
    2. 创建后缀名为.cpp的源文件
    3. 在头文件中写函数的声明
    4. 在源文件中写函数的定义

    示例:

    //swap.h文件
    #include<iostream>
    using namespace std;
    
    //实现两个数字交换的函数声明
    void swap(int a, int b);
    
    
    //swap.cpp文件
    #include "swap.h"
    
    void swap(int a, int b)
    {
    	int temp = a;
    	a = b;
    	b = temp;
    
    	cout << "a = " << a << endl;
    	cout << "b = " << b << endl;
    }
    
    //main函数文件
    #include "swap.h"
    int main() {
    
    	int a = 100;
    	int b = 200;
    	swap(a, b);
    
    	system("pause");
    
    	return 0;
    }
    
    

    7 指针

    7.1 指针的基本概念

    指针的作用: 可以通过指针间接访问内存

    • 内存编号是从0开始记录的,一般用十六进制数字表示

    • 可以利用指针变量保存地址

    7.2 指针变量的定义和使用

    指针变量定义语法: 数据类型 * 变量名;

    示例:

    int main() {
    
    	//1、指针的定义
    	int a = 10; //定义整型变量a
    	
    	//指针定义语法: 数据类型 * 变量名 ;
    	int * p;
    
    	//指针变量赋值
    	p = &a; //指针指向变量a的地址
    	cout << &a << endl; //打印数据a的地址
    	cout << p << endl;  //打印指针变量p
    
    	//2、指针的使用
    	//通过*操作指针变量指向的内存
    	cout << "*p = " << *p << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    指针变量和普通变量的区别

    • 普通变量存放的是数据,指针变量存放的是地址
    • 指针变量可以通过" * "操作符,操作指针变量指向的内存空间,这个过程称为解引用

    总结1: 我们可以通过 & 符号 获取变量的地址

    总结2:利用指针可以记录地址

    总结3:对指针变量解引用,可以操作指针指向的内存

    7.3 指针所占内存空间

    提问:指针也是种数据类型,那么这种数据类型占用多少内存空间?

    示例:

    int main() {
    
    	int a = 10;
    
    	int * p;
    	p = &a; //指针指向数据a的地址
    
    	cout << *p << endl; //* 解引用
    	cout << sizeof(p) << endl;
    	cout << sizeof(char *) << endl;
    	cout << sizeof(float *) << endl;
    	cout << sizeof(double *) << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    总结:所有指针类型在32位操作系统下是4个字节

    7.4 空指针和野指针

    空指针:指针变量指向内存中编号为0的空间

    **用途:**初始化指针变量

    **注意:**空指针指向的内存是不可以访问的

    示例1:空指针

    int main() {
    
    	//指针变量p指向内存地址编号为0的空间
    	int * p = NULL;
    
    	//访问空指针报错 
    	//内存编号0 ~255为系统占用内存,不允许用户访问
    	cout << *p << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    野指针:指针变量指向非法的内存空间

    示例2:野指针

    int main() {
    
    	//指针变量p指向内存地址编号为0x1100的空间
    	int * p = (int *)0x1100;
    
    	//访问野指针报错 
    	cout << *p << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    总结:空指针和野指针都不是我们申请的空间,因此不要访问。

    7.5 const修饰指针

    const修饰指针有三种情况

    1. const修饰指针 — 常量指针
    2. const修饰常量 — 指针常量
    3. const即修饰指针,又修饰常量

    示例:

    int main() {
    
    	int a = 10;
    	int b = 10;
    
    	//const修饰的是指针,指针指向可以改,指针指向的值不可以更改
    	const int * p1 = &a; 
    	p1 = &b; //正确
    	//*p1 = 100;  报错
    	
    
    	//const修饰的是常量,指针指向不可以改,指针指向的值可以更改
    	int * const p2 = &a;
    	//p2 = &b; //错误
    	*p2 = 100; //正确
    
        //const既修饰指针又修饰常量
    	const int * const p3 = &a;
    	//p3 = &b; //错误
    	//*p3 = 100; //错误
    
    	system("pause");
    
    	return 0;
    }
    

    技巧:看const右侧紧跟着的是指针还是常量, 是指针就是常量指针,是常量就是指针常量

    7.6 指针和数组

    **作用:**利用指针访问数组中元素

    示例:

    int main() {
    
    	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    
    	int * p = arr;  //指向数组的指针
    
    	cout << "第一个元素: " << arr[0] << endl;
    	cout << "指针访问第一个元素: " << *p << endl;
    
    	for (int i = 0; i < 10; i++)
    	{
    		//利用指针遍历数组
    		cout << *p << endl;
    		p++;
    	}
    
    	system("pause");
    
    	return 0;
    }
    

    7.7 指针和函数

    **作用:**利用指针作函数参数,可以修改实参的值

    示例:

    //值传递
    void swap1(int a ,int b)
    {
    	int temp = a;
    	a = b; 
    	b = temp;
    }
    //地址传递
    void swap2(int * p1, int *p2)
    {
    	int temp = *p1;
    	*p1 = *p2;
    	*p2 = temp;
    }
    
    int main() {
    
    	int a = 10;
    	int b = 20;
    	swap1(a, b); // 值传递不会改变实参
    
    	swap2(&a, &b); //地址传递会改变实参
    
    	cout << "a = " << a << endl;
    
    	cout << "b = " << b << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    总结:如果不想修改实参,就用值传递,如果想修改实参,就用地址传递

    7.8 指针、数组、函数

    **案例描述:**封装一个函数,利用冒泡排序,实现对整型数组的升序排序

    例如数组:int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };

    示例:

    //冒泡排序函数
    void bubbleSort(int * arr, int len)  //int * arr 也可以写为int arr[]
    {
    	for (int i = 0; i < len - 1; i++)
    	{
    		for (int j = 0; j < len - 1 - i; j++)
    		{
    			if (arr[j] > arr[j + 1])
    			{
    				int temp = arr[j];
    				arr[j] = arr[j + 1];
    				arr[j + 1] = temp;
    			}
    		}
    	}
    }
    
    //打印数组函数
    void printArray(int arr[], int len)
    {
    	for (int i = 0; i < len; i++)
    	{
    		cout << arr[i] << endl;
    	}
    }
    
    int main() {
    
    	int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };
    	int len = sizeof(arr) / sizeof(int);
    
    	bubbleSort(arr, len);
    
    	printArray(arr, len);
    
    	system("pause");
    
    	return 0;
    }
    

    总结:当数组名传入到函数作为参数时,被退化为指向首元素的指针

    8 结构体

    8.1 结构体基本概念

    结构体属于用户自定义的数据类型,允许用户存储不同的数据类型

    8.2 结构体定义和使用

    语法:struct 结构体名 { 结构体成员列表 };

    通过结构体创建变量的方式有三种:

    • struct 结构体名 变量名
    • struct 结构体名 变量名 = { 成员1值 , 成员2值…}
    • 定义结构体时顺便创建变量

    示例:

    //结构体定义
    struct student
    {
    	//成员列表
    	string name;  //姓名
    	int age;      //年龄
    	int score;    //分数
    }stu3; //结构体变量创建方式3 
    
    
    int main() {
    
    	//结构体变量创建方式1
    	struct student stu1; //struct 关键字可以省略
    
    	stu1.name = "张三";
    	stu1.age = 18;
    	stu1.score = 100;
    	
    	cout << "姓名:" << stu1.name << " 年龄:" << stu1.age  << " 分数:" << stu1.score << endl;
    
    	//结构体变量创建方式2
    	struct student stu2 = { "李四",19,60 };
    
    	cout << "姓名:" << stu2.name << " 年龄:" << stu2.age  << " 分数:" << stu2.score << endl;
    
    
    	stu3.name = "王五";
    	stu3.age = 18;
    	stu3.score = 80;
    	
    
    	cout << "姓名:" << stu3.name << " 年龄:" << stu3.age  << " 分数:" << stu3.score << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    总结1:定义结构体时的关键字是struct,不可省略

    总结2:创建结构体变量时,关键字struct可以省略

    总结3:结构体变量利用操作符 ‘’.’’ 访问成员

    8.3 结构体数组

    **作用:**将自定义的结构体放入到数组中方便维护

    语法:struct 结构体名 数组名[元素个数] = { {} , {} , ... {} }

    示例:

    //结构体定义
    struct student
    {
    	//成员列表
    	string name;  //姓名
    	int age;      //年龄
    	int score;    //分数
    }
    
    int main() {
    	
    	//结构体数组
    	struct student arr[3]=
    	{
    		{"张三",18,80 },
    		{"李四",19,60 },
    		{"王五",20,70 }
    	};
    
    	for (int i = 0; i < 3; i++)
    	{
    		cout << "姓名:" << arr[i].name << " 年龄:" << arr[i].age << " 分数:" << arr[i].score << endl;
    	}
    
    	system("pause");
    
    	return 0;
    }
    

    8.4 结构体指针

    **作用:**通过指针访问结构体中的成员

    • 利用操作符 ->可以通过结构体指针访问结构体属性

    示例:

    //结构体定义
    struct student
    {
    	//成员列表
    	string name;  //姓名
    	int age;      //年龄
    	int score;    //分数
    };
    
    
    int main() {
    	
    	struct student stu = { "张三",18,100, };
    	
    	struct student * p = &stu;
    	
    	p->score = 80; //指针通过 -> 操作符可以访问成员
    
    	cout << "姓名:" << p->name << " 年龄:" << p->age << " 分数:" << p->score << endl;
    	
    	system("pause");
    
    	return 0;
    }
    

    总结:结构体指针可以通过 -> 操作符 来访问结构体中的成员

    8.5 结构体嵌套结构体

    作用: 结构体中的成员可以是另一个结构体

    **例如:**每个老师辅导一个学员,一个老师的结构体中,记录一个学生的结构体

    示例:

    //学生结构体定义
    struct student
    {
    	//成员列表
    	string name;  //姓名
    	int age;      //年龄
    	int score;    //分数
    };
    
    //教师结构体定义
    struct teacher
    {
        //成员列表
    	int id; //职工编号
    	string name;  //教师姓名
    	int age;   //教师年龄
    	struct student stu; //子结构体 学生
    };
    
    
    int main() {
    
    	struct teacher t1;
    	t1.id = 10000;
    	t1.name = "老王";
    	t1.age = 40;
    
    	t1.stu.name = "张三";
    	t1.stu.age = 18;
    	t1.stu.score = 100;
    
    	cout << "教师 职工编号: " << t1.id << " 姓名: " << t1.name << " 年龄: " << t1.age << endl;
    	
    	cout << "辅导学员 姓名: " << t1.stu.name << " 年龄:" << t1.stu.age << " 考试分数: " << t1.stu.score << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    **总结:**在结构体中可以定义另一个结构体作为成员,用来解决实际问题

    8.6 结构体做函数参数

    **作用:**将结构体作为参数向函数中传递

    传递方式有两种:

    • 值传递
    • 地址传递

    示例:

    //学生结构体定义
    struct student
    {
    	//成员列表
    	string name;  //姓名
    	int age;      //年龄
    	int score;    //分数
    };
    
    //值传递
    void printStudent(student stu )
    {
    	stu.age = 28;
    	cout << "子函数中 姓名:" << stu.name << " 年龄: " << stu.age  << " 分数:" << stu.score << endl;
    }
    
    //地址传递
    void printStudent2(student *stu)
    {
    	stu->age = 28;
    	cout << "子函数中 姓名:" << stu->name << " 年龄: " << stu->age  << " 分数:" << stu->score << endl;
    }
    
    int main() {
    
    	student stu = { "张三",18,100};
    	//值传递
    	printStudent(stu);
    	cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl;
    
    	cout << endl;
    
    	//地址传递
    	printStudent2(&stu);
    	cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age  << " 分数:" << stu.score << endl;
    
    	system("pause");
    
    	return 0;
    }
    

    总结:如果不想修改主函数中的数据,用值传递,反之用地址传递

    8.7 结构体中 const使用场景

    **作用:**用const来防止误操作

    示例:

    //学生结构体定义
    struct student
    {
    	//成员列表
    	string name;  //姓名
    	int age;      //年龄
    	int score;    //分数
    };
    
    //const使用场景
    void printStudent(const student *stu) //加const防止函数体中的误操作
    {
    	//stu->age = 100; //操作失败,因为加了const修饰
    	cout << "姓名:" << stu->name << " 年龄:" << stu->age << " 分数:" << stu->score << endl;
    
    }
    
    int main() {
    
    	student stu = { "张三",18,100 };
    
    	printStudent(&stu);
    
    	system("pause");
    
    	return 0;
    }
    

    8.8 结构体案例

    8.8.1 案例1

    案例描述:

    学校正在做毕设项目,每名老师带领5个学生,总共有3名老师,需求如下

    设计学生和老师的结构体,其中在老师的结构体中,有老师姓名和一个存放5名学生的数组作为成员

    学生的成员有姓名、考试分数,创建数组存放3名老师,通过函数给每个老师及所带的学生赋值

    最终打印出老师数据以及老师所带的学生数据。

    示例:

    struct Student
    {
    	string name;
    	int score;
    };
    struct Teacher
    {
    	string name;
    	Student sArray[5];
    };
    
    void allocateSpace(Teacher tArray[] , int len)
    {
    	string tName = "教师";
    	string sName = "学生";
    	string nameSeed = "ABCDE";
    	for (int i = 0; i < len; i++)
    	{
    		tArray[i].name = tName + nameSeed[i];
    		
    		for (int j = 0; j < 5; j++)
    		{
    			tArray[i].sArray[j].name = sName + nameSeed[j];
    			tArray[i].sArray[j].score = rand() % 61 + 40;
    		}
    	}
    }
    
    void printTeachers(Teacher tArray[], int len)
    {
    	for (int i = 0; i < len; i++)
    	{
    		cout << tArray[i].name << endl;
    		for (int j = 0; j < 5; j++)
    		{
    			cout << "\t姓名:" << tArray[i].sArray[j].name << " 分数:" << tArray[i].sArray[j].score << endl;
    		}
    	}
    }
    
    int main() {
    
    	srand((unsigned int)time(NULL)); //随机数种子 头文件 #include <ctime>
    
    	Teacher tArray[3]; //老师数组
    
    	int len = sizeof(tArray) / sizeof(Teacher);
    
    	allocateSpace(tArray, len); //创建数据
    
    	printTeachers(tArray, len); //打印数据
    	
    	system("pause");
    
    	return 0;
    }
    

    8.8.2 案例2

    案例描述:

    设计一个英雄的结构体,包括成员姓名,年龄,性别;创建结构体数组,数组中存放5名英雄。

    通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排序,最终打印排序后的结果。

    五名英雄信息如下:

    		{"刘备",23,"男"},
    		{"关羽",22,"男"},
    		{"张飞",20,"男"},
    		{"赵云",21,"男"},
    		{"貂蝉",19,"女"},
    

    示例:

    //英雄结构体
    struct hero
    {
    	string name;
    	int age;
    	string sex;
    };
    //冒泡排序
    void bubbleSort(hero arr[] , int len)
    {
    	for (int i = 0; i < len - 1; i++)
    	{
    		for (int j = 0; j < len - 1 - i; j++)
    		{
    			if (arr[j].age > arr[j + 1].age)
    			{
    				hero temp = arr[j];
    				arr[j] = arr[j + 1];
    				arr[j + 1] = temp;
    			}
    		}
    	}
    }
    //打印数组
    void printHeros(hero arr[], int len)
    {
    	for (int i = 0; i < len; i++)
    	{
    		cout << "姓名: " << arr[i].name << " 性别: " << arr[i].sex << " 年龄: " << arr[i].age << endl;
    	}
    }
    
    int main() {
    
    	struct hero arr[5] =
    	{
    		{"刘备",23,"男"},
    		{"关羽",22,"男"},
    		{"张飞",20,"男"},
    		{"赵云",21,"男"},
    		{"貂蝉",19,"女"},
    	};
    
    	int len = sizeof(arr) / sizeof(hero); //获取数组元素个数
    
    	bubbleSort(arr, len); //排序
    
    	printHeros(arr, len); //打印
    
    	system("pause");
    
    	return 0;
    }
    

    后记

    相信能对着黑马视频学习到这里的人,已经迈出了万里长征的第一步,祝贺你!

    如果需要全套B站黑马C++视频讲义以及其他学习资料,烦请关注我偶然建立的个人公众号漏网小鱼,诚挚地邀请您扫描下方二维码,日常分享一些我(菜鸟)用过的学习资料、干货和学习方法,无门槛不劝退,我们一起打怪升级,期待与你的共同进步!后台回复“1”,免费获取百度云链接。
    在这里插入图片描述

    展开全文
  • C++函数返回多个值

    万次阅读 2017-06-30 11:42:10
    因为C++没有内置的从函数或方法返回多个值的语法,程序员在需要时使用各种技术来模拟之,而自C++11的引入,这个数量进一步提升。在本文里,我想提供我们今天所拥有的从函数返回多个值的某些选择的个概况,
  • 个lua与C++粘合框架

    千次阅读 2013-09-19 11:51:19
    背景: 这是之前那篇烂文章的个扩展吧!在游戏领域,特别多的使用到lua,作为...为了解决lua原生API的问题,就出现了一些框架、库来改善,比如lua++,luabind…,窃以为,luabind是史最强大的lua与C++粘合,无出
  • 使用C++编写个DHT爬虫,实现从DHT网络爬取BT种子

    万次阅读 多人点赞 2021-06-12 21:44:50
    使用C++编写个DHT爬虫,实现从DHT网络爬取BT种子1、相关术语1.1、P2P网络1.2、DHT网络1.3、Kademlia算法1.4、KRPC协议1.5、MagNet协议1.6、本段小结2、BT下载的一些概念梳理2.1、BT软件下载原理2.2、迅雷获取种子...
  • 我发现呀,这大家对面试题的需求还是很大的,这里总结了千道知识点,能换您个收藏吗
  • c++ 容器、继承层次、句柄类

    千次阅读 2013-11-17 21:37:22
    、容器与继承 在容器中保存有继承关系的对象,如果定义成保存基类对象,则派生类将被切割,如果定义成保存派生类对象,则保存基类对象又成问题(基类对象将被强制转换成派生类对象,而派生类中定义的成员未被初始...
  • C++二叉树层次输出

    千次阅读 2018-05-17 21:45:52
    //返回队列的第个元素 if (temp) { //如果是非空结点 cout << temp->data ; c.pop_front(); //出队列 c.push_back(temp->left); //左孩子 c.push_back(temp->right); //右孩子 } else { c....
  • 看到个介绍 C++17 的系列博文(原文),有十来篇的样子,觉得挺好,看看有时间能不能都简单翻译一下,这是第三篇~ 在之前的文章中我介绍了一些C++17语言核心的变化,这次我会介绍更多的相关细节,涉及的主题有:内联...
  • C++实现个ping命令

    千次阅读 2017-06-04 16:30:22
    今天利用C++实现个自己的ping命令,首先我们在Linux下运行下ping命令,看它完成了那些功能: 1.向目标IP发送个64字节的ICMP请求包,然后收到目标IP的个ICMP应答包,并逐个打印发送请求包到接收应答包...
  • 使用c++构建个二叉树

    千次阅读 2014-11-09 13:18:46
    How to create a binary tree by c++   The binary tree is a fundamental data structure used in computer science. The binary tree is a useful data structure for rapidly ...
  • 这两天在看OSG的插件实现的源代码的过程中发现了个让我比较纳闷的问题:比如在OBJ插件中,其实现的ReadWriterOBJ类的readNode函数,代码如下: osgDB::ReaderWriter::ReadResult ReaderWriterOBJ::readNode(std...
  • (C/C++)函数参数传递和返回机制

    千次阅读 2017-01-17 23:19:42
    引用这概念在C++里面提出的,了解函数的传递以及返回机制是十分重要的,这里主要用C++来描述。 :变参函数的调用机制: 对于C语言,它的调用规则遵循_cdedl调用规则。 在_cdedl规则中: 1.参数从右到左依次...
  • C++封装个易用的打印backtrace信息的函数

    千次阅读 多人点赞 2021-05-21 22:09:15
    C++封装个易用的打印backtrace信息的函数1、前言2、几个需要用到函数2.1、backtrace函数2.2、backtrace_symbols函数2.3、__cxa_demangle函数3、测试各个函数的使用3.1、使用backtrace()获取到的堆栈信息3.2、使用...
  • c++程序设计基础(第五版)() 习题与解答

    万次阅读 多人点赞 2019-09-09 20:06:44
    C++程序设计基础(第5版)() 习题与解答 第1章练习题 同步练习1.1 、选择题 1.个最简单的C++程序,可以只有个( )。 (A)库函数 (B)自定义函数 (C)main函数 (D)空函数 2.函数名是( ),用于...
  • 怎样从个DLL中导出C++

    万次阅读 2008-10-08 16:58:00
    原文作者:Alex Blekhman 翻译:朱金灿 原文来源:http://www.codeproject.com/KB/cpp/howto_export_cpp_classes.aspx 译文来源:http://blog.csdn.net/clever101 C++语言毕竟能和Windows DLLs能够和平共处。...
  • C++打开个网页

    千次阅读 2015-11-20 15:54:36
    SW_MINIMIZE 最小化,并把Z order顺序在此视窗之后(即视窗下一层)的视窗启动 SW_RESTORE 启动视窗并还原为初始化大小 SW_SHOW 以当前大小和状态启动视窗 SW_SHOWDEFAULT 以预设方式运行 SW_SHOWMAXIMIZED 启动视窗...
  • C++C++基础大杂烩(篇)

    千次阅读 2018-05-28 18:19:38
    本博客之前有C语言的教程,在C语言的基础进行C++的学习,会轻松蛮多。此文就简单地将一些不太同的地方进行点一点,主要涉及的章节就是C语言的那几个部分:数据类型、运算符、表达式、输入输出、流程控制、函数、预...
  • .变量的内存实质到 1.1变量的实质 1.2 赋值给变量 1.3变量在哪里? 二. 指针是个什么东西? 三. 二级指针(指针的指针) 3.1 定义与初始化 3.2间接数据访问 3.2.1.改变级指针指向 3.2.2改变 N-1 级指针...
  • C++ 设计个窗口类

    千次阅读 2017-10-29 23:16:36
    .步骤 - 设计个窗口类 - 注册窗口 - 创建窗口 - 显示及更新窗口二.窗口类分析typedef struct { UINT style; //类的类型 CS_HREDRAW | CS_VERDRAW 水平和垂直重画 WNDPROC lpfnWndProc; //窗口过程函数,是...
  • C++基础面试——

    万次阅读 多人点赞 2018-03-31 11:25:15
    面试中的C++常见问题1.在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern “C”?答:首先,extern是C/C++语言中表明函数和全局变量作用范围的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块...
  • 嵌入式C++开发详解(

    万次阅读 多人点赞 2017-02-07 00:03:21
    嵌入式C++开发详解(C++概述 1.嵌入式开发中为什么选择C++语言? (1)面向过程编程的特点 C语言特点:C语言是在实践的过程中逐步完善的 ·没有深思熟虑的设计过程 ·使用时存在很多“灰色地带” …… ...
  • C++ 国外程序员整理的 C++ 资源大全

    千次阅读 2015-06-26 14:20:39
    关于 C++ 框架、库和资源的一些汇总列表,由 fffaraz发起和维护。...C++ Standard Library:是系列类和函数的集合,使用核心语言编写,也是C++ISO自身标准的部分。Standard Template Library:标准模板库C POSIX l

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 162,952
精华内容 65,180
关键字:

c++返回上一层

c++ 订阅