[C++入门课程]楚栋浩的C++入门课程

前言

自我介绍

2015年开始学习和使用C++

课程形式

  1. b站直播(一对一辅导):哔哩哔哩直播
  2. 将录像剪辑为课程上传b站:C++入门-直播录像(未剪辑)
  3. 编写配套的CSDN博客
  4. 实践代码:chudonghao/cpp-tutorial

课程思路

  1. 思想为纲,尝试理解C/C++,而不是学习C/C++
  2. 兴趣为先,尽量做有趣的事
  3. 实践为主,从实践中学习

入门内容大概

  1. 编译过程:源代码 编译器 构建工具 IDE main
  2. 从思想出发
    1. 数据结构与算法
  3. C++如何表示数据结构
    1. 基本数据类型
    2. 内存 变量 结构体
  4. C++如果表示算法
    1. 表达式
    2. 函数
      1. 返回值
      2. 名称
      3. 参数
      4. 函数体
      5. 运算符
        1. 运算符重载
  5. C++如何组织语言
    1. 预处理
    2. 头文件引入
    3. 声明与定义
    4. 命名空间
    5. 指针与引用
      1. 函数的值传递
  6. 其它基础知识
    1. 基本输入输出

课程记录

0. 工作与课程安排

C/C++工作方向:

  1. 机器视觉
  2. 嵌入式
  3. Qt 军工(外包) 医疗影像
  4. 大厂(后端) 大厂(外包)
  5. 音视频
  6. 游戏

从发展上看,工作稳定性从高到低:

  1. 大厂(后端)、游戏、音视频
  2. 医疗影像、机器视觉、嵌入式
  3. Qt、外包

课程时间安排:
1-3个月(40-120小时)

0. 开发环境

安装操作系统

Ununtu官网教程:Install Ubuntu desktop | Ubuntu

参考Ununtu文档安装即可。

如果你用虚拟机,则大致过程如下:

  1. 下载Ubuntu镜像(iso文件)
  2. 下载并安装VirtualBox/vmware
  3. 通过VirtualBox创建虚拟机,大致配置:
    1. 显存调最大
    2. 内存调当前系统的一半
    3. CPU调当前系统的一半
    4. 立即创建虚拟磁盘
  4. 启动虚拟机并选择下载的iso文件启动
  5. 按照指引安装Ubuntu;大致过程为:选择语言为中文、选择清除整个磁盘(准备的空磁盘)并安装、时区选择上海、输入计算机名、输入用户名和密码
  6. 安装成功后重启虚拟机即可

如果你现在使用的是Windows系统,目标是安装双系统,则大致的过程如下(不使用虚拟机,后面有虚拟机大致安装过程):

  1. 准备一个U盘
  2. 准备一个空的硬盘并安装
  3. 下载Ubuntu镜像(iso文件)
  4. 下载U盘启动器制作工具UltraISO或者balenaEtcher
  5. 制作U盘启动器
    如果是使用UltraISO,则:
    1. 首先打开ISO文件,然后点击菜单栏《启动》->《写入磁盘镜像》
    2. 选择要安装的U盘
    3. 点击写入
  6. 重启电脑
  7. 按F2/DELETE按键进入BISO,找到启动相关的内容,将U盘作为第一启动项,保存并推出
  8. 按照指引安装Ubuntu;大致过程为:选择语言为中文、选择清除整个磁盘(选择准备的硬盘)并安装、时区选择上海、输入计算机名、输入用户名和密码
  9. 安装成功后重启电脑即可

自己熟悉和使用一段时间Ubuntu,刚开始可能不太适应。

适应之后会喜欢Linux操作系统,并且会觉得Windows系统是真的差劲。

常用的操作:

  1. windows 键预览窗口或打开应用
  2. ctrl+alt+t 打开控制台

常用的应用:

  1. gedit 文本编辑器
  2. gnome-terminal 控制台(输入和执行命令)
  3. firefox 浏览器

常用的命令:

  1. cd 切换工作目录
安装编译器-g++

通过控制台执行如下指令(过程中需要输入密码,输入密码时不会显示字符,莫慌):

sudo apt install g++

也可安装编译器clang:

sudo apt install clang
安装IDE-CLion
  1. 通过控制台安装依赖:
