c++学习笔记 (不定期更新ing)

2024-04-09 17:48

本文主要是介绍c++学习笔记 (不定期更新ing),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • c++
    • 第一章 绪论
      • 1.1 计算机程序设计语言的发展
        • 1.1.1机器语言与汇编语言与高级语言
        • 1.1.2面向对象的语言
      • 1.2 面向对象的方法
        • 1.2.1由来:面向过程的程序设计方法
        • 1.2.2面向对象的方法
        • 1.2.3 基本概念——对象、类
      • 1.3 面向对象(Object Oriented)的软件开发
        • 1.3.1 分析 OOA
        • 1.3.2 设计 OOD
        • 1.3.3 编程 OOP
        • 1.3.4 测试 OOT
        • 1.3.5 维护 OOSM
      • 1.4 信息的表示与存储
        • 1.4.1 计算机数字系统、R进制与进制转换
        • 1.4.2 信息的存储单位
        • 1.4.3 原码,反码,补码
        • 1.4.4 定点数与浮点数(阶码与尾数),以及数的表示范围
        • 1.4.5 非数值信息的表示
      • 1.5 程序的开发过程
        • 1.5.1源程序(.cpp)与目标程序(.o)
        • 1.5.2 程序开发流程
      • 1.6 小结
    • 第二章 C++补充内容
      • 2.1逗号运算符
      • 2.2条件运算符
      • 2.3位运算
      • 2.4枚举类型
      • 2.5内联函数
      • 2.6带默认参数值的函数
      • 2.7函数重载
    • 第三章 类(class)和对象(object)
      • 3.1类的基本概念
      • 3.2成员函数
        • 3.21用指针来调用成员函数(本质)
        • 3.22**用引用传递来访问成员函数**
      • 3.3保护成员
      • 3.4构造函数和析构函数
        • 3.41 构造函数constructor
        • 3.42 析构函数
      • 3.5类的组合
      • 3.6UML
      • 3.7综合实例
      • 3.8讨论

c++

本笔记基于学校课件,附带一点笔者思维习惯,旨在梳理c++的基本知识和语法,欢迎大家阅读与指正。

* @author: SN
* @date :2021/11/20
* @version 0.3

第一章 绪论

1.1 计算机程序设计语言的发展

1.1.1机器语言与汇编语言与高级语言

0101 0110

add r1 r2 r3

printf(“hello”);

1.1.2面向对象的语言

特点

  • 是高级语言。
  • 将客观事物看作具有属性和行为的对象。
  • 通过抽象找出同一类对象的共同属性行为,形成
  • 通过类的继承与多态实现代码重用
  • 优点:符合认识对象,到对象的行为属性的认知顺序

1.2 面向对象的方法

1.2.1由来:面向过程的程序设计方法

由main函数开始的一种树状结构(顺序,选择与循环)

  • 程序 = 数据结构 + 算法 = 变量 + 函数

  • 优点:自顶向下,逐层分解为易于控制和处理的独立子任务

  • 缺点:可重用性差,数据安全性差,难以开发大型 软件和图形界面的应用软件

    • 这是因为数据和处理数据的过程(函数)是分离的相互独立的实体,数据结构改变,函数被迫改变。也就是说,面对旧的问题,采用新的方法会有巨大开销。

    • 图形用户界面难以用过程函数实现

1.2.2面向对象的方法

将数据及对数据的操作方法封装在一起,作为一个 相互依存、不可分离的整体——对象

同类型对象抽象出其共性,形成

  • 通信:类通过一个简单的外部接口,与外界发生关系。 对象与对象之间通过消息进行通信
  • 抽象:将一类事物的共同属性(数据结构)和行为(函数)封装起来:class = data + function
  • 程序模式:main函数下只有class间的通信 ,没有割裂的函数与数据
  • 优点:模块关系简单独立,数据更安全。继承与多态便于重用。
  • 对象 = 数据结构 + 算法 程序 = (对象 + 对象 +……+对象)+ 消息
1.2.3 基本概念——对象、类

