ShuangChenYue ShuangChenYue
首页
  • Cpp之旅
  • Cpp专栏
  • Effective_CPP
  • muduo网络库
  • Unix环境高级编程
  • Cpp提高编程
  • 计算机网络
  • 操作系统
  • 数据结构
  • Linux
  • 算法
  • 基础篇
  • MySql
  • Redis
  • 电子嵌入式通信协议
  • 深入浅出SSD
  • 文件系统
  • 汇编语言
  • STM32
  • 随笔(持续更新)
  • Git知识总结
  • Git 创建删除远程分支
  • nvm使用小结
  • 虚拟机固定 IP 地址
  • Shell 脚本学习笔记
  • VScode 插件 CodeGeeX 使用教程
  • KylinV10 将项目上传至 Github教程
  • KylinV10 安装 MySQL 教程(可防踩雷)
  • kylinV10-SP1 安装 QT
  • 高并发内存池
  • USBGUARD 项目编译环境配置
  • Power_Destory 项目
  • U 盘清除工具编译教程
  • 个人博客代码推送教程
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • MFC编程随记
  • MFC实现ini配置文件的读取
  • MFC实现点击列表头排序
  • 贴图法美化Button按钮
  • 如何高效阅读嵌入式项目代码
  • NAND Flash
  • ARM 处理器
  • 嵌入式基础知识-存储器
  • 闪存存储和制造技术概述
  • 芯片IO驱动力
  • 主流先进封装技术介绍
  • 虎牙C++技术面经
  • 金山一面复习
  • 完美世界秋招 C++ 游戏开发面经(Cpp部分)
  • 博客搭建
  • 网站收藏箱
首页
  • Cpp之旅
  • Cpp专栏
  • Effective_CPP
  • muduo网络库
  • Unix环境高级编程
  • Cpp提高编程
  • 计算机网络
  • 操作系统
  • 数据结构
  • Linux
  • 算法
  • 基础篇
  • MySql
  • Redis
  • 电子嵌入式通信协议
  • 深入浅出SSD
  • 文件系统
  • 汇编语言
  • STM32
  • 随笔(持续更新)
  • Git知识总结
  • Git 创建删除远程分支
  • nvm使用小结
  • 虚拟机固定 IP 地址
  • Shell 脚本学习笔记
  • VScode 插件 CodeGeeX 使用教程
  • KylinV10 将项目上传至 Github教程
  • KylinV10 安装 MySQL 教程(可防踩雷)
  • kylinV10-SP1 安装 QT
  • 高并发内存池
  • USBGUARD 项目编译环境配置
  • Power_Destory 项目
  • U 盘清除工具编译教程
  • 个人博客代码推送教程
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • MFC编程随记
  • MFC实现ini配置文件的读取
  • MFC实现点击列表头排序
  • 贴图法美化Button按钮
  • 如何高效阅读嵌入式项目代码
  • NAND Flash
  • ARM 处理器
  • 嵌入式基础知识-存储器
  • 闪存存储和制造技术概述
  • 芯片IO驱动力
  • 主流先进封装技术介绍
  • 虎牙C++技术面经
  • 金山一面复习
  • 完美世界秋招 C++ 游戏开发面经(Cpp部分)
  • 博客搭建
  • 网站收藏箱
  • Cpp之旅

    • 第1章 基础
      • 1.1 程序
      • 1.2 新东西
      • 1.3 函数
      • 1.4 类型、变量、运算
        • 1.4.1 运算
        • 1.4.2 初始化
      • 1.5 作用域和生命周期
      • 1.6 常量
      • 1.7 指针、数组、引用
        • 1.7.1 空指针
      • 1.8 建议
    • 第2章 用户自定义类型
    • 第3章 模块化
    • 第4章 错误处理
    • 第5章 类
    • 第6章 基本操作
    • 第7章 模板
    • 第8章 概念和泛型编程
    • 第9章 标准库
  • Cpp专栏

  • Effetcive_CPP

  • muduo网络库

  • Unix环境高级编程

  • Cpp提高编程

  • CPP语言
  • Cpp之旅
霜晨月
2023-11-24
目录

第1章 基础

# Cpp之旅(学习笔记)第1章

# 第1章 基础

# 1.1 程序

Cpp是一门编译型语言。源代码必须交由编译器处理生成可执行文件,然后由链接器组装成可执行程序。

一个可执行文件通常是为一个特定的硬件与操作系统组合而制定的,换句话说,它在安卓设备与Windows个人电脑之间是不可移植的,因此,可移植性指的是源代码的可移植性,即源代码可以在多种系统中编译成功,然后运行。