sudo apt install libfuse2
  1. 下载JetBrains Toolbox App,解压后启动
  2. 通过JetBrains Toolbox App安装CLion
  3. 启动CLion

激活CLion可以通过JetBrains免费教育许可证JetBrains在线商店购买许可证进行。

CLion已经内置CMake和gdb

0. 编译过程、编译器、构建工具、IDE

开发环境准备完毕后,我们要清楚开发过程及各个程序的作用和关系。

各个软件的定位
  • CLion IDE
  • cmake 构建工具
  • gdb/lldb 调试器
  • g++/clang 编译器
不同软件的作用

编译器(g++/clang) 用于完成编译过程:

  1. 预处理 main.cpp(源代码) -> main.cpp.p(预处理结果)
  2. 编译 main.cpp.p(预处理结果) -> main.cpp.s(汇编代码)
  3. 汇编 main.cpp.s(汇编代码) -> main.cpp.o(机器码)
  4. 链接 main.cpp.o(机器码)+系统的库(已经编译好的机器码) -> main(可执行程序)

构建工具(cmake) 用于:

  1. 组织你的项目/源码
  2. 创建编译指令
  3. 最终通过编译器生成可执行程序

IDE(CLion/VS/vscode/qtcreator)(集成开发环境) 用于组织开发环境,完成对下列工具的调用:

  1. 编辑器(编辑源代码)
  2. 代码补全工具(帮助编辑源代码)
  3. 构建工具
  4. 编译器
  5. 调试器

1. 课程思路

  1. 思想为纲,尝试理解C/C++,而不是学习C/C++
  2. 兴趣为先,尽量做有趣的事
  3. 实践为主,从实践中学习

比如:

思想1:
数据结构与算法:对现实世界的抽象/对现实世界进行建模
面向过程:1 2 3 4,就是描述一个过程
面向对象:一个过程伴随一个状态

思想2:
C++是一门语言!语言!
不论是汉语、英语、C++,语言是用来表达思想的!它是用来描述人的想法的!
我们要把C++当成语言来学,就像学习英语一样!

有些人可能会把C++当作工具来学习和使用,这完全忽视了语言的含义!

2. 数据结构与算法

2.1 基本数据类型与结构体

基础知识:
计算机的基本结构:

  1. CPU
  2. 总线
  3. 内存

计算机内存如何表示数据:

  1. 二进制 0 1
  2. 8个0/1组成一个字节(后续我们用格子来描述)
  3. 一个格子能代表256个数字(256种状态)

计算机如何表示算法:

  1. 首先程序是存在内存里的,用数字表示的行为或指令
  2. 用特定数字代表特定行为,比如
    1. 加减乘除
    2. 内存拷贝
  3. 若干指令组合成为算法

程序:
指令操控数据

C++基本数据类型:

  1. bool
  2. char/unsigned char
  3. short/unsigned short
  4. int/unsigned int
  5. long/unsigned long
  6. long long/unsigned long long
  7. float
  8. double

数组

常量:
无法直接修改的值

C风格字符串:
以0结尾

数组

结构体

基础知识:
CPU如何存取内存(格子):
32位64位的含义:

  1. 连接CPU与内存的总线宽度
  2. CPU一次性取几个0/1(取几个格子的数据)
    1. 32位表示CPU一次取4个格子
    2. 64位表示CPU一次取8个格子

物理内存的排布:
如果是64位,则8个格子为一行

内存使用优化:
不要让CPU分两次取一个基本数据类型

结构体的内存结构

结构体的成员访问

2.2 函数、表达式和逻辑语句

函数定义:

<返回值> <函数名>(<若干参数>) {
   <若干语句>
   return <>;
}

函数调用

<函数名>(<参数>)

表达式
运算符:

  1. 可以被视作函数调用
  2. 优先级
  3. 结合性
a + b * c;
operator+(a, operator*(b, c));

逻辑语句:
判断和跳转

条件语句(if/switch):只判断

返回/跳转/继续/中断(return/goto/continue/break):只跳转

