C/C++

C++

1. c++

1.1 c++特征

# c++ 是编译型语言 编译后通过链接 整合其他依赖库 生成可执行文件 以后再运行 无需编译 执行效率高
# c++ 支持面向对象编程 同时处理运行速度块
# c++ 主要组成部分:核心语言(提供所有构建:变量 数据类型) + c++标准库(提供函数用于操作文本 字符串等) + 标准模块库(提供方法 用于操作数据结构等)

1.2 c++环境安装

#window10 安装c++编译器(MinGw, Cygwin)
MinGw下载:https://nchc.dl.sourceforge.net/project/mingw-w64/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/installer/mingw-w64-install.exe
配置系统环境变量:Path--->x:/mingw/bin
测试环境: g++ -v

#Linux(ubuntu)
sudo apt update   # 更新资源包列表
sudo apt install bulid-essential  # 安装bulid-essential软件包(gcc, g++等)
g++ -v(gcc:9.3.0)

#相关的开发工具
Visual Studio:https://visualstudio.microsoft.com/zh-hans/downloads/
Clion: https://www.jetbrains.com/clion/download/
JetBrains所有软件破解补丁:https://pan.baidu.com/s/1rRMi2k3NelLmE1-89xM4Mg   提取码:u3om


1.3 c++ 实例

//---->main.cpp
// 导入头文件-input output stream 输入输出流[python:外部的模块]
#include<iostream>

// 声明命名空间std
using namespace std;

//携带参数 int main(int arg )
int main(){
    // 把打印对象交给cout对象
    // std::endl 表示结束 会有换行,刷新缓冲区数据
    std::cout << "hello c++" << std::endl;
    // 函数执行结束的状态码
    return 0;
}

//小结:
//1.g++ main.cpp 直接编译成一个可执行文件(exe) 
//2.;分号是结束语句标识
//3.int _test; :至少要有空格 用于编译器编译区分
//4.int main{}: 主函数入口
//5.<<运算符用于向屏幕传多个值
//6. std 命名空间 决定函数 类 变量作用域  可以使用namespace 进行声明
//单行注释
/*多行注释*/


2. 变量

2.1 变量命名

//标识符用于识别变量&函数&类&模块
//标识符(变量)通常^[A-Z a-Z _] 且区分大小写
//C++关键字:https://www.runoob.com/w3cnote/cpp-keyword-intro.html

2.2 基本数据类型

Data Type Memory(byte) Format Specifier
int(整形) 4 %d
char(单个字符型) 1 %c
float(浮点型) 4 %f
double(双浮点型) 8 %lf
bool(布尔类型) 1 -
string(多个字符) 32 %s

//修饰符类型(添加在数据类型前)
//signed(有符号) unsigned long(双精度) short
//eg:unsigned a; <=> unsigned int a;

2.3 数据类型重命名

//通过typedef声明(对数据类型取别名)
typedef int bb;
bb length; <-> int length;

2.4 关键字

//1.const声明常量(初始化一次)
const int aa = 11; 
//常量固定值 可以是任何基本数据类型(int float char string bool) 在定义后不可进行修改 否则会出现编译错误

// 2.define 预处理 编译前执行
#define aa 11

//3.扩展c++11 auto关键字
auto i = 10; // 自动匹配合适的变量类型

//4.typedef type name重命名数据类型
typedef int i;
int number;<-> i number;

//5.static修饰符 (默认变量可在其他源文件使用使用static后只能在本源文件使用)
static int a;
//整数常量可携带后缀U(unsigned) L(long) 
// 浮点型可携带后缀e | E(指数类型)

//6.override 重写
class A{
  public:
    virtual void test(){}
};
class B:public A{
  void test() override{}  
};
//继承情况下用于检查1.父类函数是否为虚函数 2.子类函数是否重写父类函数

//7.final
class A final{};
// class B: public A{};  # 编译错误 final修饰的类 限制被子类继承

class C{
   virtual void test() final{}
}; 
class D{
    // void test(){}  # 编译错误 final修饰的虚函数 限制被子类重写 
}

2.4.1 const

#include <iostream>

using namespace std;

class v{
public:
    const int x=1;  // 常量成员属性 不可再修改
    //mutable int y;  // mutable修饰符属性 使常函数 常对象 可以修改成员属性值
    int y;
    int z;
    //构造函数
    v(){
        cout << "无参构造"<< endl;
    }

    void test(){
        //this: v*
        //x =3;  // 编译错误
        y=2, z=3;
        cout << x << " " << y << " " << z << endl;
        cout << "test...."<< endl;
    }

    // 常函数(对成员数据修改权限管理)
    void test1() const{
        //this: const* v
        // 1.不能修改成员属性数据  2.实则是修饰的this指针
        //y=2, z=3;  //编译错误
        //test();  // 常函数默认只能调用常函数
        test2();
        cout << "test1...."<< endl;
    }
    
    void test2() const{
        cout << "test2...."<< endl;
    }
};



int main(){
    // 常对象
    const v v1;  // 需要提供自定义构造函数
    v1.x;  // 可获取成员属性数据、
    v1.test1();  // 可调用对象函数
    //v1.y = 55;  // 默认不可对成员属性修改
    //v1.y = 55;  // 常函数&&常对象修改成员属性可使用关键字mutable 修饰成员属性

}
//常函数
//1.函数内部默认不可修改成员
//2.默认只可调用常函数
//3.所有对象均可调用常函数
//常对象
//1.默认只能调用常函数
//2.默认不可修改成员
//使用mutable 修饰符修饰成员函数 常对象常函数可对成员修改

2.4.2 auto

#include <iostream>

using namespace std;

int main(){
    // 关键字const
    const int a = 11;  // const 修饰符修饰的变量只能初始化一次 不可再进行修改
    //a = 22;// 报错
    
    //关键字auto
    auto i = 10;  // auto 修饰符修饰的变量自动匹配变量类型
    cout << i << endl;  // 10

}

2.4.3 static

2.4.3.1 变量-static

#include <iostream>

using namespace std;

static int a = 22;  // 静态类型变量初始化

int t1(){
    static int n = 0;
    n ++;
    cout << "n1=" << n << "\t";
    return n;
}
int t2(){
    int n = 0;
    n ++;
    cout << "n2=" << n << "\t";
    return n;
}

int main(){
    //关键字static
    cout << "静态全局变量a=" << a << endl;
    t1();
    t1();
    t1();
    t2();
    t2();
    t2();
    // n1=1    n1=2    n1=3
    // n2=1    n2=1    n2=1

}
//默认变量函数类均可在其他源文件使用(extern)   
//使用static 修饰符只能在本源文件使用
/* static修饰局部变量 只能在本源文件使用
 * 只初始化一次 保存在全局数据区 而非栈内存
 * 再调用值会是之前调用的结果
 *
 * 静态全局变量 相较于全局变量仅仅是只能在本地源文件使用
 * 函数类型加static 类似作用
 */



2.4.3.2 类-static

#include <iostream>

using namespace std;

class food{
public:
    static string name;  // 静态成员变量
    //static string name="xyz";  //编译错误
    int price;

    food(int price):price(price){
        cout << "构造函数"<< endl;
    }
    //静态成员函数
    static void test(){
        //cout << price << endl;  // 不能访问普通成员
        cout<< name << endl;  // 只能访问静态成员(函数 属性)
        cout << "test..."<< endl;
    }
    //普通成员函数
    void test1(){
        test();  // 能调用静态成员函数:对象已创建
        cout <<"test1..."<<endl;
    }

};

string food::name="xyz";  // 静态成员变量初始化

int main(){
    food f(11);
    //cout << f.name << endl;  // 不可直接对静态成员属性访问 需要初始化
    cout<<"静态成员属性初始化后---------------------"<< endl;
    cout<< f.name << endl;
    cout << f.price<< endl;
    cout << "------------------------------------"<< endl;
    food::test();  // 类名访问成员
    f.test();  // 使用对象访问成员

}
/* 静态成员属性
 * 1.不可在类中修改
 * 2.在类的外部进行 静态成员初始化才可访问 
 * 静态成员函数
 * 内部只能访问静态成员(函数 属性) 没有this指针
 * 静态成员不需要创建对象也可访问 通过类名::静态成员
 * 非静态成员需要创建对象 才能访问(静态&&非静态) 这些成员会分配到指定空间
 */

2.4.4 move

#include <iostream>

using namespace std;

int cal(int&& x, int && y){
    cout << x*y << endl;
    return x*y;
}


int main(){
    int x=3, y=2;
    //cal(x, y);  //必须接收右值、
    cal(3, 2);  // 6
    cal(move(x), move(y));  // 6
}

/* move函数
 * 1.使左值转换成右值
 * 2.对已有对象的数据转移到新对象时触发移动构造函数
 */

2.4.5 this

#include <iostream>

using namespace std;
class cls{
public:
    int id;
    cls(){
        cout << "无参构造"<< endl;
    }
    cls(int id){
        this->id = id;
        cout << "有参构造"<< endl;
    }
    
    void test(){
        cout << "test...."<< endl;
    }
    
    //链式调用
    cls test1(){
        cout << "test1...."<< endl;
        return *this;  // 返回当前对象
    }

    cls test2(){
        cout << "test2...."<< endl;
        return *this;  
    }
    ~cls(){
        cout << "析构"<< endl;
    }
};

int main(){
    //this指针
    cls c;
    //1.普通调用
    c.test();
    //2.链式调用
    c.test1().test2();

}
/* this指针
 * 1.表示当前对象的指针
 * 2.在类成员函数可以使用this指针
 * 3.用于在构造函数内部区分同名的变量(参数与成员属性名相同)
 * 4.链式调用时用于返回对象本身
 */

2.5 输入输出

2.5.1 一般输入输出

// 一般格式输入输出cout&&cin
#include <iostream>

using namespace std;

int main(){
    // 输入(通常和输入同时使用)
    cout<< "输入姓名:" << endl;
    string name;
    cin >> name; // cin关键字输入
    
    // 输出
    cout << "姓名:" << name << endl;
    //1.布尔类型
    bool n = false;
    cout << "n输出:" << n << endl;
    cout << "n输出:" << boolalpha <<  n << endl;
    cout << "----------------------------------"<< endl;
    //2.整形
    int n1 = 10;
    cout << "十进制:" << dec << n1 <<endl;
    cout << "八进制:" << oct << n1 <<endl;
    cout << "十六进制:" << hex << n1 <<endl;
    cout << "----------------------------------"<< endl;
    //3.浮点型(精确度 自动四舍五入)
    float pi = 3.14159263;
    cout << setprecision(4) << pi <<endl;
    return 0;
}

// 小结:cin&cout读取的是数据流 而非具体数据 输入的数据优先存入缓冲区 再从缓冲区输出

//scanf格式化输入: scanf(% + 格式选项 + 格式转换符, 变量地址(&变量))

// printf格式化输出: % + 格式选项 + 格式转换符
    /* 格式选项
     * w 输出的占位 若大于w 则实际输出
     * - 左对齐 默认右对齐
     * + 输出符号(+ -) 默认只输出-
     * # 八(十)进制的值非0 输出 格式(0x|0)
     * .p 输出精度 浮点数由p决定小数点后保留的数 字符串 左对齐输出p个字符
     */

    /* 格式转换符(eg:%D)
     * d 有符号的十进制整形(<=>%i)
     * u 无符号十进制整形
     * o 无符号八进制
     * x 无符号十六进制(小写)
     * X 无符号十六进制(大写)
     * c 单个字符('a')
     * f 以浮点型输出
     * e 以科学计数法形式输出浮点型(小写)
     * s char*字符串输出("example")
     * S wchar*字符串输出
     * p 指针的地址
     * g 自动选择格式化方式(输出最短)%f %e
     */

    /* 特殊字符
     * \n 换行
     * \f 清屏换页
     * \r 回车
     * \t tab符
     * \xhh ascii使用十六进制表示*/

2.5.2 格式化输入输出

// 格式化输入输出
//#include <cstdio> //格式化输入输出
#include <iostream>  

using namespace std;

int main(){
    // 格式化输入
    int A, B;
    scanf("%d\n%d",&A,&B);
    printf("%d+%d=%d\n",A,B, (A+B));
    // 格式化输出
    char a = 'a';
    long b = 20L;
    float c = 3.14;
    double d = 0.67891;
    int e = 0xfff;
    char f[] = "c++";
    string g = "go";
    printf("a=%c \n", a);
    printf("b=%d \n", b);
    printf("c=%.1f \n", c);
    printf("d=%4.2f \n", d);
    printf("e=%#x \n", e);
    printf("f=%s \n", f);
    printf("g=%s \n", g.c_str());
}
//out:
//1
//2
//1+2=3
//a=a
//b=20
//c=3.1
//d=0.68
//e=0xfff
//f=c++
//g=go

/* C++常用的几种格式化输入输出方式(函数)
 *scanf(); 控制台键盘输入
 *sscanf(); 字符串缓冲区输入与指定格式相符数据
 *fscanf(); 文件输入
 *printf(); 控制台输出
 *sprintf();
 *fprintf();
 */

2.6 运算符

2.6.1 算术运算符

#include <iostream>

using namespace std;

int main(){
    // 算术运算符
    /* +:加
     * -:减
     * *:乘
     * /:取商
     * %:取余
     * ++:自增(不能用于表达式:eg:(x+y)++)
     * --:自减
     */
    int a = 11, c = 10, e = 5;
    int b;
    b = a++; 
    cout << "b[a++]:" << b <<endl;
    cout << "a[a++]:" << a <<endl;
    b = ++a; // 优先执行a+1
    cout << "b[++a]:" <<b <<endl;
    cout << "a[++a]:" <<b <<endl;
    
    printf("%d(/)%d=%d \n", a, e, a/e);
    printf("%d(%)%d=%d \n", a, e, a%e);

    return 0;
}

//b[a++]:11
//a[a++]:12
//b[++a]:13
//a[++a]:13
//13(/)5=2
//13(%)5=3

2.6.2 关系运算符

#include <iostream>

using namespace std;

int main(){
    //关系运算符
    /* ==:等于
     * !=:不等于
     * >:大于
     * <:小于
     * >=:大于等于
     * <=:小于等于
     */
    int a , b;
    a = 10;
    b = 11;
    if(a == b){
       cout << "a==b" << endl;

    } else{
        cout << "a!=b" << endl;
    }
    return 0;
}

2.6.3逻辑运算符

#include <iostream>

using namespace std;

int main(){
    //逻辑运算符
    /* &&:and(python)
     * ||: or(python)
     * !: not(python)
     */
    int a , b;
    a = 1;
    b = 0;
    if (a&&b){
        cout << "a&&b:true" << endl;
    }
    if(a||b){
        cout << "a||b:true" << endl;
    }
    if(!(a&&b)){
        cout <<"!(a&&b):true" << endl;
    }

    return 0;
}

2.6.4 位运算符

// 位运算:按照二进制格式进行运算(char short  int long)
// 内存存储数据的基本单位是字节(byte) 一个字节=8位(bite);位是最小的数据量单位;
// 3(10) = 00000011(2)
#include <iostream>

using namespace std;

int main(){
    //位运算符
    /* &:位 "与"
     * |:位 "或"
     * ^: 位 "异或"
     * ~: 位 "取反"
     * <<: 左移
     * >>: 右移
     */
    int a , b;
    a = 1;
    b = 2;
    cout << "a&b:" << (a&b) << endl;  // a&b:0
    cout << "a|b:" << (a|b) << endl;  // a|b:3
    cout << "a^b:" << (a^b) << endl;  // a^b:3
    cout << "b<<1:" << (b<<1) << endl;  // b<<1:4
    cout << "b>>1:" << (b>>1) << endl;  // b>>1:1
    cout << "~b:" << (~b) << endl;  // ~b:-3

    return 0;
}


2.6.5 其他运算符

#include <iostream>

using namespace std;