对象

  • 一般对象 = 静态特征(数据与描述) + 动态特征(行为与功能)
  • 面向对象方法中的对象 = 一组属性 + 一组行为

  • 一般类 = 忽略非考虑特征后,一系列具有共性的对象的抽象
  • 面向对象方法中的“类”:具有相同属性和服务的一组对象的集合
    • 为属于该类的全部对象提供了抽象的描述,包括属性和行为两个主要部分。
    • 类是对象的抽象,对象是类的实例。(钱范是类,钱是对象)
    • 封装技术:屏蔽掉对象的内部细节,开放有限的接口与外界交互.
    • 继承技术:特殊类的对象拥有其一般类的全部属性与服 务,称作特殊类对一般类的继承。
      • 有序序列是一般类的话,数组,字符串,队列是特殊类
      • 有助于软件复用!提高开发效率
    • 多态性:指在一般类中定义的属性或行为,被特殊类继承之后,可以具有不同的数据类型或表现出不同的行为。这使得同一个属性或行为在一般类及其各个特殊类中具有不同的语义。
      • 序列的排序可以被继承为字符排序,数的排序,字符串排序

1.3 面向对象(Object Oriented)的软件开发

面向对象的软件工程

面向对象的软件工程是面向对象方法在软件工程领域的全面应用。它包括:

  • 面向对象的分析(OOA)
  • 面向对象的设计(OOD)
  • 面向对象的编程(OOP)
  • 面向对象的测试(OOT)
  • 面向对象的软件维护(OOSM)
1.3.1 分析 OOA

本阶段要求精确扼要地抽象出系统的必要任务,不必关心实现。

  • 列出对象,归纳类
  • 寻找交互关系
1.3.2 设计 OOD
  • 首先是进行类的设计,类的设计可能包含多个层次(利用继承和派生机制)。
  • 以类为基础提出程序设计的思路和方法,包括了算法的设计
  • 在此设计阶段,并不牵涉某一具体的计算机语言。
1.3.3 编程 OOP
  • 本笔记采用C++语言
1.3.4 测试 OOT
  • 以对象的类作为基本测试单位
1.3.5 维护 OOSM
  • 使用面向对象的方法开发的软件,其程序与问题域是一致的,因此,在维护阶段运用面向对象的方法可以大大提高软件维护的效率

1.4 信息的表示与存储

信息

控制信息

  • 指令
  • 控制字(非打印字符,比如换行)

数据信息

  • 数值信息
    • 定点数
    • 浮点数
  • 非数值信息
    • 字符数据
    • 逻辑数据
1.4.1 计算机数字系统、R进制与进制转换

1.4.2 信息的存储单位
  • bit, b 一个二进制位
  • byte , B (1B = 8bits )字节,是计算机操作的单位
    • 千字节 1 KB = 1024 B
    • 兆字节 1 MB = 1024 KB
    • 吉字节 1 GB = 1024 MB
  • 机器字长:一般指参加运算的寄存器所含有的二进制数的位数,代表了机器的精度,如32位、64位等。在内存中也就相应可能有264(64位)种不同的值,地址长度也是64位。
1.4.3 原码,反码,补码
1.4.4 定点数与浮点数(阶码与尾数),以及数的表示范围
1.4.5 非数值信息的表示

外文

  • ASKII码:用一个非负的byte表示一个字符(只有128个字符)
    • {[0,31],{127}}表示33个控制字符(不可显示)
      • \10 换行
      • \128 删除
    • [32,126]表示95个可显示字符
      • \65 “A”
      • \96 “a”
      • \57 “9” //字符“9”和数字9 的机器码是不一样的,强制类型转换是不会修改机器码的
  • EBCDIC:字节最高位也参与,最多可以表示256个字符

汉字

  • 国标码(GB2312-80标准):是二字节码,用两个非负字节编码表示一个汉字(只使用了6763个汉字,当然其他的可以表示数字与符号)。
    • GB2312-80 GB2312将代码表分为94个区,对应第一字节;每个区94个位,对应第二字节,**两个字节的值分别为区号值和位号值加32(20H)。**01-09区为符号、数字区,16-87区为汉字区,10-15区、88-94区是有待进一步标准化的空白区。GB2312将收录的汉字分成两级:第一级是常用汉字计 3755个,置于16-55区,按汉语拼音字母/笔形顺序排列;第二级汉字是次常用汉字计3008个,置于56-87区,按部首/笔画顺序排列。故而 GB2312最多能表示6763个汉字。