循环语句(for/while):判断和跳转的结合

  int times_eating = 3;
  const char *food_1 = "rice";
  const char *food_2 = "noodles";
  const char *food_3 = "apple";

  for (int number_eating = 0; number_eating < times_eating; /*continue*/ ++number_eating) {
    if (number_eating == 0) {
      std::cout << number_eating + 1 << " " << food_1 << std::endl;
    } else {
      //
    }
    continue;
    // break;
    if (number_eating == 1) {
      std::cout << number_eating + 1 << " " << food_2 << std::endl;
    }
    if (number_eating == 2) {
      std::cout << number_eating + 1 << " " << food_3 << std::endl;
    }
  }// break;

  std::cout << "switch: " << std::endl;
  {
    int number_eating = 0;
    for (; number_eating < times_eating;) {
      switch (number_eating) {
      case 0:
        std::cout << number_eating + 1 << " " << food_1 << std::endl;
        break;
      case 1:
        std::cout << number_eating + 1 << " " << food_2 << std::endl;
        break;
      case 2:
        std::cout << number_eating + 1 << " " << food_3 << std::endl;
        break;
      default:
        break;
      }
      ++number_eating;
    }
  }

3. 数据结构和函数进阶

3.1 常见数据结构
  1. 数组
  2. 链表
  3. 队列
  4. 集合
  5. 映射
函数调用–栈–递归
3.2 指针与引用

基础知识:
内存的结构

指针:

  1. 获取指针
  2. 使用指针

引用:

  1. 语法糖 – 和指针等价
3.3 函数调用的底层原理

4. 实践-俄罗斯方块

基础知识:

  1. 应用程序都是基本输入输出系统

控制台程序:

  1. 打字机
  2. 显示器
4.1 源代码结构

顶层之可以包含如下内容:

  1. 预处理指令,例如:
    1. 头文件引入
    2. 宏定义
  2. 定义类型
  3. 定义变量
  4. 定义函数
4.2 俄罗斯方块之移动骨牌

文件CMakeLists.txt的内容(构建工具的代码):

# 设置cmake参数(最小版本)
cmake_minimum_required(VERSION 3.20)
# 声明工程
project(Tetris)

# 设置C++标准c++17
set(CMAKE_CXX_STANDARD 17)

# 创建可执行程序Tetris
add_executable(Tetris main.cpp)
# 指定Tetris链接ncurses库
# 该库可能需要安装: sudo apt install libncurses-dev
target_link_libraries(Tetris PRIVATE ncursesw)

文件main.cpp的内容:

// 引入std::setlocal函数
#include <locale>
// 引入std::clamp函数
#include <algorithm>
// 引入ncurses库,包括类型和函数:
// * WINDOW - 窗口类型
// * newwin - 创建窗口
// * mvwprintw - 在窗口上显示内容 
// * wrefresh - 刷新窗口显示
// * getch - 获取用户输入
// * napms - 等待若干时间
// * ...
#include <ncurses.h>

// 四格骨牌
struct Tetro {
};

// 格子
struct Cell {
  // bool 0/1 false/true 1字节
  bool has_tetro;//< 有没有骨牌
};

// 棋盘
struct Board {
  // 20 行 10 列
  Cell cells[20][10];
};

Board board;
WINDOW *board_window;

void CreateWindow() {
  //height, width, starty, startx
  board_window = newwin(20, 10, 0, 0);
}

void RenderBoard() {
  // 遍历行
  for (int row = 0; row < 20; ++row) {
    // 遍历每个格子
    for (int col = 0; col < 10; ++col) {
      // 看格子里有没有骨牌,有则绘制*,无则绘制空白
      Cell &cell = board.cells[row][col];
      if (cell.has_tetro) {
        mvwprintw(board_window, row, col, "*");
      } else {
        mvwprintw(board_window, row, col, " ");
      }
    }
  }
  // 刷新界面
  wrefresh(board_window);
}

int main() {
  // 初始化界面系统ncurses
  setlocale(LC_ALL, "");
  initscr();
  cbreak();
  noecho();
  curs_set(0);
  scrollok(stdscr, TRUE);
  nodelay(stdscr, TRUE);

  // 创建窗口
  CreateWindow();

  // 记录骨牌当前的位置
  int x = 0;
  int y = 0;

  // 循环处理用户的输入
  for (;;) {
    // 等待一毫秒(节约CPU)
    napms(1);

    // 删除之前的骨牌
    board.cells[y][x].has_tetro = false;

    // 获取用户输入
    char input = getch();

    // 处理用户输入
    // w a s d 表示上下左右
    switch (input) {
    case 'w':
      y = y - 1;
      break;
    case 'a':
      x = x - 1;
      break;
    case 's':
      y = y + 1;
      break;
    case 'd':
      x = x + 1;
      break;
    }
    // 防止越界
    x = std::clamp(x, 0, 9);
    y = std::clamp(y, 0, 19);

    // 创建新的骨牌
    board.cells[y][x].has_tetro = true;
    // 绘制棋盘
    RenderBoard();
  }

  // 退出程序
  return 0;
}
5.1 声明、定义、命名空间-初步