int main(){
    // 指针运算符 *(取值) &(取址)
    int par;
    par = 10;
    cout << "par:" << par << endl;  // par:10
    cout << "&par:" << &par << endl;  // &par:0x61fe1c
    cout << "*(&par):" << *(&par) << endl;  // *(&par):10

    //sizeof关键字 返回变量||数据类型的字节大小
    cout << "char(size):" << sizeof(char) << endl;  // char(size):1
    cout << "int(size):" << sizeof(int) << endl;  // int(size):4
    cout << "float(size):" << sizeof(float) << endl;  // float(size):4

}

2.6.6 运算符重载

#include <iostream>
#include <functional>

using namespace std;
//运算符重载
class money{
public:
    int id;
    int value;
    money(){}

    money(int id, int value):id(id),value(value){}

    //运算符重载+函数(类内部)
    int operator+(money m){
        cout << "执行内部重载运算符+"<<endl;
        return this->value + m.value;
    }
    //拷贝赋值运算符重载
    void operator=(money &m){
        cout << "执行赋值运算符重载"<< endl;
        this->id = m.id;
        this->value = m.value;  // 默认是浅拷贝(若是存在指针成员 则使用深拷贝)
    }
};

// 移动赋值运算符重载
class tt{
public:
    int *p = nullptr;
    tt(){}
    tt(int *p):p(p){}
    void operator=(const tt & t){
        p = new int(*t.p);  // 深拷贝
        cout << "拷贝赋值运算符重载"<< endl;
    }
    void operator=(tt && t){
        p = t.p;
        t.p = nullptr;
        cout <<"移动拷贝赋值运算符重载"<< endl;
    }
    //普通成员函数
    void test(){
        cout << "test...."<< endl;
    }
    //调用运算符重载
    void operator()(int*p){
        cout << "调用重载+携带数据:" << *p << endl;
    }

    ~tt(){}

};
//运算符重载全局函数
int operator-(money m, money &m1){
    cout << "执行外部重载运算符-"<<endl;
    return m.value - m1.value;
}
//输出运算符重载
ostream& operator<<(ostream &out, money &m){//使用&out:cout不允许拷贝
    out << "输出运算符重载:id=" << m.id << " value="<< m.value<< endl;
    return out;
}
//输入运算符重载
void operator>>(istream &in, money & m){
    in>>m.id >>m.value;
}

int main(){
    // 算术运算符重载(简化代码 应用场景多样)
    int a = 1, b = 2;
    int c = a + b;  // 通常的运算符应用
    money m1(1,11);
    money m2(2,22);
    int a1 = m1 + m2;  // 运算符重载应用(类内部)
    int a2 = m1 - m2;  // 运算符重载(全局)
    cout << "c=" << c << " a1=" << a1 << " a2=" << a2 <<endl;
    cout << "-------------1-----------"<< endl;

    // 输出运算符重载
    cout << m1 << m2<< endl;
    cout << "------------2------------"<< endl;

    // 输入运算符重载
    money m3;
    cout<<"输入:"<< endl;
    cin>>m3;
    cout<< "m3.id="<< m3.id<<" m3.value" << m3.value<<endl;

    // 拷贝赋值运算符重载
    cout << "------------3------------"<< endl;
    cout << m1 << m2 << endl;
    cout << "------------4------------"<< endl;
    m1 = m2;
    cout << m1 << m2 << endl;

    // 移动赋值运算符重载
    cout << "------------5------------"<< endl;
    int* pt = new int(44);
    tt t1(pt);
    tt t2;
    //t2 = t1;
    t2 = move(t1);
    cout << *t2.p << endl;
    //cout << *t1.p << endl;  // 编译错误


    // 调用运算符重载
    cout << "------------6------------"<< endl;
    t2.test();  // 普通调用
    t2(pt);  // 重载调用
    plus<int> p;
    cout<< p(1, 2) << endl;  // 重载调用应用
    delete pt;

    return 0;

}
/* 运算符重载实则是调用operator函数
 * 输出运算符<<:cout对象禁止拷贝(父类的拷贝构造函数为私有) 它负责从缓冲区获取数据打印输出有且只有一个对象
 * cout<<1<<2;链式输出需要返回值ostream out
 * 不可使用运算符重载:
 * 成员访问运算符 .  || 域运算符 :: || 长度运算符 sizeof  || 条件运算符 ?:  || 预处理符号 #
 */

2.7 命名空间namespace

#include <iostream>
#include <string>

/*
//a.使用using声明 避免局部变量同名带来的编译过程中冲突(推荐)
using std::cin;
using std::cout;
using std::endl;
*/
using namespace std;  //b.using 指示(不推荐)

int a = 100; //定义一个全局变量a

//1.通过namespace声明
namespace c_member {
    int a = 20;  // namespace 内部变量a

    string name[]{"c", "c#", "c++"};
    void fn(){
        cout << "c_member::fn调用" << endl;
    }
    // 嵌套namespace
    namespace c_web {
        string name[]{"c_socket"};
        void fn(){
            cout << "c_member::c_web::fn调用"<< endl;
        }
    }
}

namespace web{
    string name[]{"python_web", "go_web"};
    void fn(){
        cout << "web::fn调用" << endl;
    }
}

//2.通过using namespace (指定函数 变量 类)使用(using namespace::name)
using namespace web;

//静态局部变量
void test_static_par(){
    static int x = 10;
    cout << "x1=" << x << "\t";
    x += 1;
    cout << "x2=" << x << endl;
}

int main(){
    // 命名空间(全局):区分不同库中的变量 函数 类等
    //1.
    cout << "c_member------(namspace_name::方式)" << endl;
    cout << "c_member::name>>" << c_member::name[0] <<endl;  // c_member::name>>c
    c_member::fn();  // c_member::fn调用
    cout << "c_member::c_web------(namspace_name::方式)" << endl;
    cout << "c_member::c_web::name>>" << c_member::c_web::name[0] <<endl;  // c_member::c_web::name>>c_socket
    c_member::c_web::fn();  // c_member::c_web::fn调用
    cout << "分割线------------------------------------------" << endl;
    //2.
    cout << "web------(using namespace方式)" << endl;
    cout << "web::name>>" << name[0] << endl;  // web::name>>python_web
    fn();  // web::fn调用


    // 校验namespace内部变量作用域(全局变量&&局部变量)
    cout << "分割线------------------------------------------" << endl;
    int a = 10; //局部变量a
    cout << "全局变量a>>" << ::a << endl;  // 全局变量a>>100
    cout << "c_member::a>>" << c_member::a << endl;  // c_member::a>>20
    cout << "局部变量a>>" << a << endl;  // 局部变量a>>10
    
    // 静态局部变量static type 变量名 (只会初始化一次)
    test_static_par();  // x1=10   x2=11
    test_static_par();  // x1=11   x2=12
    return 0;
}



/*::范围解析运算符
*全局作用域符(::name) 作用域为全局命名空间(变量同名声明全局变量)
*类作用域符(class::name) 指定类型变量的作用域范围是具体的某个类
*命名空间作用域符(namespace::name) 指定类型变量的作用域范围是具体某个命名空间
*/

2.8 预处理器(宏)&&头文件

-----------par.h
//条件宏
#ifndef CODE_PAR_H  // 定义宏(CODE_PAR_H 宏名称随意)
#define CODE_PAR_H  // 执行宏
int abc = 111;

int check_min(int x, int y){
    if(x>y){
        return y;
    } else
        return x;
}
#endif //CODE_PAR_H  // 结束

------------main.cpp
#include <iostream>  # 头文件
#include "par.h"

using namespace std;
// 宏变量(大写)
#define AGE 24
#define A 2*2  // 不携带参数的宏a
#define B(q) q*q  // 携带参数的宏b
#define C(q) (q)*(q)
#define D(x, y) x##y
#define F(x) #x
#define check_max(x,y)(x>y?x:y)

int main(){
    /* 头文件使用
    * 用于变量 函数 类的定义声明 在源文件.cpp中导入使用
    * <>:默认在gcc编译器环境内查找; "":默认在当前工程查找 再到系统编译器环境查找
    */
    cout << "par.h(abc)=" << abc << endl;
    cout << "par.h(check_min函数)=" << check_min(2, 3) << endl;
    printf("分割线----------------------->\n");
    //预处理器(宏)
    int g = A+A; // 2*2+2*2
    int s = B(2+2); // 2+2*2+2=8
    int k = C(2+2); // (2+2)*(2+2)=16
    printf("g=%d s=%d k=%d\n", g, s, k);  // g=8 s=8 k=16
    printf("分割线----------------------->\n");
    int y = D(11,22);
    const char *z=F(abcdefg);  // "abcdefg"
    int max = check_max(33, 22);
    printf("y=%d, z=%s, res=%d\n", y, z, max);  // y=1122, z=abcdefg, res=33
    printf("分割线---------预定义宏-------------->\n");
    cout << "value__LINE__:" << __LINE__ << endl;  // 行号493
    cout << "value__FILE__:" << __FILE__ << endl;  // 当前文件目录C:\Users\xyz\Desktop\code\main.cpp
    cout << "value__DATE__:" << __DATE__ << endl;  // 日期Oct 13 2020
    cout << "value__TIME__:" << __TIME__ << endl;  // 时间15:58:07
    printf("分割线---------条件宏 控制代码执行-------------->\n");

#if AGE
    cout << "if false:可实现注释效果\nif true 则执行语句 主要是判断变量宏AGE是否存在" << endl;
#else
    cerr << "其他代码执行" << endl; // 针对栈满 cerr不缓冲 也可以直接输出信息(多用于错误输出)
#endif
}
/* 格式:#define 宏名称 表达式(数值等)
 * 宏
 * 1.宏变量:define AGE 10; 类似const 常量
 * 2.条件宏主要用于判断是否存在某个宏变量
 * 预处理器在编译前优先替换常量的值 通过宏可以方便修改常量的值
 * 对于函数调用 使用宏定义简单功能的函数能提高程序的执行效率 不会在栈内存开辟空间 类似内联函数 不用再调用耗时耗资源
 */

2.9字符串

#include <iostream>
#include <string>  // 提供c++string类 相关方法
#include <cstring>  // 提供c string类 相关方法

using namespace std;

int main(){
    // 1.c 字符串定义
    char str[] = "good";
//    char str[] = {'g', 'o', 'o', 'd', '\0'};
    cout << "char str(size)=" << sizeof(str) << endl; //char str(size)=5
    //操作(c)
    char d[1];
    strcpy(d, str);  //拷贝字符串
    cout << "d:" << d << endl;
    strcat(d, "abc"); // 拼接字符串
    cout << "d:" << d << endl;
    strlen(d);  // 字符串长度
    cout << "d(length):" << d << endl;
    


    // 2. c++(namespace string) 字符串定义
    string a = "c++";
    string b;
    string c = " is no.1";

    //操作(c++)
    cout << "(b):" << (b=c) << endl;  // 字符串赋值替换
    cout << "(a+b):" << (a+=b) << endl; //字符串拼接
    cout << "(a+b)(长度):" << (a+=b).length() << endl;  // 字符串长度
    cout << "c.find('i'):" <<c.find('i') <<endl;  // 查找特定字符位置
    cout << "b(添加元素):" << b.append("+abc") << endl;
    cout << "b(特定位置插入元素):" << b.insert(8, "+efg") << endl;
    cout << "b(特定元素位置):" << b.find("+efg") << endl;
    cout << "b(修改元素):" << b.replace(8, 8, "") << endl;  // pos:替换开始下标 n1:替换的范围 s:替换内容
    cout << "(a+b)(截取特定位置元素):" << (a+b).substr(0, 3) << endl;  //pos:开始下标  n:结束下标

}
/*
char str(size)=5
d:good
d:goodabc
d(length):goodabc
(b): is no.1
(a+b):c++ is no.1
(a+b)(长度):19
c.find('i'):1
b(添加元素): is no.1+abc
b(特定位置插入元素): is no.1+efg+abc
b(特定元素位置):8
b(修改元素): is no.1
(a+b)(截取特定位置元素):c++
 */

2.10 数组

// 数组定长的容器 使用连续性的一串地址存放数据 不允许添加删除 下标从0开始 可查询修改
// 数组定义:type(c++数据类型) arryName(数组名) [arry_size(数组元素数量必须大于0且为常量表达式)] (const int size = 10 ;>> int a[size];)
// 多维数组:type arryName [arry_size1][arry_size2]...(类比坐标系x,y,z)
#include <iostream>

using namespace std;

int main(){
    //初始化int类型数组(2行3列)
    //int a[2][3]{{1, 2, 3},{4, 5, 6}};
    int a[2][3] = {
            {1, 2, 3},
            {4, 5, 6},
             };
    // int a[2][3] ={1,2,3,4,5,6}
    // 修改数组内数据
    a[0][0] = 99;
    
    // 数组大小(长度)  int size=sizeof(数组)/sizeof(int);
    cout << "数组a大小:" << sizeof(a) / sizeof(a[0][0]) << "\n";

    // 通过索引获取数组数据(从0开始)
    for(int x=0;x<2;x++){
        for (int y = 0; y < 3; y++) {
            cout << "a[" <<x << "][" <<y <<"]>>>" << a[x][y] << endl;
        }
    }
	
    // vector越界
    vector<int> _arr{11, 22, 33, 44};
    int arry[4] = {11, 22, 33, 44};
    printf("_arr[4]:%d\n", _arr[4]);
    printf("_arr.at(4):%d\n", _arr.at(4));  // vector .at可进行范围检测 判断是否越界再取数据
    printf("arry[4]:%d\n", arry[4]);  // 普通数组不存在检测机制 除非越界范围大 编译也会出错
    return 0;
}
// 数组越界问题:会导致相邻地址的数组数据被越界数据覆盖

//vector(不定长数组容器应用)
#include <iostream>
#include <vector>

using namespace std;

int main(){
    //vector(不定长数组容器)
    //1.声明初始化(相似数组)
    vector<int> arr{1,2,3};  // vector<int> arr={1,2,3};
//    vector<int> _arr(4, 3);  //非初始化 调用vector构造函数 相当于_arry长度为4 值均为3

    //2.增删改查
    cout<<"arr.push_back" << endl;
    arr.push_back(4);  // 添加(尾部添加)
    for(int i=0;i<arr.size();++i){cout <<i <<"=" << arr[i] << endl;}

    arr.insert(arr.begin()+1, 22);  // 在数组下标1添加数据22
    cout<<"arr.insert " << endl;
    for(int i=0;i<arr.size();++i){cout <<i <<"=" << arr[i] << endl;}
    cout << endl;


    arr[3] = 5;  // 修改数据
//    arr.at(3) = 5;  // 修改
    cout<<"arr.at " << endl;
    for(int i=0;i<arr.size();++i){cout <<i <<"=" << arr[i] << endl;}


    cout << "查询遍历arr" << endl;
    for(int i=0;i<arr.size();++i){  // 查询遍历
        cout << i << "=" << arr[i] << endl;
    }


    arr.erase(arr.begin(), arr.begin()+2);  // 删除下标0到1的数据[0,2)
    cout<<"arr.erase " << endl;
    for(int i=0;i<arr.size();++i){cout <<i <<"=" << arr[i] << endl;}
    arr.clear();  // 清空数组数据

    // vector越界
    vector<int> _arr{11, 22, 33, 44};
    int arry[4] = {11, 22, 33, 44};
    printf("_arr[4]:%d\n", _arr[4]);
//    printf("_arr.at(4):%d\n", _arr.at(4));  // vector .at可进行范围检测 判断是否越界抛出异常再取数据
    printf("arry[4]:%d\n", arry[4]);  // 普通数组不存在检测机制 除非越界范围大 编译也会出错

    // 二维
    //二维数组定义
//    int a1[]{1, 2, 3, 4};
//    int a2[]{11, 22, 33, 44};
//    int a3[]{1, 2, 3, 4};
//
//    int a[3][4]{
//            {1, 2, 3, 4},
//            {11, 22, 33, 44},
//            {111, 222, 333, 444}
//    };
    //vector定义
    vector< vector<int> > x{
        {1, 2, 3, 4},
        {11, 22, 33, 44},
        {111, 222, 333, 444}
    };  // 二维vector

    for(vector<int> value:x){
        for (int v:value){
            cout << v << "\t";
        }
        cout << endl;
    }
//    1       2       3       4
//    11      22      33      44
//    111     222     333     444

    return 0;
}
/*
 arr.push_back
0=1
1=2
2=3
3=4

arr.insert
0=1
1=22
2=2
3=3
4=4

arr.at
0=1
1=22
2=2
3=5
4=4

查询遍历arr
0=1
1=22
2=2
3=5
4=4

arr.erase
0=2
1=5
2=4
 */