1.5 程序的开发过程

1.5.1源程序(.cpp)与目标程序(.o)
  • 汇编语言源程序(.s)翻译成目标程序。
  • 一般高级语言编译成目标程序。
  • python源程序只能解释,解释一句执行一句,不会生成目标程序。
1.5.2 程序开发流程
  • 编辑(得到.cpp源文件)
  • 编译(经过.s文件,翻译得到.o文件)
  • 链接(与其他.o文件和库文件连接为 .exe文件)
  • 运行调试

1.6 小结

  • 语言是具有一套语法、词法规则的系统。计算机程序设计语言是计算机可以识别的语言,用于描述解决问题的方法,供计算机阅读和执行。
  • 计算机语言可以分为:机器语言、汇编语言、高级语言和面向对象的语言。
  • 面向对象的软件工程是面向对象方法在软件工程领域的全面应用。
  • 计算机加工的对象是数据信息,而指挥计算机操作的是控制信息。所有的信息在计算机内部都是用二进制数表示的,具体的表示方式根据信息的类型有所不同。

第二章 C++补充内容

2.1逗号运算符

格式:表达式1,表达式2

效果:先求解1,再求解2,最终结果为表达式2的值(一般需要小括号括起来)

m = (1+2 , 3*4)
结果是m = 12

2.2条件运算符

格式:表达式1?表达式2:表达式3

效果:表达式1真,值为表达式2,假为表达式3

x = 3>2 ? 3 : 2;
结果 x=3;

注意:a ? b : c ? d : e <=>a ? b : (c ? d : e) 右边优先

2.3位运算

& 按位与
作用:用0来&将某些位置0,保留剩下位数
char c; int a; 
c=a & 0xff;| 按位或
作用:用1来|将某些位置1,保留剩下位数
a = a | 0xff;//低字节置1^ 按位异或
作用:用1来^可以翻转bit~ 按位取反(这是一个单目运算符)>> << 移位(有符号数右移高位补符号位)

2.4枚举类型

对枚举元素按常量处理,不能对它们赋值。例如,

  • 不能写:TRUE = 1;

枚举元素具有默认值,它们依次为: 0,1,2,…也可以在声明时另行指定枚举元素的值,如:

  • enum Weekday{SUN=7,MON=1,TUE,WED,THU,FRI,SAT};

枚举值可以进行关系运算。

整数值不能直接赋给枚举变量,如需要将整数赋 值给枚举变量,应进行强制类型转换。

作用:结果只有四种可能,可以声明一个枚举类型,声明一个枚举类 型的变量来存放可能结果。

2.5内联函数

声明时使用:inline int func();

目的:减少程序的执行时间

对于一些功能简单、规模较小又频繁使用的函数,可以设计为内联函数。

注:内联函数体内不能有循环语句和switch语句

2.6带默认参数值的函数

在函数声明时可以给缺省参数值。

int add(int x = 5,int y = 6) { return x + y; 
}
int main() { add(10); //10+6 add(); //5+6 
}

注意:

  • 所有默认参数靠右。例子中x有默认值,y没有是不行的,反之则可行。
  • 默认参数只能指定一次。也就是说在声明写默认参数,在定义时不能写了。

2.7函数重载

C++允许功能相近的函数在相同的作用域内以相同 函数名声明,从而形成重载。方便使用,便于记忆

也就是作用域相同,函数名相同,参数不同

int add(int x, int y); 
float add(float x, float y); //形参类型不同 
int add(int x, int y); 
int add(int x, int y, int z);//形参个数不同
//第一种不区分返回值的原因是重载函数的本质要求是形参不同(参数名不同不算)

重载的基础一定是有相同的功能。

第三章 类(class)和对象(object)

3.1类的基本概念

类:C++封装的基本单元,将数据和函数封装在一起

  • 它为属于该类的全部对象提供了统一的抽象描述,其内部包括属性和行为两个主要部分。

    • 可与struct 进行对比.struct 和 class都是类型,struct中只有数据,class里面除了数据还有方法。
  • 利用类可以实现数据的封装、隐藏、继承与派生。

  • 利用类易于编写大型复杂程序,其模块化程度比C中采用函数更高。