最小的Cpp程序:

int main(){}
1

定义一个main函数,不接受任何参数,也不做任何事情。

# 1.2 新东西

//旧版本
#include <iostream>
int main() {
	std::cout << "Hello, World!\n";
}
1
2
3
4
5
//Cpp20版本
import std;
int main() {
    std::cout << "Hello, World!\n";
}
1
2
3
4
5

import std;指示编译器去声明标准库变量的存在。如果没有这个声明,std::cout << "Hello, World!\n";将没有意义。但是指令import将所有标准库放进一个单独的std模块还没有成为标准。

# 1.3 函数

double get(const vector<double>& vec, int index);
//函数类型是:
double(const vector<double>&,int)
1
2
3

对于成员函数来说,类的名称也是函数类型的一部分:

char& String::operator[](int index);
//函数类型是:
char& String::(int)
1
2
3

# 1.4 类型、变量、运算

  • 前缀0b表示二进制整数字面量,如:0b10101010
  • 前缀0x表示十六进制整数字面量,如:0xBAD12CE3
  • 前缀0表示八进制字面量,如:0334

可以引入单引号(’)作为数字分隔符提升长字面量的可读性

例如:Π的值大约是:3.14159 ‘ 26535 ’ 89793 ‘ 23846 ’ 26433 ‘ 83279 ’ 50288

用十六进制表示就是:0x3.243F ’ 6A88 ‘ 85A3 ’ 08D3

# 1.4.1 运算

  • 部分操作符的计算顺序是从左向右的:

    x.y、x->y、x(y)、x[y]、x<<y、x>>y、x&&y、x||y

  • 但赋值符号的计算顺序是从右往左的:

    x += y

# 1.4.2 初始化

  • 使用=或者{}初始化
double d1 = 2.3;
double d2 {2.3};//等价于double d2 = {2.3}
1
2
  • 使用=的形式是C语言传统的方式,如果拿不定主意,就是用更通用的{}列表形式。可以避免隐式类型转换导致的信息丢失
int i1 = 7.8;//i1变成了7(你可能感到意外)
int i2 {7.8};//错误:floating-point to integer conversion
1
2
  • 当使用=而不是{}的时候,会进行从double到int及从int到char这样的窄化类型转换。

如果变量的类型可以从初始化符号中推导出来,就无需显示指定类型

auto b = true; 	//bool类型
auto ch = 'x';	//char类型
auto i = 123;	//int类型
auto d = 1.2;	//double类型
...
1
2
3
4
5

使用auto声明变量时,作者倾向于使用=符号,因为没有类型转换的风险。当然,偏好使用{}也无伤大雅。

当没有明显的需要显示地指定类型时,一般使用 auto 。

理由如下:

  1. 该定义的作用域较大,我们希望代码的读者清楚地知道其类型。
  2. 初始化表达式的类型(对读者来说)不是显而易见的。
  3. 我们希望明确规定某个变量的范围和精度(如:希望使用double而非float)。

# 1.5 作用域和生命周期

  • **局部作用域:**在函数或匿名函数中定义的名字叫局部名字,作用域从声明它的地方开始,直到声明语句所在的块结尾。语句块的边界由一对{}决定。函数参数的名字也属于局部名字。
  • **类作用域:**定义在类的内部,不在任何函数、匿名函数、enum class中,可被叫做成员名字(或类成员名字)。作用域从它括起声明的左花括号 { 开始,到对应的右花括号 } 结束。
  • 命名空间作用域:在命名空间内部,并且不再任何函数、匿名函数、enum class中,则把这个名字叫做命名空间成员名字。作用域从声明它的地方开始,到命名空间结束为止。

某些对象也可以没有名字,比如:临时对象或者用new创建的对象:

vector<int> vec;					//vec是全局名字(全局整数动态数组)
void fct(int arg) {					//fct是全局名字(全局函数)arg是局部名字(局部整数参数)
    string motto {"Who dares wins"};//motto是局部名字
    auto p = new Record{"Hume"};	//p指向无名Record对象(由new创建)
    //...
}
struct Record {
  	string name;					//name是Record的成员名字(字符串成员)
    //...
};
1
2
3
4
5
6
7
8
9
10

一个new创建的对象可以持续“生存”,知道用delete将其销毁。

# 1.6 常量