2.11 struct结构体(c++ 类)

#include <iostream>
#include <string>  // 集成了string类所有操作

using namespace std;

// 声明结构体类型_class
struct _class {
    string cls_name;
    string department;
    int cls_id;
    // 3.初始化结构体(构造函数)
    _class (string a = "", string d = "", int c = 0){
        cls_name = a;
        department = d;
        cls_id = c;
    }
};

//声明结构体类型people->创建一个新的数据类型
struct people {
    string name;
    int age;
    string gender;
    string hobby;
    _class class_info;
};

// 类
struct volume{
    int x;
    int y;
    int z;
    volume(){
        cout << "无参构造"<< endl;
    }
    ~volume(){
        cout << "析构"<<endl;
    }
};

int main(){
    // 1.初始化结构体people(指定变量stu&&赋值)
    people stu;
    stu.name = "露娜";
    stu.age = 20;
    stu.gender = "女";
    stu.hobby = "弹吉他";
    stu.class_info.cls_name = "计算机应用技术1班";
    stu.class_info.department = "计算机系";
    stu.class_info.cls_id = 1;

    // 2.初始化结构体people
    //    people stu = {"露娜", 20, "女", "play_guitar", "计算机应用技术1班", "计算机系", 1};

    // 输出信息
    cout << "name:" << stu.name << "\n";
    cout << "age:" << stu.age << "\n";
    cout << "gender:" << stu.gender << "\n";
    cout << "hobby:" << stu.hobby << "\n";
    cout << "cls_name:" << stu.class_info.cls_name << "\n";
    cout << "department:" << stu.class_info.department << "\n";
    cout << "cls_id:" << stu.class_info.cls_id << "\n";

    // 3.堆内存创建结构体对象
    people* s2 = new people;
    (*s2).name = "zero";
    (*s2).age = 25;
    (*s2).gender = "男";
    (*s2).hobby = "打球";
    (*s2).class_info.cls_name = "计算机应用技术2班";
    (*s2).class_info.department = "计算机系";
    (*s2).class_info.cls_id = 3;
    cout << (*s2).name << " " <<  (*s2).age << " " << endl;

    // 4. 类
    volume v;  // 创建对象v
    v.x = 1;  // 访问对象成员
    cout << v.x << endl;

    return 0;
}

// struct 可以嵌套使用 可通过这个结构体类型(新数据类型)创建多个变量->类(多个实例)
// 不可在定义新的结构体内进行赋值操作(定义的过程单纯是创建新的数据类型)
// c++ struct结构体可以有析构函数和构造函数 默认类中成员变量为公有 但也可以私有 没有多态 继承机制
// 构造函数初始化结构体避免了 (指定变量 赋值初始化)这种方式因为某些成员(结构体内部的变量)未初始化 导致 后面成员不能初始化问题 同时也避免字符串编译问题
//struct与class区别:
//1.struct 成员默认public 
//2.struct 多用于表示一个新结构类型


2.12 枚举类型

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

enum struct GENDER{MALE, FEMALE};
class stu;

class stu{
public:
    string name;
    GENDER gender;
    stu(string name, GENDER gender):name(name), gender(gender){}
};

int main(){
    // 枚举类型
    //1. 限定作用域
    //1.1 定义
    enum class COLOR{RED, GREEN=22, BLUE};

    //1.2 取值
    cout << (int)COLOR::RED <<endl;  // 0
    cout << (int)COLOR::GREEN <<endl;  // 22
    cout << (int)COLOR::BLUE <<endl;  // 23
    cout <<"-----------------------------"<<endl;

    // 2.不限定作用域
    enum LOGO{QQ, WEIXIN=222, MT};
    // enum T{QQ, TAOBAO};  // 编译错误
    cout << LOGO::QQ <<endl;  // 0
    cout << LOGO::WEIXIN <<endl;  // 222
    cout << LOGO::MT <<endl; // 223

    // 3.应用
    cout <<"-----------------------------"<<endl;
    stu s("FSH", GENDER::MALE);
    cout << "name=" << s.name <<endl;
    cout << "gender=" << ((int)(s.gender)==0 ?"男": "女")<<endl;
}
/* 枚举类型
 * 1.限定作用域
 * 1.1 定义 enum class a{}; enum struct b {};
 * 1.2 成员可以赋值 默认输出下标位置 有赋值操作会影响后边数据输出
 * 2.不限定作用域
 * 2.1 定义 enum xx{}
 * 2.2 成员可以赋值 默认输出下标位置
 */


3. 条件判断

3.1 if

#include <iostream>

using namespace std;


int main(){
    // 条件判断
    cout << "输入数字:" << endl;
    int a;
    cin >> a;
    if(a>=1 && a<=5){
        cout << a << "\n 1<=a<=3";
    }else if(a>5){
        cout << a << "\n a>5";
    }else{
        cout << "a非整形|a非法输入";
    }

}
/*if(条件一){}
 *   else if(条件二){}
 *   else{其他条件}
 * 可以使用条件运算符 ?:
 * exp1 ? exp2 : exp3; -> if(){}-else{}
 * express1(true)->express2; || express1(false)->express3; eg:bool result = a > 3 ? 1: 0; 
 */

3.2 switch -case

#include <iostream>

using namespace std;


int main(){
    // 条件判断
    cout << "是否检测到他|她说谎(0|1):" << endl;
    char flag;
    cin >> flag;

    switch (flag) {
        case '0':
            cout << "说谎了";
            break;
        case '1':
            cout << "没说谎";
            break;
        default:
            cout << "观察中";
            break;
    }

}

/* 小结:
 *case(常量且不能相同 否则switch无法判断执行的语句) 
 *break用于满足条件后结束switch语句 
 *default其他条件不满足则执行默认 可以省略break
 */


4. 循环

4.1 while

#include <iostream>
#include <windows.h> //c++:Sleep函数

using namespace std;

int main(){
    int num = 3;
    while(num <=3 && num >=0){
        printf("倒计时:%d\n", num);
        Sleep(1000);  // ms
        num --;
    }
    return 0;
}
// 使用continue && break 进行循环控制

4.2 do-while

#include <iostream>
#include <windows.h> //Sleep函数

using namespace std;


int main(){
    int num = 3;
    do{
        printf("倒计时:%d\n", num);
        Sleep(1000);  // ms
        num --;
    }while(num <=3 && num >=0);
    return 0;

}
// while && do-while :while先判断再执行 | do-while先执行后判断 确保循环至少执行了一次

4.3 for

#include <iostream>

using namespace std;

int main(){
//for 实现九九乘法表
int i, j;
for (i=1;i<10;i++){
    for(j=1;j<=i;j++){
        printf("%d*%d=%d\t", j, i, j*i);
    }
    printf("\n");
}

}

/*
1*1=1
1*2=2   2*2=4
1*3=3   2*3=6   3*3=9
1*4=4   2*4=8   3*4=12  4*4=16
1*5=5   2*5=10  3*5=15  4*5=20  5*5=25
1*6=6   2*6=12  3*6=18  4*6=24  5*6=30  6*6=36
1*7=7   2*7=14  3*7=21  4*7=28  5*7=35  6*7=42  7*7=49
1*8=8   2*8=16  3*8=24  4*8=32  5*8=40  6*8=48  7*8=56  8*8=64
1*9=9   2*9=18  3*9=27  4*9=36  5*9=45  6*9=54  7*9=63  8*9=72  9*9=81
 */

// c++11 for循环使用
// 1. for(i;i<10;i++) 类型
// 2.for(type i:容器(可序列化))
/* for(int val:{1,2,3,4}){cout << val;}  {1,2,3,4} 任意序列*/


5. 函数

5.1 一般函数

#include <iostream>

using namespace std;