//一种用户自定义类型,声明形式:
class 类名称{public://(外部接口,以被任意实体访问)private://(只允许本类的成员函数访问)protected://(只允许子类及本类的成员函数访问)
};//例子
class  Person {public: void eat(int food, int drink);//成员函数void sleep(int time);//成员函数private: int height,weight,age;//成员数据
};
  • 结构

    class 类名 {}

  • 类名

通常首字母大写,名词 :Person

  • 成员函数

    • 同一般函数的声明
  • 成员数据

    • 同一般变量的声明
  • 存储控制

    • public:公有成员,外部接口,可以被任意实体访问,任何外部函数都可以访问公有类型数据和函数。
    • private:私有成员,只允许本类成员函数访问(如果紧跟在类名称的后面声明私有成员,则关键字private可以省略。)
    • protected:保护型成员,只允许子类及本类的成员函数访问

对象:就是类型的实例化

  • 类名 对象名;

    • 例:Person Lee;
  • 类中成员互访

    • 直接使用成员名
  • 类外访问

    • 使用==“对象名.成员名”==方式访问 public 属性的成员

3.2成员函数

  • 在类中说明原型,可以在类外给出函数体实现,并在函数名前使用==类名 : :==加以限定。
    • 将类的定义和成员函数定义分开,是C++程序开发的惯常做法。通常,类的定义在头文件中,因此通常将函数声明放在头文件中,而将函数的定义放在其他文件中。以避免函数的定义被编译多次
    • 可以将类的定义看作是类的外部接口,而成员函数的定义看作是类的内部实现,此时成员函数前面需要加类名 和 ::
  • 可以直接在类中给出函数体,形成内联成员函数(所以不能使用switch,while之类)。(不推荐也不举例)