源代码(编译单元)顶层:

  1. 定义(全局)变量
  2. 定义函数
  3. 定义类型
  4. 声明(全局)变量
  5. 声明函数
  6. 声明类型

函数体:

  1. 定义局部变量
  2. 定义结构体

命名空间的创建:

namespace <name>{
}

命名空间的嵌套:

namespace <name>{
namespace <name2>{
}
}

别名:

namespace <alias> = <name>;

可以简化使用

  1. using namespace <ns>;
  2. using <symbol>;
  3. 作用域

符号名解决冲突:
4. ::代表全局
5. 明确指定命名空间

adl

5.2 类
  1. struct vs class

  2. private vs public

  3. 成员变量

  4. 成员函数

  5. 外部访问成员变量

  6. 外部调用成员函数

  7. 内部访问成员变量

  8. 内部访问成员函数

  9. 静态成员变量

  10. 静态成员函数

  11. 外部访问静态成员变量

  12. 外部调用静态成员函数

  13. 内部访问静态成员变量

  14. 内部访问静态成员函数

  15. 成员函数的实现原理

  16. 声明类型

  17. 定义类型

  18. 定义成员变量

  19. 声明静态成员变量(全局变量)

  20. 声明成员函数

  21. 声明静态成员函数(全局函数)

  22. 定义静态成员变量(全局变量)

  23. 定义成员函数

  24. 定义静态成员函数(全局函数)

工程实践的经验总结:数据与过程的绑定
取“类”与“面向对象”

const-常量
const的第二种语法-修饰this指针
volatile
mutable

RAII 有始有终!!
一个对象必然包含两个部分:创建和销毁

类-构造函数-析构函数

// 默认构造
// 带参数构造函数
// 复制构造
// 移动构造
// 析构函数

隐式创建的构造函数-析构函数
隐式调用的构造函数-析构函数

// 初始化列表

模板

模板——相对高级的宏替换

模板的重要作用:替换——代码复用

模板参数推导

类模板

形参:可以是类型或整数(包括枚举)

函数模板

成员函数模板

模板特化

偏特化
全特化

模板实例化

创建一个实实在在的类型定义或函数定义

STL
容器
迭代器

说法1:遍历需要一个工具
说法2:遍历过程中发生变化的对象

new - delete
  1. malloc 动态分配一段内存

  2. free 释放一段动态内存(malloc分配的)

  3. new = malloc + 构造函数

  4. delete = 析构函数 + free

new 的用法

// 创建一个基本数据类型
int *p_int = new int;
// 创建一个基本数据类型的数组
int *p_int_array = new int[100];

int **pp_int = new int*;
*pp_int = new int;
class Snake{
public:
 Snake();
 Snake(int x,int y);
 int x;
 int y;
};
// 创建一个对象
Snake *p_snake = new Snake{}; // new Snake
// Snake *p_snake2 = new Snake{1, 2};
// 创建一对象数组
Snake *p_shake_array = new Snake[100]{};

delete 的用法

// 销毁一个基本数据类型
delete p_int;
// 销毁一个基本数据类型的数组
delete []p_int_array;

delete *pp_int;
delete pp_int;
// 销毁一个对象
delete p_snake;
// 销毁对象数组
delete []p_shake_array;
函数指针
获取
使用
封装、继承、多态

封装:把一组数据或着过程实现,交给外部使用,外部不用关心内部如何实现
继承: IS-A
覆盖:语法上覆盖父类的函数

通过命名空间调用特定成员函数

从内存角度理解继承

多继承

成员/继承 访问说明符:
private protected public
对于类外访问类成员而言:
访问说明符链路上必须全是public
对于类内访问父类成员而言:
访问说明符链路上不能包含private

virtual

重写

相同的过程,不同的行为

继承中构造函数和析构函数的行为

函数重载

运算符重载

调用体
lambda
智能指针
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值