//内联函数
inline int fn(int x, int y){
   return x + y;
    
//有返回值的自定义函数
/*格式:
 * 函数return返回的类型  函数名(参数) {函数体执行代码(可省略)}
 * return_type fn_name(par_list) {}*/
int min_v(int x , int y) {
    if(x>y){
        cout << "x-y(min):" << y;
    }else if (x==y){
        cout << "x-y(min):" << x || y;
    }else{
        cout << "x-y(min):" << x;
    }
    return 0;
}

// void 声明函数max_v不需要返回值 且参数名可忽略 参数类型必须存在
void max_v(int x, int y){
    if(x>y){
        cout << "x-y(max):"  << x;
    } else if(x==y){
        cout << "x-y(max):" << y||x;
    }else{
        cout << "x-y(max):" << y;
    }
}

// 指针形参对实参的影响
void change(int *a, int *b){
    int t = *a;
    *a = *b;
    *b = t;
}
// 函数重载
int cal(int x, int y){
    return x+y;
}
double cal(int x, double y){
    return x+y;
}
int cal(int x, int y, int z){
    return x+y+z;
}
int main(int arg, char** args){
     //arg:参数的个数 && args:程序启动后携带的参数数据  .exe 后携带的参数(命令行启动)
    cout << "arg=" << arg <<  endl;
    cout << "args=" << args << "  args[0]=" << args[0] << "  args[1]=" << args[1] <<endl;
    //调用函数
    min_v(4, 5);  // x-y(min):4
    printf("\n");
    max_v(2, 3);  // x-y(max):3
    int a=7, b=8;
    printf("调用前(a-b):%d-%d\n", a, b);  // 调用前(a-b):7-8
    change(&a, &b);
    printf("调用后(a-b):%d-%d\n", a, b);  // 调用后(a-b):8-7
    // 函数重载(函数名相同+参数个数&&类型&&顺序不同)
    cout << "int-cal1:" << cal(1, 2) << endl;  // int-cal1:3
    cout << "double-cal2:" << cal(1, 2.4) << endl;  // double-cal2:3.4
    cout << "int-cal3:" << cal(1, 2, 3) << endl;  // int-cal3:6
    // 内联函数(不会有压栈的操作
    // 直接将函数代码放置在调用位置
    // 但是也会延长函数内部变量的生命周期)
    int a=1, b=2;
    cout << fn(a, b) << endl;  // 3
    return 0;
}
/*
形参:int x, int y 接收参数的变量;相当于局部变量 调用函数后会销毁;
实参: int a =2, b=3 传递给形参的变量;默认情况下形参不会直接对实参传递过来的值有影响;指针形参&&引用形参均可以对传递过来的实参变量
函数重载:函数名称相同 参数的类型&&个数&&顺序不同 用于实现类似功能的同名函数
函数参数传递值的方式:值传递(拷贝数据) && 引用传递(拷贝数据地址) && 指针传递(拷贝数据地址)
传递函数的参数是值传递 相当于值的拷贝 在函数内部默认不可修改 可使用指针(引用)参数进行修改
数组传递内存地址数据
*/


5.2 lambda函数(c++11)

#include <iostream>

using namespace std;

int _add(int a, int b){
    return a+b;
}

int cal(int a, int b, int (*fn)(int,  int )){
    return fn(a, b);
}

int main(){
    /*lambda 匿名函数
     * [捕获外部变量供使用] (形参) mutable修饰符申明可以进行修改的捕获变量 异常设定-> 返回类型 {函数体}
     * []()->int{}
     * [capture](parms) mutable exception->return_type{body}
     * 最简洁的表达式 [capture] {body}
     * */
    int a=1, b=2, c=3;
    auto f = [](int d){printf("f-(d):%d\n", ++d);};  // (ps:auto自动匹配变量类型)
//    auto f = [](int d){printf("f-(d):%d \n", ++d);return 0;}(a);  // f-(a):2
    auto f1 = [a] () mutable {printf("f1-(a):%d \n", ++a);};  // 捕获列表添加a 使用外部变量(mutable 申明可以对a修改)
    auto f2 = [a] {printf("f2-(a):%d \n", a);};  // 捕获变量a且不能在函数体进行修改
    auto f3 = [=, &a] {printf("f3-(a-b-c):%d-%d-%d \n", a=11, b, c);};  // a引用形式捕获 其他变量在函数体修改
    auto f4 = [&, a] {printf("f4-(a-b-c):%d-%d-%d \n", a, b=22, c=33);};
//    auto f5 = [this]{this->[]{printf("c++");};};
    // 调用
    f(a);  // f-(d):2
    f1();  // f1-(a):2
    f2();  // f2-(a):1
    f3();  // f3-(a-b-c):11-2-3
    f4();  // f4-(a-b-c):1-22-33
    //[](int d){printf("f-(d):%d\n", ++d);}();  // 直接调用
    //void(*p)() = []{cout<<"111"<<endl;};  // 通过函数指针接收再调用
    //p()
    
    //lambda函数应用(参数传递)
    int res1, res2;
    res1 = cal(2, 3, _add);
    res2 = cal(4, 5, [](int a, int b){return a*b;});
    cout << "res1=" << res1 << "\t" << "res2=" << res2<<endl;

     return 0;
}
/*[]:捕获列表其他用法(局部变量)
 * [a,b,..]-捕获多个变量 
 * [=]值-拷贝所有外部变量值(不可再修改值) 
 * [&]引用-引用方式接收所有外部变量(可修改值)
 */



6. 对象

6.1 对象初识

#include <iostream>
#include <string>

using namespace std;

//类(class 关键字创建类)
class cls{
    int flag;  // 私有属性
public:
    //属性(默认私有)
    int cls_id;
    string name;
    //方法(默认私有)
    void test(){
        cout << "cls_id:" << cls_id << " name:" << name << endl;
    }
    // 外部 关联类成员函数
    //1.类名::函数名
    void test1(cls & c);

    // 2.友元函数
    friend void test2(cls & c);

    // 3.友元类
    friend class ss;
};

//友元类
class ss{
public:
    void get_data(cls & c){
        c.flag = 333;
        c.test();
        cout << "get_data调用"<< endl;
    }
};

// 在类外部关联成员函数
void cls::test1(cls & c) {
    c.flag = 111;  //可访问修改类中成员
    //c.test();
    cout << "test1..." << endl;
}
// 通过友元函数关联
void test2(cls & c){
    c.flag = 222; //可访问修改类中成员
    //c.test();
    cout << "test2..."<< endl;
}


int main(){
    // 栈内存创建对象
    cls c1;
    // 堆内存创建对象
    cls * c2 = new cls;
    printf("------------------------------\n");
    //访问对象成员(属性+函数方法)
    c1.name = "c++1班";
    c1.cls_id = 1;
    c1.test();
    c2->name = "c++2班";
    c2->cls_id = 2;
    c2->test();
    //访问类中的成员函数
    c2->test1(c1);
    delete c2;

    // 友元函数
    printf("------------友元函数------------------\n");
    test2(c1);
    // 友元类
    printf("------------友元类------------------\n");
    ss s;
    s.get_data(c1);  // 通过友元类访问修改类成员

    return 0;
}
/* 栈内存创建的对象 其方法保存在内存是一个指针 指向函数表 函数表通过栈内存函数地址调用函数
 * 访问修饰符 public private protected
 * public:类内部访问+类外部访问
 * private:类中所有成员默认私有 只能在类内部或友元(friend 修饰符修饰的类后函数)可以访问使用 外部不可访问修改 
 * protected: 与私有类似 但是派生类内部也可以访问父类中成员 
 *
 * 类中成员默认为私有 只能在类内部使用 通过public进行公有化声明 类外部也可使用
 * 若采用分离式的编译 通常类在头文件.h进行声明 源文件.cpp实现成员函数
 * 类中的成员函数默认都是内联函数 不会在栈内存开辟空间 直接放置再调用位置
 * 在类外部访问修改类成员
 * 1.通过在类外部创建全局函数并用类名::函数名声明
 * 2.通过在类中声明friend 友元函数
 * 3.通过在类中声明friend class 友元类
 * 4.友元类(函数) 可以访问类中所有私有(保护)成员
 */


6.1.1 初始化列表形式 构造

#include <iostream>

using namespace std;

class A{
public:
    A(int id){
        cout << "A有参构造"<< endl;
    }
};
class B: public A{
public:
    B():A(1){
        cout << "B有参构造"<< endl;
    }
};
class C{
public:
    const int c;
    C(int c):c(c){}
};
class D{
public:
    int & d;  // 类成员为引用
    D(int d):d(d){}
};
class E{
public:
    A a;  // 类成员为对象
    E(int e):a(e){}
};


int main(){
    // 初始化列表形式执行构造函数
    //1.继承下 子类初始化 父类没有无参构造 手动调用父类有参
    B b;

    //2.const修饰的类成员
    C c(1);

    //3.初始化引用成员数据
    D d(2);

    //4.类中成员为对象 其对象没有无参构造 初始化对象
    E e(3);

    return 0;
}

6.2构造函数

6.2.1 构造函数初识

#include <iostream>

using namespace std;

class time{
public:
    //属性
    int hour;
    int minute;

    // 特殊函数(构造函数)
    //1.普通无参构造函数
    time(){
        hour = 3;  // 初始化属性
        minute=50;
        cout << "无参构造函数执行"<< endl;
    }
    //2.有参构造函数
    time(int h){
        hour = h;// 初始化属性
        cout <<"有参1构造函数执行"<< endl;
    }

    //3.初始化参数列表构造函数
    time(int hour, int minute):hour{hour}, minute{minute}{
        cout << "有参2构造函数(初始化列表参数)执行"<< endl;
    }

    //普通函数
    void test(){
        cout<<"普通函数执行"<< endl;
    }
};

int main(){
    //栈内存创建对象
    //1.无参构造函数
    time t1;
    time t2;
    time t3(); // 不会创建对象 单纯函数的原型

    //2.有参构造函数
    time t4(5);


    //堆内存创建对象
    time* t5 = new time;
    time* t6 = new time(5, 10);
    //初始化对象属性
    cout << "t1初始化:" << t1.hour << "-" << t1.minute << endl;
    cout << "t4初始化:" << t4.hour << "-" << t4.minute << endl;
    cout << "t6初始化:" << t6->hour << "-" << t6->minute << endl;

    delete t5;
    delete t6;

    return 0;
}
/*
1.构造函数完成对象成员属性赋值操作
2.默认创建对象会执行类的无参构造函数(即使类中没有) 也可自定义构造函数
3.无参构造可以添加关键字 time()=default 使用默认机制实例无参对象
4.构造函数具备重载的特性(无参构造与有参构造 对象创建自动重载调用构造函数)
5.初始化列表参数构造函数必须使用情况(执行构造函数前完成初始化赋值):有继承关系 成员中有常量或者引用
6.存在有参构造函数后 不能再执行进行无参构造函数的调用 对象需要初始化携带参数
7.静态成员属性static int id 需要在类外部进行初始化
8.有一个参数的构造函数 可以在实例对象时触发隐式调用->time t4 = 5;会触发有参构造函数;可以使用 explicit 关键字限制隐式调用
 */

6.2.2 委托构造函数

//初始化列表参数形式执行构造函数(委托构造函数)
#include <iostream>

using namespace std;

class time{
public:
    int hour;
    int minute;

    //1.普通无参构造函数(初始化列表参数形式)
    time():time(5, 1){
        cout << "无参构造函数执行"<< endl;
    }

    //2.有参构造函数(初始化列表参数形式)
    time(int h):time(h, 3){
        cout <<"有参1构造函数执行"<< endl;
    }

    //3.初始化参数列表构造函数
    time(int hour, int minute):hour{hour}, minute{minute}{
        cout << "有参2构造函数(初始化列表参数)执行"<< endl;
    }
};

int main(){
    //通过初始化列表参数形式执行构造函数创建对象
    time t1;

    return 0;
}
// 在参数少的构造函数委托给参数多的构造函数
// 委托构造函数:创建对象 根据参数多少 自选调用的构造函数 time(){}&&time(int){} 委托给time(int ,int){} 使用初始化列表参数方式进行

6.2.3 拷贝构造函数

//已有对象拷贝执行拷贝构造函数
#include <iostream>

using namespace std;

class cls{
public:
    int cls_id;

    cls(int cls_id):cls_id(cls_id){
        cout << "有参构造函数执行"<< endl;
    }
    //拷贝构造函数
    cls(const cls &c){
        // 已有对象数据拷贝到另一个对象
        this->cls_id = c.cls_id;  // cls_id=c.cls_id
        cout << "拷贝构造函数执行" << endl;
        //c.cls_id = 3; // const约束-报错
    }

    ~cls(){
        cout << "析构函数执行-" <<cls_id<< endl;
    }
};

int main(){
    //拷贝构造
    cls c1(2);  // 初始化对象
    cls c2 = c1;  // 拷贝对象
    cout << "*pc1=" << &c1 << "  c1.cls_id=" << c1.cls_id << endl;
    cout << "*pc2=" << &c2 << "  c2.cls_id=" << c2.cls_id <<endl;
    return 0;
}
/* 
 * 拷贝构造(已有一个对象 从已有对象进行拷贝)
 * const 禁止修改原对象初始化成员数据
 * 拷贝构造函数参数使用&引用接收 避免再次拷贝 形成递归拷贝问题
 * 禁止触发拷贝构造函数:拷贝构造函数变为私有private或者拷贝构造函数cls(const cls & c)=delete{}
 */

6.2.4 移动拷贝函数

#include <iostream>

using namespace std;

class subject {
public:
    int* id= nullptr;

    subject(){
        cout <<"无参构造"<<endl;
    }
    subject(int* id):id(id){
        cout <<"有参构造"<<endl;
    }
    subject(const subject &s){
        id = new int(*s.id);  // 类成员有指针 使用深拷贝
        cout <<"拷贝构造"<<endl;
    }
    // 移动构造(原有对象数据移动到新对象数据)
    // 默认编译器不提供移动构造函数(反而执行拷贝构造)
    subject(subject && s){
        id = s.id;  // 新对象指向原有对象成员数据地址
        s.id = nullptr; //原有对象指针成员 不在指向之前数据的地址
        cout << "移动构造"<< endl;
    }

    ~subject(){
        cout <<"析构"<< endl;
    }
};


int main(){
    // 拷贝
    int* i = new int(1);
    subject s(i);
    subject s1 = s; //深拷贝执行拷贝构造函数
    cout << s.id << "\t"<<s1.id << endl;  // 堆内存数据地址 0x10a6e20  0x10a6e40
    cout <<"------------------------"<< endl;
    // 移动构造(接收右值)
    int* i1 = new int(2);
    subject s2(i1);
    subject s3 = move(s2);  // move(转换成右值)
    cout << s2.id <<"\t"<< s3.id <<endl; // 0  0x1e6e60(执行移动构造函数 且s2指针成员执行的数据地址不再存在 移动给了s3)

    cout <<"------------------------"<< endl;

    return 0;
}
/*
移动构造
使用场景:对象有指针成员 且原对象不再使用 新对象与原对象一样
比较:相较于拷贝构造 节省了开辟新空间 和拷贝的操作
实质:是类成员数据权限的交换 把原对象的指针成员指向的数据地址 变为新对象所拥有 原对象指向为空
 */

6.3 析构函数

#include <iostream>

using namespace std;

class cls{
public:
    cls(){
        cout << "构造函数执行" << endl;
    }
    ~cls(){
        cout << "析构函数执行" << endl;
    }
};

void create_object(){
    //栈内存形式创建对象
    //函数调用结束则销毁对象c
    cls c;
}

int main(){
    // 栈内存形式创建对象
    cout << "---------1-----------"<< endl;
    create_object();
    cout << "---------2-----------"<< endl;
    // 堆内存形式创建对象
    cls* c1 = new cls;
    cout << "---------3-----------"<< endl;
    delete c1;
    return 0;
}
/* 析构函数没有参数 没有返回值 函数名是类名 是~(无参构造函数) 取反
 * 对象销毁时自动调用
 */

6.4 继承

6.4.1 继承初识

#include <iostream>

using namespace std;

// 父类parent
class parent{
public:
    int id=1;
    void get(){
        cout << "money=" << money <<endl;
        cout << "mid=" << mid <<endl;
    }
private:
    int money = 2;
protected:
    int mid = 3;
};

// son继承parent
class son: public parent{

};
int main(){
    //继承
    parent p;
    son s;
    p.get();
    s.get();
    cout << p.id << endl;
    cout << s.id << endl;

}
// 单继承格式:class 子类: 访问修饰符(public private protected) 父类
// 多继承格式: class 子类: 访问修饰符 父类1, 访问修饰符 父类2,.....

6.4.2 修饰符修饰的继承

#include <iostream>

using namespace std;

// 父类parent
class parent{
public:
    int id=1;
    void get(){
        cout << "id=" << id <<endl;  // 父类公有
        cout << "money=" << money <<endl;  // 父类私有
        cout << "mid=" << mid <<endl;  // 父类保护
    }
private:
    int money = 2;
protected:
    int mid = 3;
};

// son继承parent
class son1: public parent{
    void get1(){
        cout << "====son1=====" << endl;
        cout << "id=" << id << endl;
        //cout << "money=" << money << endl;  // 父类是私有 子类也是 不可访问
        cout << "mid=" << mid << endl;  // 父类为protected 子类内部可以访问
    }
};

class son2: private parent{
    void get2(){
        cout << "====son2=====" << endl;
        cout << "id=" << id << endl;  // 子类为私有
        //cout << "money=" << money << endl;  // 子类为私有  类内部不可访问
        cout << "mid=" << mid << endl;  // 子类为私有 子类内部可以访问
    }
};

class son3: protected parent{
    void get3(){
        cout << "====son3=====" << endl;
        cout << "id=" << id << endl;
        //cout << "money=" << money << endl;  // 父类是私有 子类也是 不可访问
        cout << "mid=" << mid << endl;  // 父类为protected 子类内部可以访问
    }
};

int main(){
    // public继承
    son1 s1;
    s1.id;  // 父类公有 子类公有
    //s1.money; // 父类私有 子类私有 (类内外部不可访问)
    //s1.mid;  // 父类保护 子类保护 (类外部不可访问, 内部可以访问)
    cout << "---------------------------------"<< endl;

    // private继承
    son2 s2;
    //s2.id;  // 父类公有 子类私有
    //s2.money;  // 父类私有 子类私有
    //s2.mid; // 父类保护 子类私有


    // protected继承
    son3 s3;
    //s3.id;  // 父类公有 子类保护(在子类内部可访问)
    //s3.money; // 父类私有 子类私有
    //s3.mid;  // 父类保护 子类保护

}
/* public继承:子类成员与父类成员权限一致
 * private继承: 子类成员均变为私有权限
 * protected继承:父类成员的公有和保护成员变为保护权限 私有不变
 * 子类继承了父类所有函数方法 除了
 * 1.父类构造 析构 拷贝构造函数
 * 2.父类重载运算符函数
 * 3.父类的友元函数
 */


6.4.3 继承下构造 析构

#include <iostream>

using namespace std;
class parent {
public:
    parent(){
        cout << "parent构造" <<endl;
    }

    ~parent(){
        cout << "parent析构" <<endl;
    }
};
class son1: public parent{
public:
    // 默认的无参构造
    // 默认先调用父类构造parent() 再调用子类构造
    son1():parent(){
        cout << "son1构造"<< endl;
    }
    ~son1(){
        cout << "son1析构"<< endl;
    }
};
// 继承下手动调用有参构造
class parent2{
public:
    parent2(int id){
        cout <<"parent2有参构造" << id << endl;
    }
    ~parent2(){
        cout <<"parent2析构" << endl;
    }
};
class son2: public parent2{
public:
    //父类没有无参构造情况
    //子类继承父类 子类有参构造执行需要优先调用父类有参构造parent2(xx)
    //使用初始化列表方式
    son2(int id):parent2(id){
        cout << "son2有参构造"<< id <<endl;
    }
    ~son2(){
        cout << "son2析构"<<endl;
    }
};

int main(){
    // 继承下构造函数
    // 1.无参
    son1 s1;  // parent构造  son1构造
    cout << "------------------------" << endl;
    // 2.有参
    son2 s2(10);  // parent2有参构造10 son2有参构造10

    cout << "------------------------" << endl;
    // 继承下析构函数
    //son2析构
    //parent2析构
    //son1析构
    //parent析构


}
/* 继承下构造函数
 * 1.优先调用父类构造 再执行子类构造
 * 2.有参构造 默认先调用父类无参构造 再调用子类构造
 * 3. 父类没有无参构造 子类实例化对象带参数  调用有参构造 实质是初始化列表方式调用父类有参构造son1():parent(){}
 * 继承下析构函数
 * 1.优先调用子类析构 再调用父类析构
 * 2.调用顺序与构造函数调用相反
 */

6.4.4 多继承

#include <iostream>

using namespace std;

//-类原型声明
class A;
class B;
class A{
public:
    B &b;  // 引用或指针形式
    A(B& b):b(b){}

};
class B{};

class F{
public:
F(){
 cout <<"F构造"<< endl;
}
~F(){
    cout <<"F析构"<< endl;
}
};
class M{
public:
    M(){
        cout <<"M构造"<< endl;
    }
    ~M(){
        cout <<"M析构"<< endl;
    }
};
//继承F M
class S: public F, public M{
public:
    S(){
        cout <<"S构造"<<endl;
    }
    ~S(){
        cout << "S析构"<<endl;
    }
};

int main(){
    // 多继承
    S s;
    // 类前置必要性
    B b;
    A a(b);
}
/* 多继承调用顺序取决于继承顺序
 * 类前置声明-类原型
 * 1.类内部实例化其他类
 * 2.使用引用或指针形式传递
 */

6.5 多态

6.5.1 虚函数

#include <iostream>

using namespace std;
//虚函数
class A{
public:
    // virtual A(){}  // 构造虚函数编译错误
    A(){
        cout << "A构造"<<endl;
    }
     virtual ~A(){
        cout << "A析构"<<endl;
    }
    virtual void test(){
        cout << "A-test..."<<endl;
    }
};
class B: public A{
public:
    B(){
        cout << "B构造"<<endl;
    }
     ~B(){
        cout << "B析构"<<endl;
    }
    void test(){
        cout <<"B-test..."<< endl;
    }
};
//纯虚函数
class C{
public:
    virtual void test1() = 0;  // 只用作子类重写函数的声明  定义新内容在子类实现
};
class D: public C{
public:
    void test1(){
        cout << "D执行111"<<endl;
    }
};
class E: public C{
public:
    void test1(){
        cout << "E执行222"<<endl;
    }

};
int main(){
    // 虚函数
    A *pa = new B;  // 父类添加虚函数
    delete pa;
    cout<<"---------------------"<< endl;
    B b;
    A & a = b;
    cout<<"---------------------"<< endl;

    // 纯虚函数
    // C c1; // 禁止纯虚函数创建实例
    C * pc = new D();
    pc->test1();  // D执行111
    delete pc;
    cout<<"---------------------"<< endl;
    C* pcc = new E();
    pcc->test1();  // E执行222
    delete pcc;
    
    return 0;
}
/* 虚函数
 * 1. 实现多态的机制
 * 2. 实现父类指针(引用) 指向子类实例的前提下 通过父类调用子类的成员函数
 * 3. 使用virtual关键字声明
 * 4. 虚函数=0 为纯虚函数
 * 5. 对象成员有虚函数 实则是编译器在处理虚函数时
 *    往对象添加一个隐藏的指针 指针指向一个数组 数组内部存放虚函数的地址
 * 6. 构造函数不能为虚函数(有对象才有虚函数)
 * 7. 析构函数可以为虚函数
 * 7.1 继承下父类指针接收子类对象 必须在父类析构加上虚函数 不加不会执行子类析构
 * 纯虚函数
 * 1.纯虚函数没有函数体 声明时 函数名后=0 
 * 2.纯虚函数在父类只作为对子类重写函数的声明 实现均在子类实现
 * 3.有纯虚函数的类为抽象类 禁止其创建对象 且继承父类的子类必须实现纯虚函数的实现 否则子类也无法创建实例 但是父类可以实现其他方法函数 继承下 子类亦可调用
 */


6.5.2 多态实例

#include <iostream>

using namespace std;

class animal{
public:
    virtual void run(){
        cout <<"animal-run..."<<endl;
    }

//    void run(){
//        cout <<"animal-run..."<<endl;
//    }
};
class cat: public animal{
public:
    void run(){
        cout << "cat-run..." << endl;
    }
};
class dog: public animal{
public:
    void run(){
        cout << "dog-run..."<< endl;
    }
};
class Home_Pet{
public:
    void pet_active(animal * a){
        a->run();
    }

};

int main(){
    //多态
    //1.静态多态(静态联编)
    cout<<"------------1------------"<< endl;
    animal a;
    cat c;
    a.run();
    c.run();
    cout<<"------------2------------"<< endl;
    animal *a1 = new animal();
    cat *c1 = new cat();
    animal * a2 = new cat(); //静态联编:animal-run...
    a1->run();
    a2->run();
    c1->run();
    cout<<"------------3------------"<< endl;
    //2.动态多态(动态联编)
    animal * a3 = new cat();  // 动态联编:父类成员函数为虚函数:cat-run...
    a3->run();

    //3.多态应用
    cout<<"------------4------------"<< endl;
    Home_Pet h;
    dog *d4 = new dog;
    cat *c4 = new cat;
    h.pet_active(d4);  // dog-run...
    h.pet_active(c4);  // cat-run...

    delete a1, a2, a3, c1, d4, c4;
    return 0;
}
/* 多态
 * 1.继承
 * 2.子类重写父类同名函数
 * 联编机制
 * 1.静态:编译过程决定调用的对象函数(函数)(默认)
 * 2.动态:运行过程决定调用的对象函数(1.使用虚函数 virtual关键字修饰的成员函数)
 * 动态类型:运行期间确定的数据类型
 * 静态类型:编译期间确定的数据类型
 * 虚函数
 * 1.virtual 修饰的函数为虚函数
 * 2. 虚函数=0 为纯虚函数
 */


7. 指针

7.1 一级指针 二级指针

#include <iostream>

using std::cin;
using std::cout;
using std::endl;


int main(){
    //指针(指针声明type* 变量名)
    int a[5]={10,20,30,40}, *pa;
    pa = a;  // 或者pa = &a[0]; 只是a指向数组的首个元素地址 &a[0]指向数组第一个元素地址
    cout << pa << " " << a << endl;  // 0x61fde0 0x61fde0
    cout << "a+1  " << a+1<< endl;  // a+1  0x61fde4
    cout << "&a[0]  " << &a[0] << endl;  // &a[0]  0x61fde0
    cout << "&a+1  " << &a+1 << endl;  // &a+1  0x61fdf4
    cout << "a+5  " << a+5 << endl;  // a+5  0x61fdf4
    printf("分隔符------------------------------------------------------>\n");

    // 指针类型(右值&(连字号运算符)获取内存地址)
    int  b= 1, *pb;  // *pb 类型:int* (剔除指针名)
    pb = &b;
    cout << pb << " "<< *pb << endl;  // 0x61fe04 1(取址取值)
    cout << &b << " " << b << endl;  // 0x61fe04 1
    printf("分隔符------------------------------------------------------>\n");

    // 指针所指向的类型
    int *(*pc)[] = NULL;  // 指针pc所指向的类型 int*()[](剔除指针名&&左*)

    //指针的值(*获取指针的值->解引用的过程)
    int *pd = NULL;  // 空指针pd
    cout << "空指针pd的值:" << pd << endl;  // pd的值:0
    printf("分隔符------------------------------------------------------>\n");

    // 二级指针(**pg)
    int g=10, *pg, **ppg;
    pg = &g, ppg = &pg;
    cout << g <<"  "<< &g << endl;
    cout << *pg <<"  " << pg << endl;
    cout << **ppg << "  " << *ppg <<  " " << ppg << endl;
    //    10  0x61fdd8
    //    10  0x61fdd8
    //    10  0x61fdd8 0x61fdd0

    //常量与指针
    printf("------------------------\n");
    int n = 111, n1 = 222;
    const int *pn = &n1;  // 指针指向一个常量
    pn = &n1;  // 可以进行重新指向
    //*pn = 112;  // 不可进行解引用修改数据
    printf("------------------------\n");
    //常量指针
    int* const pp = &n;  // 初始化一个常量指针
    *pp = 333;  // 解引用可以进行数据修改
    //pp = &n1; // 常量指针不可进行重新指向
    //指针常量指向常量
    const int* const ppp = &n;
    //*ppp = 444;
    //ppp = &n1;  // 不可进行重新指向 亦不可进行解引用数据修改
    return 0;

}

/*运算符优先级: ()>[]>*
 * 指针 也是一个变量 也占用内存 明确指针的内存地址是其本身开辟空间内存的地址 和指针的值是指向的变量的内存地址 区别
 * 空指针 可以使用int* p = 0 || int* p = nullptr(c++11) || int* p = NULL声明
 * 指针指向常量 不可进行解引用修改地址所指向的数据 可以进行重新指向新的地址
 * 常量指针 不可进行重新指向 可以解引用进行数据修改
 * 二级指针(指针的指针)存放一级指针在内存中的地址 ;一级指针存放变量数据的内存地址;
 */

7.2 指针与函数

#include <iostream>
#include <synchapi.h>

using namespace std;

// 指针函数(函数返回指针)
int* change(){
    int n = 100;
    // 返回栈内存的一个地址 函数执行结束可能存在局部变量被销毁释放
    //return &n;

    // 解决:使用堆内存进行开辟新内存空间解决局部变量被销毁问题
    return new int(n);
};

//函数指针
void who(string name){
    cout<< name  << endl;
}


void who_say(string x, void(*p)(string)){
    p(x);
}

int main(){
    // 指针函数的问题(局部变量值)
    int* data = change();
    Sleep(1000);  // 延时操作
    cout << "data=" << *data << endl; // data=0(return &n结果)
    printf("-------------------------\n");
    cout << "data=" << *data << endl; // data=100(return new int(n)结果)
    delete data;  // 释放堆内存data
    data = nullptr;  // 避免指针随机指向内存 占用资源
    printf("-------------------------------------------\n");

    //函数指针
    who("mary");  // 直接调用函数
    void (*pf)(string) = who;  // 没有返回值 有参数的函数指针
    pf("peter");  // 函数指针调用
    printf("-------------------------------------------\n");

    //函数指针应用
    who_say("lory", pf);  // lory
    return 0;

}
/* 函数指针与指针函数的区别
 * 函数指针:是指针 指向函数; 指针函数:是函数 返回值是指针;
 * 函数指针: type (*fn)(int); 指针函数: type *fn(int);
 * 函数指针应用:降低耦合度 直接使用函数指针传递 调用函数进行使用
 */


7.3 指针与数组

#include <iostream>

using namespace std;

int main(){
    int d[]{1,2,3,4}, *pd = NULL;  // 空指针pd
    cout << "空指针pd的值:" << pd << endl;  // pd的值:0
    cout << "d输出的值:" << d << "  sizeof(d):" << sizeof(d) << endl;
    //指针与数组
    pd = d;  // ->pd = &d[0]
    cout << "pd的值:" << *pd << "  sizeof(*pd):" << sizeof(pd)<<endl; // pd的值:1  sizeof(*pd):8 (*指针在表达式中表示获取指针值)
    cout << "通过指针pd访问数组元素(推荐使用:确保访问的一定是数组元素):" << endl; // *(d+1)=2 ->d[1]=2
    while (pd<=&d[sizeof(*d)-1]){
        cout << pd << "  " << *pd << endl;
        pd ++;
    }
    /*
    通过指针pd访问数组元素:
    0x61fdc0  1
    0x61fdc4  2
    0x61fdc8  3
    0x61fdcc  4
     */
    printf("数组指针-----------------用法和其他指针相同------------------------------------->\n");
    int q[3] = {1,2,3};
    int (*pq)[3] = &q;
    cout << pq << " " << *pq << " " << &q[0]<<endl;  // 0x61fdf4 0x61fdf4 0x61fdf4
    cout << pq+1 << " " << *(pq+1)  << " " << *(pq+2) << endl;  // 0x61fe00 0x61fe00 0x61fe0c
    cout << **pq << " " << *pq[0] << endl;  // 1 1 (右值*取值解引用)

    // *pq指向数组首个元素地址 == *(pq+1)是下标为1数组的元素地址 == pq指向整个数组的首地址;
    // *(pq+1) 实则是地址的偏移 偏移了指针指向类型(int=4[64位系统])字节
    // *(*pq):解引用首个元素地址指向的值;*(pq[0]):*(数组首个元素地址)=地址指向的首个元素值
    printf("指针数组--------------------------------------------------------------------->\n");
    int t[4]={1,2,3,4};
    int* s[4];  // 定义一个指针数组
    s[0] = &t[0];  // 赋值操作
    cout << *s[0] << endl;  // 1
    return 0;
}
/* 优先级:()>[]>*
* 指针数组与数组指针区别
* 指针数组:是数组 所有元素均为指针类型; 数组指针:是指针 指向一个数组的首元素地址;
* 指针指向数组默认指向数组首元素地址;可以通过偏移*(arry+1) 获取所有数组数据
*/

7.4 指针与结构体

#include <iostream>
#include <vector>

using namespace std;

//指针与结构体
struct B{
    int x;
    int y;
    vector<int> z;
};

int main(){
    // 指针与结构体
    B bb={1,2};  // 初始化结构体 bb
    (bb.z).push_back({11});
    B* pbb = &bb;  //声明一个指向bb的 B*类型指针
    // 通过指针访问结构体元素
    cout << pbb->x << "  " << (bb).x<< endl;  
    cout << pbb->y << "  " << bb.y <<  endl;
    cout << pbb->z.at(0) << "  " << bb.z.at(0) <<  endl;

    printf("--------------------------------------\n");
    //通过偏移位置(指针变量+变量类型长度)获取结构体数据
    int *pbbb = (int*)&bb;  // 声明一个指向bb的int*类型指针
    cout << *pbbb << endl;
    cout << *(pbbb+1) << endl;
    return 0;
}

7.5 智能指针

#include <iostream>
#include <memory>

using  namespace  std;

class A;
class B;
class C;
class D;
class E;

class A{
public:
    int id;
    A(int id):id(id){cout<<"构造"<< id <<endl;}
    ~A(){cout<<"析构"<< id << endl;}
};

// shared_ptr循环拷贝问题
class B{
public:
    int id;
    shared_ptr<C> sp_c;
    void get_c(shared_ptr<C> sp_c){
        this->sp_c = sp_c;
    }
    B(int id):id(id){cout<<"B构造"<< id <<endl;}
    ~B(){cout<<"B析构"<<id <<endl;}

};
class C{
public:
    int id;
    shared_ptr<B> sp_b;
    void get_b(shared_ptr<B> sp_b){
        this->sp_b = sp_b;
    }
    C(int id):id(id){cout<<"C构造"<<id << endl;}
    ~C(){cout<<"C析构"<<id << endl;}
};

// weak_ptr解决循环拷贝问题
class D{
public:
    int id;
    //shared_ptr<E> sp_e;
    weak_ptr<E> wp_e;
    void get_e(shared_ptr<E> sp_e){
        this->wp_e = sp_e;
    }
    D(int id):id(id){cout<<"D构造"<< id <<endl;}
    ~D(){cout<<"D析构"<<id <<endl;}

};
class E{
public:
    int id;
    //shared_ptr<D> sp_d;
    weak_ptr<D>wp_s;
    void get_d(shared_ptr<D> sp_d){
        this->wp_s = sp_d;
    }
    E(int id):id(id){cout<<"E构造"<<id << endl;}
    ~E(){cout<<"E析构"<<id << endl;}
};
int main(){
    //智能指针
    //1.unique_ptr
    A * pa = new A(1);  // 构造1
    cout << "-----------------1----------------"<<endl;
    // 1.1初始化
    unique_ptr<A> p2(new A(2));  // 构造2 析构2 :自动释放指针对象
    // unique_ptr<A> p3 = p2;  // 编译错误 :禁止拷贝
    int* pi = new int(1);
    unique_ptr<int> p4(pi);
    // unique_ptr<int> p5(pi);  // 编译错误:禁止两个unique_ptr包装同一个指针
    // 1.2 解引用取值
    cout << "p4取值:" << *p4 <<endl;
    cout << "p4取值:" << *(p4.get()) <<endl;
    // 1.3 手动释放
    p4.reset();
    p2.reset(pa);  // 析构1: 释放pa
    cout << "-----------------2----------------"<<endl;

    //2.shared_ptr
    //2.1 初始化
    shared_ptr<A> sp(new A(3));
    cout <<"计数:" << sp.use_count() <<endl;
    shared_ptr<A> sp1 = sp;  // 可以进行拷贝
    cout <<"计数:" << sp.use_count() <<endl;
    //2.2 通过计数为0释放
    sp.reset();
    cout <<"释放计数:" << sp.use_count() <<endl;
    sp1.reset();
    cout <<"释放计数:" << sp.use_count() <<endl;
    //2.3 循环拷贝问题
    cout << "-----------------3----------------"<<endl;
    B * pb = new B(111);
    C * pc = new C(222);
    // 智能指针shared_ptr
    shared_ptr<B> sp_b(pb);
    shared_ptr<C> sp_c(pc);
    // 产生shared_ptr循环拷贝 未析构
    pb->get_c(sp_c);  // B构造111
    pc->get_b(sp_b);  // C构造222


    //3.weak_ptr
    //3.1 初始化(多用于shared_ptr)
    cout << "-----------------4----------------"<<endl;
    int* pii = new int(4);
    shared_ptr<int> sp2(pii);
    cout << "计数:" << sp2.use_count() << endl;
    shared_ptr<int> sp3 = sp2;
    cout << "计数:" << sp3.use_count() << endl;
    weak_ptr<int> wp(sp2);
    cout << "计数:" << wp.use_count() << endl;  // 计数不会增加
    //3.2 取值
    cout << "wp取值:"<< *wp.lock()<<endl;
    cout << "wp取值:"<< *(wp.lock().get())<<endl;
    //3.3 解决shared_ptr循环引用问题
    cout << "-----------------4----------------"<<endl;
    D * pd = new D(333);
    E * pe = new E(444);
    // 智能指针shared_ptr
    shared_ptr<D> sp_d(pd);
    shared_ptr<E> sp_e(pe);
    // shared_ptr不会循环拷贝 构造+析构
    pd->get_e(sp_e);
    pe->get_d(sp_d);

}
/* 智能指针必要性(堆内存数据) 避免出现下面问题:
 * 1.野指针: 声明指针未初始化 int *p; 解决:int *p = nullptr;
 * 2.重复释放:对同一指针多次delete操作; 解决:delete 一次
 * 3.内存泄露:int *p = new int(4); 未对delete p;操作 堆内存未回收; 解决:delete p;
 *
 * 智能指针c++11
 * 1.需要加载头文件<memory>
 * 2.三种:unique_ptr(唯一)  shared_ptr   weak_ptr
 * 3.实质均是类对象
 * 4.多为栈内存智能指针 对堆内存数据释放 堆内存智能指针没有意义
 *
 * unique_ptr
 * 1.唯一指针 初始化unique_ptr<类型> p(指针)
 * 2.不可进行拷贝操作 同一个指针不可同时用unique_prt包装
 * 3.默认自动释放 可以进行显式(手动)释放内存 p.reset();
 *
 * shared_ptr
 * 1.共享(同一内存)指针 初始化shared_prt<类型>p(指针);
 * 2.可进行拷贝操作 但是作为类成员 可能产生循环拷贝问题
 * 3.通过p.use_cout()判断计数 计数为0则释放
 *
 * weak_ptr
 * 1. 弱指针 初始化weak_ptr<类型> p(shared_ptr or weak_ptr)
 * 2. 指针包装多用于shared_ptr 可解决shared_ptr循环拷贝问题
 */


8.引用

#include <iostream>

using namespace std;

int t1(int a, int b){return 0;}
int t2(int &a, int &b){return 0;}
int t3(int &&a, int &b){return 0;}
int t4(int &&a, int &&b){return 0;}
//返回值为引用
int& t5(int &a){
    return a;
}
int t6(int &b){
    return b;
}

int main(){
    //左值引用(左值引用默认只接收左值 使用const关键字可以使用右值)
    int a = 1, b = 2;
    int& c = a;  // c:左值引用; a:左值
    // int &e = 11;  // 报错
    const int &d = 11;
    printf("--------------------------\n");
    
    //右值引用(c++11使用 只接收右值 不能接收左值)
    //int && g = a;  // 报错
    int && f = 3;  // f:右值引用; 2:右值
    printf("-----------函数调用---------------\n");
    t1(a, b);
    t2(a, b);
    t3(1, b);
    t4(1, 2);
    t5(a) = 111;
    //t6(a) = 111;  // 报错
    cout << (t5(a) = 111) << endl;  // 111
    // 函数的返回值为引用 则编译器不会为返回值创建临时变量 直接返回变量的引用 若返回局部变量 则会在函数调用结束后销毁


}
/* 左右值判断:  左值||右值 = 右值 或 是否能获取地址 能则是左值 不能则是右值
 * 引用:变量别名 不占用内存 指向目标变量的内存地址 不会开辟新空间;
 * 不存在空引用 定义时必须初始化指向一个变量;数组没有引用 数组是一堆数据 引用只能指向一个数据;
 * 变量可以有多个引用 引用只能指向一个变量;
 * 应用:变量起别名&&形参接收参数(非拷贝 直接指向传递过来的实参变量值 效率高)&&引用作为返回值不会在内存中产生副本;
 */


9. 异常处理

#include <iostream>
#include <vector>
using namespace std;


int div1(int a, int b){
    if(b==0){
        //1.常规异常处理
        //exit(111);  //退出程序操作
        //abort();  // 终止程序
        //2.抛出异常(exception)
        throw runtime_error("b不能为0");
    }
    int res = a/b;
    return res;
}

int main(){
    // 异常(exception)捕获
    try{
        div1(1, 0);
    } catch (runtime_error e) {
        cout << e.what() <<endl;
    }
    cout << "-------------------------------"<< endl;
    vector<int> v = vector<int>{1,2,3};
    try{
        v.at(100);
        throw out_of_range("越界");
    }catch (...) {
        cout << "任何类型异常捕获信息" << endl;
    }
    return 0;

}
/* 异常
 * 所有异常均继承自exception
 * 1.异常处理
 * 1.1 常规异常处理 exit(错误码); || abort();
 * 1.2.抛出异常 throw 异常子类(错误信息)    exception(异常父类)
 * 2.异常捕获
 * 2.1 常规:try{可能存在异常代码}catch(异常类型 exception){异常处理}
 * 2.2 多个catch 也只会执行一个
 * 函数异常声明 void fn(int x) throw(int,char*,);
 */


10. 动态内存

10.1 new delete

#include <iostream>

using namespace std;

int main(){
    //栈内存数据 函数调用结束 自动删除
    int a = 1;

    // 堆内存数据(开辟空间 存数据 接收空间 需要手动删除回收资源)
    //    new int;  // 开辟空间new 类型
    //    new int(10);// 存数据10
    int* p = new int(10);  // 指针接收空间数据10
    // 分布实现
    //    int* p1 = nullptr;
    //    p1 = new int;
    //    *p1 = 10;
    cout << "堆内存解引用数据:" << *p << endl;  // 10
    // 栈内存释放内存
    delete p;
    p = nullptr;  //p 不再指向堆内存空间
    if (p!= nullptr){
        cout << "堆内存数据:" << *p << endl;
    }

    //数组与堆内存
    int* p_arry = new int[10];
    cout << "第0个元素:" << *p_arry << endl;
    cout << "第1个元素:" << *(p_arry+1) << endl;

    // 栈内存释放删除数组空间
    delete[] p_arry;

    return 0;
}
/* c++动态内存分配 new 数据类型(数据) 删除释放delete
 * new
 * 1.new 自定义类型 默认执行类的构造函数 
 * 2.成功申请内存空间 返回内存空间地址 用相应类型指针接收
 * 3.申请失败 则抛出bad_alloc异常
 * 4.本质是c 中malloc实现
 *
 * delete
 * 1.delete 自定义类型 默认执行类的析构函数
 * 2.释放内存 不会修改指针 指针依然指向原来的地址
 * 3.重复delete 会出现异常
 * 4.本质是c 中free实现
 *
 *
 * 内存区划分
 * 1.栈内存:局部变量 函数的参数等 空间小 调用函数结束回收资源 有独立的回收机制 存活周期短
 * 2.堆内存:手动申请变量内存(new) 空间大 不存在回收机制 手动回收(delete) 存活周期长
 * 3.代码区:存放执行代码 只读常量
 * 4.共享内存区:高效I/O映射方式 用于装载一个共享的动态内存库,用户可以使用系统接口创建共享内存 做进程间通讯
 * 5.字符常量区:常量字符串
 * 6.静态存储区: 全局数据 静态数据
 */

10.2 malloc free

#include <iostream>

using namespace std;
class A{
public:
    A(){cout<<"构造"<<endl;}
    ~A(){cout<<"析构"<<endl;}
};

int main(){
    // c动态内存
    //1. malloc
    //1.1 申请空间
    // 自定义申请空间大小(字节)
    malloc(4);  // 申请4字节的空间大小
    // 根据类型自动申请空间大小
    malloc(sizeof(int));
    //1.2 存值
    int * pa = (int*) malloc(sizeof(int));
    *pa = 1;
    cout << "pa取值:" << *pa <<endl;
    free(pa);
    cout << "pa取值:" << *pa <<endl;
    cout << "-----------------1---------------------"<<endl;
    //2. free
    A* paa = (A*) malloc(sizeof(A));
    A a;  // 构造  析构
    *paa = a;
    free(paa);
    return 0;
}
/* c 动态内存 malloc --free
 * malloc
 * 1.申请空间 需要提供空间大小(字节)
 * 2.申请成功空间返回void *  失败返回NULL即是空指针
 * 3.不会执行自定义类型的构造函数
 * 
 * free
 * 1. 空指针可以多次释放 非空则不允许
 * 2. 不会执行自定义类型的析构函数
 */


11. 模板

//函数模板
template <typename 模板名1, typename 模板名2,...>
返回类型 函数名(模板名 参数名){函数体}

//类模板(class == typename效果一致 单纯用于区分修饰的模板类型)
template <class T1, class T2, ...>
class 类模板名{
    成员变量+成员函数
}

11.1 函数模板

#include <iostream>
#include <cstdarg>  // ...作为函数形参接收可变参数


using namespace std;

// 弱化类型+模板重载
template <typename T, typename H>
T cal(T a, H b){
    return a + b;
}
template <typename T, typename H, typename J>
J cal(T a, H b, J c){
    return a+b+c;
}

// 可变参数(参数类型不变)
template <typename T>
T sum_all(double _a, initializer_list<T> li){
    const T * begin = li.begin();
    T all =0;
    for(const T & n: li){
        all += n;
    }
    return all;
}

// ...省略号函数模板(参数类型可变)
template<typename T>
void test(T t){  // 终止递归的最后一个元素
    cout <<"0----->"<< t<<endl;
}
template<typename... S, typename T>
void test(const T& t, const S&... args){  //&:避免传参拷贝 使用引用 const可接收左右值
   cout << "可变参数个数:" << sizeof...(S) <<endl;
   cout << "1----->" << t <<endl;  // 输出参数值
   test(args...);  // 递归调用打印其他参数

}

// ...作为形参
void cout_all(int a, ...){
    va_list args_li;  // 获取可变参数列表
    int all=0;
    int argvalue;
    all += a;
    va_start(args_li, a);  // 指定开始循环获取的参数列表
    do{
        argvalue = va_arg(args_li, int);  // 获取参数值
        all += argvalue;
    } while(argvalue!= 0);  // =0终止循环遍历参数列表
    va_end(args_li); // 结束可变参数的遍历
    cout << "all=" << all <<endl;
}


int main(){
    //模板编程(实质:重载过程)
    //函数模板
    //1.弱化参数类型
    cout << cal(1.1, 1) << endl;
    cout << cal(1.11, 1, 1.456) << endl;
    cout << "-----------------------1-----------------------"<<endl;
    //2.弱化参数个数(使用可变参数)
    cout << "sum=" << sum_all(99.99,{1.1, 2.1, 3.1, 4.1, 5.1, 6.0}) <<endl;
    cout << "-----------------------2-----------------------"<<endl;
    //3.省略号在函数模板作为可变参数
    test("aa", 3.14, 5.55, 666, 'q');
    cout << "-----------------------3-----------------------"<<endl;
    //4.省略号作为函数形参
    cout_all(1, 2, 3, 4, 0);

}
/* 模板编程实际上就是重载过程
 * 函数模板(重新构建一个新函数 弱化参数类型 参数传递个数)
 * 1.重新构建一个新函数
 * 2.弱化了参数类型 并且函数模板也可以重载
 * 3.弱化参数个数(使用可变参数|python:*args):initializer_list(类型一致) 或省略号(类型可不同)
 * 4.可变参数initializer_list<类型唯一> 解引用获取参数值
 * 5.省略号参数->可变参数应用函数模板 必须放置在形参最后 否则会导致递归函数执行编译错误
 *
 */


11.2 类模板

#include <iostream>

using namespace std;

// 类模板
template <typename T>
class DIY_Vector{
    // 数组接收数据
    T c[10];
    int i=0; //下标索引
public:
    DIY_Vector()=default;
    DIY_Vector(initializer_list<T> li){
        for(auto v :li){
            c[i] = v;
            i++;
        }
        cout << c << endl;
    }

    T at(int id){
        return c[id];
    }
    int size(){
        return i;
    }
    T operator[](int id){
        return c[id];
    }
    void push(T v){
        c[i] = v;
        i++;
    }
};


//继承下类模板
template <class T1>
class F{
public:
    T1 a;
    F(T1 a):a(a){cout<<"F有参构造"<<endl;}
    ~F(){cout<<"F析构"<<endl;}
};


// 子类不是模板类(强制指定父类类型)
class S1: public F<int>{
public:
    S1(int b):F(b){cout<<"S1有参构造"<<endl;}
    ~S1(){cout<<"S1析构"<<endl;}
};

// 子类是模板类
template<class T1>
class S2: public F<T1>{
public:
    S2(T1 c):F<T1>(c){cout<<"S2有参构造"<<endl;}
    ~S2(){cout<< "s2析构"<<endl;}
};
int main(){
    // 模板类(多应用于多变容器)
    //1. 模拟vector
    DIY_Vector<int> c;
    c.push(11);
    c.push(22);
    c.push(33);
    cout << "c.size():" << c.size()<< endl;
    cout << "c.at():" << c.at(0)<< endl;
    cout << "c[]:" << c[2]<< endl;
    cout << "----------------1---------------------"<<endl;
    //2. 继承下模板类
    F<int> f(11);
    S1 s1(22);
    S2<string> s2("abc");
    cout << f.a << "\t" << s1.a << "\t" << s2.a << endl;
    return 0;
}


12. 文件

在 UNIX/Linux 平台中,用文本方式或二进制方式打开文件没有任何区别。

在 UNIX/Linux 平台中,文本文件以\n(ASCII 码为 0x0a)作为换行符号;而在 Windows 平台中,文本文件以连在一起的\r\n(\r的 ASCII 码是 0x0d)作为换行符号。

在 Windows 平台中,如果以文本方式打开文件,当读取文件时,系统会将文件中所有的\r\n转换成一个字符\n,如果文件中有连续的两个字节是 0x0d0a,则系统会丢弃前面的 0x0d 这个字节,只读入 0x0a。当写入文件时,系统会将\n转换成\r\n写入。

也就是说,如果要写入的内容中有字节为 0x0a,则在写人该字节前,系统会自动先写入一个 0x0d。因此,如果用文本方式打开二进制文件进行读写,读写的内容就可能和文件的内容有出入。

因此,用二进制方式打开文件总是最保险的  ----转载于http://c.biancheng.net/view/311.html

c++ 标准库提供读写文件方式
    1. >> || << 读写文件:适用于以文本形式读写文件   ios::in | ios::out
    2.read() || write() :适用于二进制形式读写文件  ios::out | ios::binary

12. 1 fstream 读取文件

#----test
i/o test...
你好 i/o
测试数据
11, 22, 33, 44
55, 66, 77, 88
    
#---- main.cpp
#include <iostream>
#include <fstream>  // 文件流
#include <sstream>  // 字符流stringstream
#include <string>  // 提供stoi() 字符串转整形  || to_string 整形转字符串


using namespace std;

int main(){
    //读取文件
    //1.创建文件流对象
    fstream fs("../3_other/test");
    //2.打开文件
    if(fs.is_open()){
        //3.1 read()读取文件(中文编码问题)
//        char c[100];
//        fs.read(c, 100);
//        cout << "读取数据:" << c <<endl;
        cout<<"-----------------------"<< endl;
        //3.2 getline()读取文件(不会出现编码问题)
        string content;
//        getline(fs, content,'\n');  // 默认读取到'\n'结束
//        cout <<"读取的单行数据:" << content <<endl;
        cout<<"-----------------------"<< endl;
        //3.3 多行输出
//        while(getline(fs, content)){
//            cout <<"读取的数据:" << content <<endl;
//            cout<<"-----------------------"<< endl;
//        }
        //3.4 读取数据再处理
        while(getline(fs, content)){
            stringstream ss;  // 创建字符流
            ss << content;
            string s;  // 单独一个数据
            while(getline(ss, s, ',')){
                cout << s <<"\t";
            }
           cout << endl;
        }
    }else{
        cout <<"文件打开失败"<<endl;
    }
    fs.close();  // 关闭文件
    return 0;
}

12. 2 fstream 写入文件

#----test1
xxx
#----main.py
#include <iostream>
#include <fstream>
#include <vector>


using namespace std;

int main(){
    //写入数据
    //数据准备
    vector< vector<int> > arr{
            {1, 2, 3, 4, 5},
            {6, 7, 8, 9, 10},
            {11, 12, 13, 14, 15}
    };

    fstream fs("../3_other/test1", ios::out);
    if(fs.is_open()){
        //1.write(数据, 数据长度)写入数据
        fs.write("c++", 1);
        //2. << 写入数据 不限定长度
        fs<<"c++ is no.1" << endl;
        //3. 写入较复杂数据
        for(vector<int> vi: arr){
            for(int v :vi){
                fs<<v;
                if(v!=vi[vi.size()-1]){
                    fs<<",";
                }
            }
            if(vi != arr[arr.size()-1]){
                fs << "\n";
            }

        }

    }else{
        cout << "文件打开失败"<<endl;
    }
    fs.close();
    return 0;

}
/* 文件打开模式
 * 1.ios::app 追加模式 所有写入追加到文件末尾
 * 2.iso::ate 文件打开定位到末尾
 * 3.ios::in 打开文件用于读取
 * 4.ios::out 打开文件用于写入 覆盖之前写入的数据
 * 5.ios::trunc
 */

12.3 fstream 定位文件位置

#----test2
abcdefg
hijklmx
    
#----main.cpp
#include <iostream>
#include <fstream>


using namespace std;

int main(){
    //创建文件流
    fstream fs("../3_other/test2");
    if(fs.is_open()){
        //文件内容定位
        char c, c1, c2;
        fs.seekg(3L, ios::beg);  // 定位读取数据(字节)
        fs>>c;
        cout << "从开头定位读取的数据:" << c << endl;
        fs.seekg(-1L, ios::end);
        fs>>c1;
        cout << "修改前从末尾定位读取数据:" << c1 << endl;
        fs.seekp(-1L, ios::end);  // 定位修改数据(字节)
        fs.put('z');
        fs.seekg(-1L, ios::end);
        fs.get(c2);
        cout << "修改后从末尾定位读取数据:" << c2 << endl;


    }else{
        cout <<"打开失败"<<endl;
    }
    fs.close();
    return 0;

}
/* seekp() 定位写入数据
 * seekg() 定位读取数据
 * 文件定位模式
 * ios::beg 从文件头开始偏移
 * ios::end 从文件尾开始偏移
 * ios::cur 从当前位置开始偏移
 *
 * get(char* s, streamsize n, char delim) || put():逐个读写单个字符(可指定读取长度字符串)
 */



12.4 二进制格式 文件读写

#include <iostream>
#include <fstream>

using namespace std;

int main(){
    //二进制写入文件
    char arr[5]{1, 2,3,4,5};
    ofstream f1("../3_other/test2", ios::out| ios::binary);
    f1.write((char*)&arr, sizeof(arr));
    f1.close();
    //二进制读取文件
    ifstream f2("../3_other/test2", ios::in | ios::binary);
    f2.read((char*)&arr, sizeof(arr));
    for(int i:arr){cout << i << "\t" << endl;}
    f2.close();
    return 0;
}


13.多任务

13.1 线程

#include <iostream>
#include <thread>


using namespace std;

void test(int id){
    cout << "Thread-test..."<< id << endl;
    cout << "test函数线程id:" << this_thread::get_id() << endl;
}

void test1(int id){
    bool flag = true;
    int i = 0;
    while (flag){
        cout << "test1..."<< id << endl;
        i++;
        if(i>10){
            //break;
            //return;
            flag = false;  // 结束线程
        }
    }

}
int main(){
    //线程
    //1.创建线程 且自动启动
    thread t(test, 11);
    // 主线程等待子线程执行完毕
//    t.join();
    // 主线程与子线程分离 子线程失去所有权 各自执行 主线程结束 子线程强制结束
    t.detach();

    cout << boolalpha << "t线程所有权:" << t.joinable() << endl;
    // 2.线程id
    cout << "t-id:" << t.get_id() <<endl;
    cout << "Main-test..."<<endl;
    cout << "主函数线程id:" << this_thread::get_id() << endl;

    // 3.手动结束线程
    thread t1(test1, 22);
    t1.join();
}
/* 线程
 * 1. 创建线程并启动 thread t;
 * 2. t.join() 等待子线程结束
 * 3. t.detach() 主线程与子线程分离 各自执行 主线程结束 子线程结束
 * 4. 使用detach() 通过joinable()判断线程所有权
 * 5. 指定线程id获取 t.get_id(); 当前线程id获取 this_thread::get_id();
 * 6. 系统休眠 #include <zconf.h> sleep(1)
 * 7. 线程休眠 std::this_thread::sleep_for || std::this_thread::sleep_until
 */

13.2 多线程

#include <iostream>
#include <string>
#include <thread>
#include <zconf.h>
#include <mutex>  // 互斥量|互斥元


using namespace std;

// 1.mutex
mutex m;

void test(string t){
    for (int i = 0; i < 10; ++i) {
        m.lock();
        cout << t << ">>" << i << endl;
//        sleep(1);
        m.unlock();
    }
}


int m1 = 100;
void get1(){
    while(1){
        // 2. lock_guard<>
        lock_guard<mutex> lg(m);
        if(m1>=0){
            cout << "get1>>>" << m1 <<endl;
            m1-= 10;
            sleep(1);
        } else
            break;
    }
}
void get2(){

    while(1){
        // 2. lock_guard<>
        lock_guard<mutex> lg(m);
        if(m1>=0){
            cout << "get2>>>" << m1 <<endl;
            m1-= 10;
            sleep(1);
        } else
            break;
    }
}

int main(){
    // 多线程
    // 1.mutex 互斥量
    thread t1(test, "thread1");
    thread t2(test, "thread2");
    cout << t1.native_handle() << endl;
    cout << t1.get_id() << endl;
    t1.join();
    t2.join();
    cout << "主函数线程1结束"<< endl;
    cout << "-----------------------------------"<< endl;
    // 2. lock_guard
    thread t3(get1);
    thread t4(get2);
    t3.join();
    t4.join();
    cout << "主函数线程2结束"<< endl;
    cout << "-----------------------------------"<< endl;
    //3. unique_lock
    cout << "主函数线程3结束"<< endl;
    return 0;
}
/* 默认多线程并发 公有资源会出现资源竞争
 * 1.mutex互斥量 针对的是公有共享资源 需要手动上锁 解锁 不允许发生拷贝构造和移动构造
 * 2.lock_guard<mutex> 能够自动解锁 构建对象时 调用m.lock() 销毁对象 调用m.unlock();
 * 3.unique_lock<mutex> 类似lock_guard<mutex> 提供更好的上锁 解锁操作
 */



13.3 线程与成员函数

#include <iostream>
#include <thread>

using  namespace std;

// 全局函数
void test11(){
    cout << "全局函数test11"<< endl;
}


class A{
public:
    static void test22(){
        cout << "静态成员函数test22" << endl;
    }
    void test11(){
        cout << "成员函数test11" << endl;
    }

};

int main(){
    // 线程修饰全局函数
    thread t1(test11);
    t1.join();
    cout << "主函数线程1结束"<< endl;
    cout << "---------------------------"<< endl;
    // 线程修饰成员函数
    A a1;
    thread t2(&A::test11, a1);
    t2.join();
    cout << "主函数线程2结束"<< endl;
    cout << "---------------------------"<< endl;
    // 线程修饰静态成员函数
    thread t3(A::test22);
    t3.join();
    cout << "主函数线程3结束"<< endl;
	return 0;
}


13.4 条件变量

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

using  namespace  std;

mutex m;  // 互斥量
condition_variable cv;  // 条件变量
int n =0;

void sender(){
    //发送方
    for (int i = 0; i < 30; ++i) {
        unique_lock<mutex> ul(m);
        n += 10;
        cout << "s>>>存储10 当前n=" << n <<endl;
        if (n>=20){
            cout << "s>>>n>=20 通知接收者"<<endl;
            cv.notify_all();
        }
        ul.unlock();
    }
}

void receiver(){
    //接收方
    for (int i = 0; i < 30; ++i) {
        unique_lock<mutex> ul(m);
        cout << "r>>>查看n总量"<<endl;
        if(n<20){
            cout << "r>>>n<20 等待发送通知"<<endl;
            cv.wait(ul);
        }
        n-=20;
        cout << "r>>>接收20 当前n="<< n <<endl;
        ul.unlock();
    }


}
int main(){
    // 线程-条件变量
    thread t1(sender);
    thread t2(receiver);
    t1.join();
    t2.join();
    cout << "主函数线程结束"<< endl;
}
// 条件变量 保证线程同步(线程间按照指定顺序执行)

14. 拷贝

// 深浅拷贝浅谈
#include <iostream>

using namespace std;

// 浅拷贝问题
class test{
public:
    int *pid = nullptr;

    test(int* pid):pid(pid){
        cout << "构造函数执行-test" << endl;
    }
    // 构造拷贝函数
    test(const test& t){
        // 1.浅拷贝(默认)
        pid = t.pid;  // 成员数据单纯拷贝值(共享一个空间)
        cout << "拷贝构造函数执行-test" << endl;
    }
    ~test(){
        cout << "析构函数执行-test" << endl;
    }

};

//深拷贝
class test1{
public:
  int *pd = nullptr;
  test1():pd(pd){
      cout << "无参构造构造函数执行-test1" << endl;
  }

  test1(int* pd):pd{pd}{
      cout << "构造函数执行-test1"<<endl;
  }
  //拷贝构造函数
  test1(const test1 &t){
      // 2.深拷贝(初始化指针成员属性:开辟新空间+添加数据+赋值指针)
      pd = new int(*t.pd);  // 成员数据独立空间 互不影响
      cout << "拷贝构造函数执行-test1"<<endl;
  }

};

// 普通类test2用于验证拷贝触发
class test2{
public:
    test2(){
        cout<< "无参构造函数执行-test2"<<endl;
    }
    test2(const test2& s){
        cout<< "拷贝构造函数执行-test2"<<endl;
    }
    ~test2(){
        cout<< "析构函数执行-test2"<<endl;
    }
};


//对象作为函数传递参数
void fn(test1 t){ //传递的参数为对象
    cout <<"fn调用结束" <<endl;
}

//对象作为函数的返回值
test2 fn1(){
    test2 t;
    return t; // 返回值为对象
}


int main(){
    //拷贝
    //1.浅拷贝(共享空间)
    printf("---------------浅拷贝---------------\n");
    int* pt = new int(3);
    test t(pt);
    test t1 = t;  // 拷贝对象(执行拷贝构造函数)
    cout << "t.pid=" << *t.pid << endl;
    cout << "t1.pid=" << *t1.pid << endl;
    printf("--------------浅拷贝修改数据后----------------\n");
    *t.pid = 33;  // 存在问题:修改对象成员数据 另一个对象成员数据亦修改
    cout << "t.pid=" << *(t.pid) << endl;
    cout << "t1.pid=" << *t1.pid << endl;

    delete pt;

    //2.深拷贝(独立空间)
    printf("--------------深拷贝----------------\n");
    int* ptt = new int(4);
    test1 tt(ptt);
    test1 tt1 = tt; // 拷贝对象
    cout << "tt.pd=" << *tt.pd << endl;
    cout << "tt1.pd=" << *tt1.pd << endl;
    printf("--------------深拷贝修改数据后----------------\n");
    *tt.pd = 44;  // 解决:修改对象成员数据 另一个对象成员数据不影响
    cout << "tt.pd=" << *tt.pd << endl;  //
    cout << "tt1.pd=" << *tt1.pd << endl;

    delete ptt;

    //3.拷贝触发
    //3.1 对象作为函数参数传递
    printf("------------拷贝触发:对象作为函数参数传递------------------\n");
    fn(tt);  // out:拷贝构造函数执行-test1

    //3.2 对象作为函数返回值
    printf("------------拷贝触发:对象作为函数返回值------------------\n");
    test2 ttt = fn1();  // 默认out:无参构造构造函数执行-test2(编译器为了避免拷贝生成临时对象消耗内存 优化拷贝动作不显示)
    //手动编译(不经过编译器优化)
    //cmakelists.txt添加set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-elide-constructors")
    //out:
    //无参构造函数执行-test2 :创建对象test2 t
    //拷贝构造函数执行-test2 : 拷贝函数内对象t给保存在临时空间的对象t1
    //析构函数执行-test2 :删除函数fn1内对象t
    //拷贝构造函数执行-test2 :拷贝临时空间对象t1给ttt
    //析构函数执行-test2  :main函数调用结束删除临时对象t1和对象ttt
    //析构函数执行-test2
    return 0;
}
/* 浅拷贝 拷贝值 (对象成员有指针成员 指针值亦拷贝)
 * 深拷贝 额外开辟空间进行存储 对象之间成员数据不影响 对象占用独立的空间 仅针对对象成员有指针有实际应用
 * 拷贝触发:参数传递||对象指向已有对象||对象作为参数传递||对象作为返回值
 * 不要返回栈内存的地址 局部变量(地址||引用) 编译会出错
 * 避免编译器自动优化 手动编译:
 * 1.命令行加参数-fno-elide-constructors
 * $>g++ -std=c++11 main.cpp -fno-elide-constructors
 * 2.cmakelist.txt cmake编译配置
 * set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-elide-constructors")
 */

15 STL(标准模板库)

C++ STL(标准模板库)是一套功能强大的 C++ 模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈
C++ STL 核心组件
    1.容器(封装了数据结构的模板类 ps:vector<> list<> queue<>)
    2.算法(算法作用于容器 大多被设计成模板函数 提供容器各种操作方式 排序 修改 添加等)
    3.迭代器(遍历对象集合[容器]的元素来完成读写操作)
    4.适配器(提供适应多接口适用的模板类)
    5.内存分配器(为容器类模板提供自定义内存申请和释放操作)

15.1 序列容器(顺序容器)

序列容器:存储数据类型为基本数据类型 容器内部元素排序与值无关 与添加进的顺序相关 但是多数容器均可使用下标索引访问(模板函数实现运算符重载)
vector<int> i{1,2,3};  i.at(0);
容器 描述
string 与vector相似,尾部插入|| 删除速度快
array 固定大小数组,支持快速随机访问,不能添加和删除
vector 可变大小数组,支持快速随机访问,在尾部之外的位置插入和删除 速度慢
deque | queue 双端(区分前后)队列,支持快速随机访问,两端插入和删除速度快
forward_list 单向链表、只支持单向顺序访问,插入和删除快,查询和更新慢
list 与单向链表不同,它是双向链表,其他一样。
#include <iostream>
#include <list>
#include <deque>

using namespace std;

int main(){
    //顺序容器
    //1.list
    cout<<"----------list----------"<<endl;
    list<int> l1 {1, 2, 3};
    // 1.1 list 增
    l1.push_front(4);
    l1.push_back(5);
    // 1.2 删
    l1.remove(1);
    // 1.3 改
    l1.front()=55;
    cout<< "元素总个数:"<< l1.size() <<endl;
    cout << "第一个元素"<< l1.front() <<endl;
    l1.back() = 66;
    cout << "最后一个元素"<< l1.back() <<endl;
    // 1.4 查
    cout <<"所有元素-迭代器方式:";
    for (auto i = l1.begin(); i != l1.end(); i++){
        cout<< *i <<"\t";
    }
    cout<< "\n";
    cout << "从大到小排序:" <<  ""<<endl;
    l1.sort();
    for (auto i = l1.begin(); i != l1.end(); i++){
        cout<< *i <<"\t";
    }
    cout<< "\n";
    cout<<"----------deque----------"<<endl;
    deque<int> d;
    // 增
    d.push_back(11);
    d.push_back(22);
    d.push_back(33);
    d.push_back(44);
    // 删
    d.pop_front();
    // 改
    d.at(1) = 55;
    *d.begin() = 111;
    *(d.end()-1) = 444;
    cout << "元素总个数:" << d.size() <<endl;
    cout << "第一个元素:" << d.at(0) << " " << d[0] << " " << d.front() << endl;
    cout << "最后一个元素:" << d.back() <<endl;
    // 查
    cout << "所有元素:";
    for(int v:d){
        cout << v << "\t";
    }
    cout <<endl;
    cout << "所有元素-使用迭代器";
    deque<int>::iterator v = d.begin();
    while (v != d.end()){
        cout <<  *v << "\t";
        v++;
    }
    cout << endl;
}

15.2 关联容器(排序容器)

关联容器:存储数据形式<key:value>(树存储结构)  会根据键值大小对数据进行升序排序
可定义排序
map<int, less<int>>  || map<int, greater<int>>
容器 描述
map **key值唯一 可以通过key修改value **
multimap 区别于map multimap可存储多个K值相同的键值对
set K=V 且 唯一 键 值均不能修改
multiset key=value 但可以重复 键 值均不能修改**
#include <iostream>
#include <map>
#include <string>
#include <set>

using namespace std;

int main(){
    //pair(键值对)
    cout << "--------------pair----------------"<<endl;
    pair<string, int> p("aa", 24);  // 生成键值对元素
    cout << p.first << " " << p.second <<endl;

    // 关联容器
    //2.map
    cout << "--------------map----------------"<<endl;
    map<int, string> stu;
    //增
    stu.insert({0, "aa"});
    stu.insert(make_pair(1, "bb"));
    stu.insert(pair<int, string>(2, "cc"));
    stu.insert(pair<int, string>(3, "dd"));
    stu.insert(pair<int, string>(3, "ee")); // key相同 添加失败
    //删
    stu.erase(0);  // 删除指定key元素
    // stu.clear(); // 删除所有元素
    //改
    stu.at(1) = "bbb";
    stu[2] = "ccc";
    // 查
    cout << "key=1 vaule=" << stu.at(3) <<endl;
    cout  << "key=1在容器出现的次数:" << stu.count(3) <<endl;
    cout << "元素总个数:" << stu.size() <<endl;
    cout<< "容器是否为空:" << stu.empty() << endl;
    cout<< "所有元素:";
    for(pair<int, string> p1: stu){
        cout << p1.first << ":" << p1.second << "\t";
    }
    cout<<endl;
    cout << "--------------multimap----------------"<<endl;
    //3.multimap
    multimap<int, string> multi_m;
    multi_m.insert({1, "aa"});
    multi_m.insert({0, "aa"});
    multi_m.insert({0, "bb"}); // 增加数据
    cout<<"元素总个数:" << multi_m.size()<<endl; // 容器元素个数
    cout<<"出现次数:" << multi_m.count(0) <<endl;  // key值出现次数
    //multi_m.erase(0);  // 删除key=0的所有value值
    //multi_m.clear(); // 删除所有元素
    cout << "所有元素:";
    for(auto i=multi_m.begin();i != multi_m.end(); ++i){
        cout << i->first << ":" << i->second <<"\t";
    }
    cout<<endl;
    // 4.set
    cout << "--------------set----------------"<<endl;
    set<int> s;
    // 增
    s.insert(111);
    s.insert({222, 333, 444, 555, 555});
    // 删
    s.erase(111);  // 删除第一个元素
    //s.erase(s.begin());
    // 查
    cout<< "元素总数:" << s.size() <<endl;
    cout << boolalpha << "容器是否为空:" << s.empty() <<endl;
    cout << "迭代器方式-所有元素:";
    for(auto i=s.begin(); i != s.end(); ++i){
        cout << *i << "\t";
    }
    cout << endl;
    cout << "--------------multiset----------------"<<endl;
    multiset<int, greater<int>> ms;
    //增
    ms.insert(10);
    ms.insert({9, 1, 3, 4, 2, 2, 2});
    // 删
    ms.erase(3);
    //ms.erase(2);  // 删除相同元素 会全部删除2
    //查
    cout << "元素总数:" << ms.size() <<endl;
    cout << "第一个元素:" << ms.begin().operator*() <<endl;
    cout << "最后一个元素:"<< ms.end().operator*() <<endl;
    cout << boolalpha<<  "容器是否为空:" << ms.empty() <<endl;
    cout << "迭代器方式-所有元素:";
    for(auto i=ms.begin(); i != ms.end(); i++){
        cout << *i << "\t";
    }
    cout << endl;
    return 0;

}
/* 关联容器
 * 1.关联式容器在存储元素,默认会根据键值的大小做升序排序
 * 2.通常可直接通过键 获取 值
 * 3. map multimap
 * 4. set multiset 默认k=v 所有相当于直接存储v 按照v升序排序
 * 5. 排序:set<int, less<int>> 默认升序  set<int, greater<int>> 降序
 * map:key值必须唯一
 * multimap:可存储k值相同的多个键值对
 * set: 键与值相同 且不重复   键值 均不能修改
 * multiset: 键与值相同 可以重复  键值 均不能修改
 */


15.3 无序容器(hash容器)

hash容器(c++>11支持):类比关联容器 存储数据键值形式<k:value>(hash表存储结构) 但是不会进行存储数据的升序排序 即数据是无序的
容器 特征
unordered_map pair<k,v>形式数据存储 k值不可重复 键值对无序
unordered_multimap 可存储多个相同的k值的键值对
unordered_set 元素唯一(k=v) 存储元素无序
unordered_multiset 可存储相同元素(k=v) 存储元素无序
#include <iostream>
#include <unordered_map>
#include <string>
#include <unordered_set>

using namespace std;

int main(){
    //无序容器
    //1.unordered_map
    cout << "--------------unordered_map---------------"<<endl;
    unordered_map<int, string> uom;
    //增
    uom.insert({3, "cc"});
    uom.insert({1, "aa"});
    uom.insert({2, "bb"});
    //删
    //uom.erase(1);
    // 改
    uom.at(1) = "aaa";
    // 查
    cout<<"元素总数:" << uom.size() <<endl;
    cout<< "所有元素:"<<endl;
    for(pair<int, string> p: uom){
        cout << p.first << ":" << p.second<<"\t";
    }
    cout << endl;

    //2.unordered_set
    cout << "--------------unordered_set---------------"<<endl;
    unordered_set<int> uos;
    //增
    uos.insert(1);
    uos.insert({3,3, 1, 4, 0, 9,5});
    // 删
    uos.erase(1);
    // 查
    cout << "元素总数:" << uos.size() <<endl;
    cout << "迭代器方式-所有元素:"<<endl;
    for(auto iter= uos.begin(); iter != uos.end(); iter ++){
        cout << *iter << "\t";
    }
    cout << endl;

}


15.4 迭代器

迭代器多用于容器遍历元素 实则是对指针的操作
begin() end() 返回一个迭代器对象
begin().base()  返回一个指向容器第一个元素的指针
*(容器.begin().base())  解引用取值
#include <iostream>
#include <vector>

using namespace std;

int main(){
    //迭代器
    vector<int> s{1, 2, 3, 4};

    auto begin = s.begin();
    auto end = s.end();
    cout << "begin=" << *begin <<endl;
    cout << "解引用begin=" << *begin.base() <<endl;
    cout << "end=" << *(end-1) <<endl;
    cout << "解引用end=" << *(end.base()-1) <<endl;

    // 迭代器-遍历容器
    for(auto i = s.begin(); i < s.end(); ++i ){
        cout << *i << "\t";
    }
    cout<<endl;
}
/* begin() 返回一个(指向容器第一个元素)迭代器对象 
 * begin().base() 返回指向容器的第一个元素的指针
 * *s.begin(): *重载 解引用获取第一个元素指针所指向的值
 */


16 make

编译过程
    源文件(.cpp) ---[1预编译]-->预编译(c/c++)文件(.i)---[2编译优化]-->编译(汇编语言)文件(.s)---[3汇编]-->二进制文件(.o)---[4链接]-->可执行(二进制)文件(.exe)

 源代码(source coprede)→预处理器(processor)→编译器(compiler)→汇编程序(assembler)→目标程序(object code)→链接器(Linker)→可执行程序(executables)  

    
1.预编译处理
    g++ -std=c++11 -E 源文件 (生成.i预处理文件)
    
2.编译优化
    g++ -std=c++11 -S 源文件 (生成.s汇编文件)
    
3.汇编
    g++ -std=c++11 -c 源文件 (生成.o二进制文件)
    
4.链接
    通过连接器将多个目标(链接库.o文件)文件链接在一起生成一个可执行程序(.exe)
    通常头文件放置在include文件 动态链接库多为跟源文件相关联的cpp文件打包
    动态链接库:g++ -fPIC -shared xx.cpp -o xx.dll (windows)
    静态链接库:ar -crv libtest.a  xx.o (linux)  生成静态链接库libtest.a


16.1 make编译

make 是一条计算机指令 可以从makefile(Makefile)文件中获取构建程序所依赖的关系
windows-mingw: mingw32-make -f 指定makefile文件 目标(或省略)
linux: make -f 指定makefile文件 目标(或省略)

16.2 makefile编译

# makefile格式
# target: 编译源文件
# tap键  命令(g++ -std=c++11 编译源文件)
#------------------------------------------------------->
# 针对一个源文件进行编译
# test: a.cpp
# 	g++ -std=c++11 a.cpp  -o test  # 生成test.exe可执行文件
#------------------------------------------------------->
# 针对多个源文件一起进行编译
#include  xx.make $(aaa)  # include 可以添加引用其他makefile文件 或者是变量aaa
target1: a.o b.o  # 编译目标  
	# -o 指定可执行文件名
	g++  -o test a.o b.o  

# 可省略command:g++ -std:c++11 -c a.cpp
a.o: a.cpp  
b.o: b.cpp 

# 伪目标 不要放置在开头 用于删除编译过程产生的文件  使用make clean指令删除||mingw32-make clean
.PHONY:clean  
clean:
	del a.o b.o  # windows  可以使用通配符 del *.o   *:任意字符出现次数>=0 || ?:字符出现次数==1 || [...]: 匹配特定字符
	#rm a.o b.o  # linux
	
	
	
# target 编译目标
# clean 删除编译过程产生的编译文件
# terminal执行指令
# $make target(指定编译目标) (linux)  # -f 指定makefile文件
# $mingw32-make target  (windows)

16.3 makefile实例

target: a.o b.o
	g++ -o test a.o b.o

a.o: a.cpp
b.o: b.cpp

.PHONY:clean
clean:
	del *.o
//----a.h
#ifndef CODE_A_H
#define CODE_A_H
#include <iostream>
#include <string>
using namespace std;

class animal{
public:
	string name;
	animal(string name):name(name){}
	void eat();
};
#endif //CODE_A_H


//----b.cpp
#include <iostream>
#include "a.h"

using namespace std;


void animal::eat() {
    cout << name << "正在进食"<<endl;
}


//----a.cpp
#include <iostream>
#include "a.h"

using namespace std;

int main(){
	cout << "中文编码测试"<< endl;
	cout << "c++ test..."<<endl;
	cout << "---------------"<<endl;
	// 多个源文件进行编译
	animal dog("狗狗");
	dog.eat();
	return 0;
}




17 cmake

17.1 cmake简介

cmake就是一个具有自己编译规则的编译工具 针对的是不同的编译器 使编译过程规范化
Clion: cmake-cmakelist.txt
qmake: xx.pro
#-----CmakeList.txt
# cmake的最低版本
cmake_minimum_required(VERSION 3.16)

# 编译的项目
project(code)

# 当前编译使用c++14版本标准来编译程序
set(CMAKE_CXX_STANDARD 14)

# 项目的执行程序 括号中的test 是最终生成的可执行程序名称  后面的是程序的源码文件
add_executable(test main.cpp stu.cpp)

17.2 cmake使用

#----主项目工程cmakelist.txt
cmake_minimum_required(VERSION 3.16)
project(code)
set(CMAKE_CXX_STANDARD 14)

# 主项目工程加载项目工程
add_subdirectory(test)
#----子项目工程cmakelist.txt
add_executable(test a.cpp  b.cpp)

set(A 10)  # 设置变量
# 打印输出
message("a=${A}")  # 输出信息 a =10
set(A ${A} 20)
message("a=${A}")  # 输出信息 a=10;20

17.3 cmake实例

//----a.h
#ifndef CODE_A_H
#define CODE_A_H
#include <iostream>
#include <string>
using namespace std;

class animal{
public:
	string name;
	animal(string name):name(name){}
	void eat();
};
#endif //CODE_A_H


//----b.cpp
#include <iostream>
#include "a.h"

using namespace std;


void animal::eat() {
    cout << name << "正在进食"<<endl;
}


//----a.cpp
#include <iostream>
#include "a.h"

using namespace std;

int main(){
	cout << "中文编码测试"<< endl;
	cout << "c++ test..."<<endl;
	cout << "---------------"<<endl;
	// 多个源文件进行编译
	animal dog("狗狗");
	dog.eat();
	return 0;
}

# 通常链接库是关联源代码cpp文件 的其他cpp文件 
# 通常链接库放置在文件夹lib  头文件放置在include
# b.cpp 为关联cpp文件(可以多个)  a.cpp为程序入口源文件  a.h为头文件

# 动态链接库
# 编译过程不会被链接到代码 在可执行文件运行时载入 可共享相同的链接库 只需要一份该库实例  实现进程中的资源共享.so文件 且程序升级较容易 只需要重新编译.so文件即可 但是可移植性差 因为环境不同 动态库存放位置不一样 导致程序运行失败

# linux动态链接库文件: xx.so + xx.h
# Windows动态链接库: .lib引入库文件(提供dll位置 编译期间加载)  .dll动态库文件(提供程序所需数据等 程序运行)  .h 头文件

g++ -fPIC -shared b.cpp -o b.dll(windows)  
g++ -fPIC -shared b.cpp -o b.so(linux)

编译:g++ -o test a.cpp -L 动态链接库目录(-l 动态链接库文件名)

# 静态链接库(一组.o文件的集合)
# 静态库链接.a文件 打包到.exe文件 且生成的可执行文件不依赖静态链接库 装载速度块 但是浪费空间和资源
g++ -c b.cpp 生成b.o文件
ar -crv libtest.a b.o(linux)

编译:g++ -o test a.cpp -L 指定静态库的搜索路径(-l 不需要lib前缀和.a后缀)
    
# g++(gcc) 编译参数
    -shared :指定生成动态链接库
    -static :指定生成静态链接库
    -std: 指定c++标准
	-fPIC :表示编译为位置独立的代码 用于编译共享库 它们可以放在可执行程序的内存里的任何地方
	-L :连接的库所在的目录
	-l:指定链接时需要的动态库 编译器查找动态连接库时有隐含的命名规则 即在给出的名字前面加上lib 后面加上.a/.so来确定库的名称
	-Wall :生成所有警告信息
	-ggdb :此选项将尽可能的生成gdb的可以使用的调试信息
	-g :编译器在编译的时候产生调试信息
	-c :只激活预处理 编译和汇编 也就是把程序做成目标文件(.o文件)
	-Wl,options :把参数(options)传递给链接器ld。如果options中间有逗号 就将options分成多个选项 然后传递给链接程序

17.4导入动态链接库编译

CLION编译器
#----主工程项目code/cmakelist.txt
cmake_minimum_required(VERSION 3.16)
project(code)
set(CMAKE_CXX_STANDARD 14)
# 加载子工程项目cmakelist.txt
add_subdirectory(test1)

#----子工程项目test/camkelist.txt
# 导入头文件
include_directories("C:/Users/FSH/Desktop/test/include")

# 添加执行程序
add_executable(test1 a.cpp)

# 导入依赖库
# 方式一(外部记得配置添加环境变量path=C:/Users/FSH/Desktop/test/lib)
#target_link_libraries(test1 C:/Users/FSH/Desktop/test/lib/b.dll)
# 方式二
find_library(lib_b_dir b C:/Users/FSH/Desktop/test/lib)  # lib_b_dir任意变量名  b:为.dll前的名
message("bb=${lib_b_dir}")
target_link_libraries(test1 ${lib_b_dir})


18 帮助文档:

[1] STL http://c.biancheng.net/view/6967.html

[2] c++ https://www.runoob.com/cplusplus/cpp-tutorial.htm

[3] makefile https://blog.csdn.net/bsp_mpu6050/article/details/107921532?utm_medium=distribute.pc_relevant.none-task-blog-title-6&spm=1001.2101.3001.4242

posted @ 2020-11-13 11:16  爱编程_喵  阅读(209)  评论(0)    收藏  举报
jQuery火箭图标返回顶部代码

jQuery火箭图标返回顶部代码

滚动滑动条后,查看右下角查看效果。很炫哦!!

适用浏览器:IE8、360、FireFox、Chrome、Safari、Opera、傲游、搜狗、世界之窗.