#include<iostream>
using namespace std;
class Clock{
public:		//void Clock();(想想这是什么)void setTime(int newH = 0, int newM = 0, int newS = 0);//可以有默认参数void showTime();
private:	int hour, minute, second;
}
int main() {Clock myClock;myClock.setTime(8, 30, 30);//调用成员函数必须指明具体对象和成员名,调用myClock对象的showTime()myClock.showTime();return 0;
}
void Clock::setTime(int newH, int newM, int newS) {hour = newH;minute = newM;second = newS;
}
void Clock::showTime() {cout << hour << ":" << minute << ":" << second;
}
//结果  8:30:30
  • 函数showTime() 全名是 Clock::showTime()
  • 类名的用处是指出showTime( ) 是类 Clock的一个成员函数
  • 没有类名的函数称为非成员函数
  • :: 叫作用域区分符(注意成员和局部变量的区别
3.21用指针来调用成员函数(本质)
  • #include "Clock.h"
    void func(Clock * ps){ps ->showTime(); 	 
    }void main(){Clock myClock; //声明一个对象,属于Clock类func(&myClock);//将对象的地址传递给函数的参数
    }
    
  • 隐含的形参,this指针

假设一个Clock类的对象c,在执行c.showTime()时,成员函数实际也接收到了一个对象c的地址,并将这个地址赋值给隐含的形参this。所有对成员的访问都隐含地加上了前缀this->(对象的地址是一个核心参数)

class Clock{
public:		void showTime() { cout << hour << ":" << minute << ":" << second; }
//等价于:cout << this->hour << ":" << this->minute << ":" << this->second;
private:	......
}
3.22用引用传递来访问成员函数

采用对象的引用来调用成员函数,形式和使用对象来调用一致。

#include "Clock.h"
void func(Clock& refsClock){refsClock.showTime(); 	 
}void main(){Clock myClock; //声明一个对象,属于Clock类func(myClock);//将对象的地址传递给函数的参数
}

引用(reference)

  • int &y = x;

    • 某个内存的别名(理解这两个字就行了)

    • reference不需要dereference即可直接获取到指向的内存空间的值。例如上例中,直接y就可以获取reference y所指向的内存空间的值,而不需要*y来获取。

    • reference的赋值操作也不需要取地址符来赋值,可以直接通过变量名,例如上例中,int &y = x, 而不需要 int &y = &x;

reference 在声明的时候就必须要有初始值,而且reference变量指向的内存地址是不能变化,不像pointer那样可以很灵活的重新指向其他地址。

3.3保护成员

使得应用程序更加可靠和易于维护

保护类的内部数据不被随意修改,限制类与外部世界的接口

减少类与其他代码的关联程度

当类的内部实现修改时,只要接口不变,外部调用它的程序不用改动

eg:将hour设置为受保护的成员,外界对hour的修改只能通过setHour函数(接口)来进行。避免了错误的赋值操作。

3.4构造函数和析构函数

3.41 构造函数constructor

构造函数(干脆叫初始化函数

  • 对象创建时自动调用的类的成员函数
  • 构造函数名与类名相同,且没有返回值;通常为公有函数。
  • 如果程序中未声明,则系统自动产生出一个默认构造函数,其参数列表为空
构造函数的实现:
Clock::Clock(int newH, int newM, int newS) {hour = newH;minute = newM;second = newS;
}
建立对象时构造函数的作用:
int main() {Clock c(0,0,0); //此处将自动调用构造函数,对象的声明也是调用成员函数c.showTime();return 0;
}
  • 主要作用:利用一个公有成员函数去初始化可能的私有成员数据

  • 下面两个都是默认构造函数(空参数或者全都有缺省),如果在类中同时出现,将产生编译错误:

Clock();
Clock(int newH=0,int newM=0,int newS=0);
//构造函数可以重载,C++根据声明中的参数自动选择合适的构造函数,但是这两种不算
  • 由于初始化比较简单,可以内联(例子是重载)。
Class Clock{
public:Clock(){};Clock(int newH){};//现场定义,视为内联//其他公有成员......
private:	int Hour//私有成员
};
void func(){Clock c1;Clock c2(10);
}
  • 复制构造函数

复制构造函数是一种特殊的构造函数,其形参为本类的对象引用。作用是用一个已存在的对象去初始化同类型的新对象。

class Point {   //Point 类的定义
public:Point(int xx=0, int yy=0) { x = xx; y = yy; }  //构造函数,内联Point(const Point& p); //复制构造函数void setX(int xx) {x=xx;}void setY(int yy) {y=yy;}int getX() const { return x; } //常函数(啥是常函数呢?)int getY() const { return y; } //常函数
private:int x, y; //私有数据
};
//成员函数的实现
Point::Point (const Point& p) {x = p.x;y = p.y;cout << "Calling the copy constructor " << endl;
}
  • 复制构造函数被调用的三种情况

    1. 定义一个对象时,用另一个对象来赋值,发生复制构造;

    2. 如果函数的形参是类的对象,调用函数时,实参会赋值给形参,发生复制构造;

    3. 如果函数的返回值是类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化一个临时无名对象,传递给主调函数,此时发生复制构造。

//形参为Point类对象的函数
void fun1(Point p) {cout << p.getX() << endl;
}
//返回值为Point类对象的函数
Point fun2() {Point a(1, 2);return a;
}//主程序
int main() {Point a(4, 5);	//第一个对象aPoint b = a;	//情况一,用a初始化b。第一次调用复制构造函数cout << b.getX() << endl;fun1(b);	//情况二,对象b作为fun1的实参。第二次调用复制构造函数b = fun2();	//情况三,函数的返回值是类对象,函数返回时调用复制构造函数cout << b.getX() << endl;return 0;
}
  • 隐含的复制构造函数
    • 如果程序员没有为类声明复制构造函数,则编译器自己生成一个隐含的复制构造函数。这个构造函数执行的功能是:用作为初始值的对象的每个数据成员的值,初始化将要建立的对象的对应数据成员。
    • 显然这是粗粒度的克隆。
3.42 析构函数
  • 完成对象被删除前的一些清理工作。
  • 在对象的生存期结束的时刻系统自动调用它,然后再释放此对象所属的空间。
  • 如果程序中未声明析构函数,编译器将自动产生一个隐含的析构函数。
Point::~Point() {};

3.5类的组合

3.6UML

3.7综合实例

3.8讨论

在这里插入代码片

这篇关于c++学习笔记 (不定期更新ing)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/888820

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