Cpp支持两种不变性:

  • const:“我承诺不修改这个值”,主要用来说明接口,可以用指针或者引用的方式传入函数参数而不用担心被改变。编译器负责强制执行const承诺。const声明的值可以在运行时被计算。
  • constexpr:“请在编译时计算出它的值”,主要用于声明常量,作用是把数据置于只读内存区域(更小概率被破坏),以及提高性能。constexpr的值必须由编译器计算。

例如:

constexpr int dmv = 17;				//dmv是一个命名常量
int var = 17;						//var不是常量
const double sqv = sqrt(var);		//sqv是一个命名常量,可能在运行时计算
double sum(const vector<double>&);	//sum不会修改它的参数
vector<double> v {1.2, 3.4, 4.5};	//v不是常量
const double s1 = sum(v);			//可行:sum(v)在运行时计算
constexpr double s2 = sum(v);		//错误:sum(v)不是一个常量表达式
1
2
3
4
5
6
7

为了使函数可在常量表达式中使用,这个函数必须被定义为constexpr 或consteval,这样才能在编译期表达式中被计算。

例如:

constexpr double square(double x){return x*x};
constexpr double max1 = 1.4*square(17);	//可行:1.4*square(17)是常量表达式
constexpr double max2 = 1.4*square(var);//错误:var不是常量,所以square(var)不是常量
const double max3 = 1.4*square(var);	//可行:允许在运行时计算
1
2
3
4

# 1.7 指针、数组、引用

  • 数组:同类型元素的连续分配序列。
  • 指针:可存放指定类型的对象的地址。

在声明中,[ ]意味着对应类型的数组,*意味着指向对应类型的指针。

char v[6];		//6个字符组成的数组
char *p = &v[3];//p指向v的第4个元素
char x = *p;	//*p代表p指向的对象
1
2
3
  • 前置一元操作符*表示取内容,前置一元操作符&表示取地址,后置一元操作符&表示指向前者的引用。
  • 引用在初始化后就不能再指向其他的对象了。
  • 当用于什么语句时,操作符&、*、[ ]被称为声明操作符。
T* p	//T*:p是一个指向T的指针
T& r	//T&:r是一个指向T的引用
T f(A)  //T f(A):f是一个函数,接受A类型的参数,返回T类型的结果
1
2
3

# 1.7.1 空指针

当确实没有对象可指向,我们希望表达出一种“没有对象可用”的含义时,可令指针取值为nullptr。所有指针类型共享同一个nullptr。

在使用指针前检查它是否为空,是明智的行为:

int count_x(const char* p, char x){
    //计算x在p[]中出现的次数
    //假定p指向一个以零结尾的字符数组(或者指向空)
    if(p == nullptr)
        return 0;
    int count = 0;
    for(; *p != 0; ++p)
        if(*p == x)
            ++count;
    return count;
}
1
2
3
4
5
6
7
8
9
10
11
  • 假定输入的char*是一个C风格字符串,也就是说,改指针指向一个以零结尾的char数组。字符串字面量中的字符是不可变的。
  • 所以为了以count_x("Hello!"),这样的格式接收字符串字面量作为参数,因此把第一个参数声明为const char*。
  • 在旧式代码中,常常用0或者NULL代替nullptr,但是,使用nullptr可以消除整数(0或者NULL)与指针(nullptr)之间存在的潜在歧义。
  • 指针可以为空,但引用不能,引用必须指向有效的对象(编译器也假定如此)。当然,有些奇淫巧计可以打破这个规则,但请不要这么左。

# 1.8 建议

  1. 使用#include 或者 import引入库,可以简化编程。
  2. 函数重载的适用情况是,几个函数的任务相同而处理的参数类型不同;
  3. 如果一个函数可能需要在编译时求值,那么将它声明为constexpr;
  4. 如果一个函数必须在编译时求值,那么将它声明为consteval;
  5. 如果一个函数不允许有副作用,那么就将它声明为constexpr或consteval;
  6. 在声明语句中制定了类型名称(而非使用auto)时,优先使用{}初始化语法;
  7. 使用auto可避免重复输入类型名称;
  8. 在if语句的条件中声明变量时,优先采用隐式检验而不是与0或nullptr进行比较;
  9. 优先使用范围for语句而非使用显示循环变量的传统for语句;
  10. 只对位运算使用unsigned;
  11. 使用nullptr而非0或NULL;
上次更新: 2024/6/3 14:54:44
第2章 用户自定义类型

第2章 用户自定义类型→

Theme by Vdoing | Copyright © 2023-2024 霜晨月
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式