C++知识要点总结笔记

news2024/11/12 20:21:16

文章目录

  • 前言
  • 一、c++基础
    • 1.指针和引用
      • 指针和引用的区别
    • 2.数据类型
      • 整型 short int long 和 long long
      • 无符号类型
    • 3.关键字
      • const
      • static
      • const和static的区别
      • define 和 typedef 的区别
      • define 和 inline 的区别
      • const和define的区别
      • new 和 malloc的区别
      • constexpr
      • volatile
      • extern
      • 前置++与后置++
      • std::atomic
  • 待续


前言

总结c++语法、内存等知识。仅供个人学习记录用


一、c++基础

1.指针和引用

指针和引用的区别

指针存放某个对象的地址,其本身就是变量(命了名的对象),本身就有地址,所以可以有指向指针的指针;可变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变
引用就是变量的别名,从一而终,不可变,必须初始化

  1. 定义和声明
    指针是⼀个变量,其值是另⼀个变量的地址。声明指针时,使用 * 符号。
    引用是⼀个别名,它是在已存在的变量上创建的。在声明引用时,使用 & 符号。
  2. 使用和操作
    指针可以通过解引用操作符 * 来访问指针指向的变量的值,还可以通过地址运算符 & 获取变量的地址。
    引用在声明时被初始化,并在整个生命周期中一直引用同一个变量。不需要使用解引用操作符,因为引用本身就是变量的别名
  3. 空值和空引用
    指针可以为空(nullptr)表示不指向任何有效的地址。
    引用必须在声明时初始化,并且不能在后续改变引用的绑定对象。因此,没有空引用的概念
    不存在指向空值的引用,但是存在指向空值的指针
  4. 可变性
    指针可以改变指针的指向,使其指向不同的内存地址。
    引用⼀旦引用被初始化,它将⼀直引用同⼀个对象,不能改变绑定。
  5. 用途
    指针通常用于动态内存分配、数组操作以及函数参数传递。
    引用通常用于函数参数传递、操作符重载以及创建别名。

2.数据类型

整型 short int long 和 long long

C++ 整型数据长度标准:
short 至少 16 位
int 至少与 short ⼀样长
long 至少 32 位,且至少与 int ⼀样长
long long 至少 64 位,且至少与 long ⼀样长

在使用8位字节的系统中,1 byte = 8 bit。
很多系统都使用最小长度,short 为 16 位即 2 个字节,long 为 32 位即 4 个字节,long long 为 64 位即 8 个字节,int 的长度较为灵活,⼀般认为 int 的长度为 4 个字节,与 long 等长。
可以通过运算符 sizeof 判断数据类型的长度。例如sizeof (int)
头文件climits定义了符号常量:例如:INT_MAX 表示 int 的最大值,INT_MIN 表示 int 的最小值

无符号类型

即为不存储负数值的整型,可以增大变量能够存储的最大值,数据长度不变。
int 被设置为自然长度,即为计算机处理起来效率最高的长度,所以选择类型时⼀般选用 int 类型。

3.关键字

const

const的作用
const 关键字主要用于指定变量、指针、引用、成员函数等的性质

  1. 常量变量:声明常量,使变量的值不能被修改。
  2. 指针和引用:声明指向常量的指针,表示指针所指向的值是常量,不能通过指针修改。声明常量引用,表示引用的值是常量,不能通过引用修改。
  3. 成员函数:用于声明常量成员函数,表示该函数不会修改对象的成员变量(对于成员变量是非静态的情况)。
  4. 常量对象:声明对象为常量,使得对象的成员变量不能被修改。
  5. 常引用参数:声明函数参数为常量引用,表示函数不会修改传入的参数。
  6. 常量指针参数:声明函数参数为指向常量的指针,表示函数不会通过指针修改传⼊的数据。

常量指针(底层const)
是指定义了一个指针,这个指针指向⼀个只读的对象,不能通过常量指针来改变这个对象的值。常量指针强调的是指针对其所指对象的不可改变性。
特点:靠近变量名。
形式:
(1)const 数据类型 * 指针变量 = 变量名
(2)数据类型 const * 指针变量 = 变量名

int temp = 10;
const int* a = &temp;
int const *a = &temp;
// 更改:
*a = 9; // 错误:只读对象
temp = 9; // 正确

指针常量(顶层const)
指针常量是指定义了一个指针,这个指针的值只能在定义时初始化,其他地方不能改变。指针常量强调的是指针的不可改变性。
特点:靠近变量类型。
形式:数据类型 * const 指针变量 = 变量名

int temp = 10;
int temp1 = 12;
int* const p = &temp;
// 更改:
p = &temp2; // 错误
*p = 9; // 正确

拓展:
顶层const:指针本身是常量;
底层const:指针所指的对象是常量;
左定值,右定向:指的是const在*的左还是右边
const在*左边,表示不能改变指向对象的值;const在*右边,表示不能更换指向的对象
若要修改const修饰的变量的值,需要加上关键字volatile;
若想要修改const成员函数中某些与类状态无关的数据成员,可以使用mutable关键字来修饰这个数据成员;

static

static关键字主要用于控制变量和函数的生命周期、作用域以及访问权限。
实现多个对象之间的数据共享 + 隐藏,并且使用静态成员还不会破坏隐藏原则;

  1. 静态变量
    在函数内部使用 static 关键字修饰的变量称为静态变量。
    静态变量在程序的整个生命周期内存在,不会因为离开作用域而被销毁。
    静态变量默认初始化为零(对于基本数据类型)。
void exampleFunction() {
 static int count = 0; // 静态变量
 count++;
 cout << "Count: " << count << endl;
}
  1. 静态函数
    在类内部使用 static 关键字修饰的函数是静态函数。
    静态函数属于类而不是类的实例,可以通过类名直接调用,而无需创建对象。
    静态函数不能直接访问非静态成员变量或非静态成员函数。
class ExampleClass {
public:
 static void staticFunction() {
 cout << "Static function" << endl;
 }
};
  1. 静态成员变量
    在类中使用 static 关键字修饰的成员变量是静态成员变量。
    所有类的对象共享同一个静态成员变量的副本。
    静态成员变量必须在类外部单独定义,以便为其分配存储空间。
class ExampleClass {
public:
 static int staticVar; // 静态成员变量声明
};
// 静态成员变量定义
int ExampleClass::staticVar = 0;
  1. 静态成员函数
    在类中使用 static 关键字修饰的成员函数是静态成员函数。
    静态成员函数不能直接访问非静态成员变量或非静态成员函数。
    静态成员函数可以通过类名调用,而不需要创建类的实例。
class ExampleClass {
public:
 static void staticMethod() {
 cout << "Static method" << endl;
 }
};
  1. 静态局部变量
    在函数内部使用 static 关键字修饰的局部变量是静态局部变量。
    静态局部变量的生命周期延长到整个程序的执行过程,但只在声明它的函数内可见。
void exampleFunction() {
 static int localVar = 0; // 静态局部变量
 localVar++;
 cout << "LocalVar: " << localVar << endl;
}

const和static的区别

在这里插入图片描述

define 和 typedef 的区别

define

  1. 只是简单的字符串替换,没有类型检查
  2. 是在编译的预处理阶段起作用
  3. 可以用来防止头文件重复引用
  4. 不分配内存,给出的是立即数,有多少次使用就进行多少次替换
    typedef
  5. 有对应的数据类型,是要进行判断的
  6. 是在编译、运行的时候起作用
  7. 在静态存储区中分配空间,在程序运行过程中内存中只有⼀个拷贝

define 和 inline 的区别

define:
定义预编译时处理的宏,只是简单的字符串替换,没有类型检查,不安全。
inline:
inline是先将内联函数编译完成生成了函数体,直接插入被调用的地方,减少了压栈,跳转和返回的操作。没有普通函数调用时的额外开销;
内联函数是一种特殊的函数,会进行类型检查;
对编译器的一种请求,编译器有可能拒绝这种请求;

C++中inline编译限制:

  1. 不能存在任何形式的循环语句
  2. 不能存在过多的条件判断语句
  3. 函数体不能过于庞大
  4. 内联函数声明必须在调用语句之前

const和define的区别

const用于定义常量;而define用于定义宏,而宏也可以用于定义常量。都用于常量定义时,它们的区别有:

  1. const生效于编译的阶段;define生效于预处理阶段。
  2. const定义的常量,在C语言中是存储在内存中、需要额外的内存空间的;define定义的常量,运行时是直接地操作数,并不会存放在内存中。
  3. const定义的常量是带类型的;define定义的常量不带类型。因此define定义的常量不利于类型检查。

new 和 malloc的区别

1、new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL;malloc分配内存失败时返回NULL。
2、使用new操作符申请内存分配时无须指定内存块的大小,而malloc则需要显式地指出所需内存的尺⼨。
3、opeartor new /operator delete可以被重载,而malloc/free并不允许重载。
4、new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。而malloc则不会
5、malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符
6、new操作符从自由存储区上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。
在这里插入图片描述

constexpr

const 表示“只读”的语义,constexpr 表示“常量”的语义
constexpr 只能定义编译期常量,而const 可以定义编译期常量,也可以定义运行期常量。
你将一个成员函数标记为constexpr,则顺带也将它标记为了const。如果你将⼀个变量标记为constexpr,则同样它是const的。但相反并不成立,一个const的变量或函数,并不是constexpr的。

constexpr变量
复杂系统中很难分辨一个初始值是不是常量表达式,可以将变量声明为constexpr类型,由编译器来验证变量的值是否是一个常量表达式。
必须使用常量初始化:

constexpr int n = 20;
constexpr int m = n + 1;
static constexpr int MOD = 1000000007;

如果constexpr声明中定义了一个指针,constexpr仅对指针有效,和所指对象无关。

constexpr int *p = nullptr; //常量指针 顶层const
const int *q = nullptr; //指向常量的指针, 底层const
int *const q = nullptr; //顶层const

constexpr函数:
constexpr函数是指能用于常量表达式的函数。
函数的返回类型和所有形参类型都是字面值类型,函数体有且只有一条return语句。

constexpr int new() {return 42;}

为了可以在编译过程展开,constexpr函数被隐式转换成了内联函数。
constexpr和内联函数可以在程序中多次定义,一般定义在头文件。

constexpr 构造函数:
构造函数不能说const,但字面值常量类的构造函数可以是constexpr。
constexpr构造函数必须有一个空的函数体,即所有成员变量的初始化都放到初始化列表中。对象调用的成员函数必须使用 constexpr 修饰

constexpr的好处

  1. 为一些不能修改数据提供保障,写成变量则就有被意外修改的风险。
  2. 有些场景,编译器可以在编译期对constexpr的代码进行优化,提高效率。
  3. 相比宏来说,没有额外的开销,但更安全可靠。

volatile

volatile是与const绝对对立的类型修饰符
影响编译器编译的结果,用该关键字声明的变量表示该变量随时可能发生变化,与该变量有关的运算,不要进行编译优化;会从内存中重新装载内容,而不是直接从寄存器拷贝内容。
作用:
指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值,保证对特殊地址的稳定访问
使用场合:
在中断服务程序和cpu相关寄存器的定义
举例说明:
空循环:

for(volatile int i=0; i<100000; i++); // 它会执⾏,不会被优化掉 

extern

定义:声明外部变量(在函数或者文件外部定义的全局变量)

前置++与后置++

self &operator++() { //前置++
 node = (linktype)((node).next);
 return *this;
}
const self operator++(int) { //后置++
 self tmp = *this;
 ++*this;
 return tmp;
}

为了区分前后置,重载函数是以参数类型来区分,在调用的时候,编译器默默给int指定为⼀个0

  1. 为什么后置返回对象,而不是引用
    因为后置为了返回旧值创建了⼀个临时对象,在函数结束的时候这个对象就会被销毁,如果返回引用,那么我请问你?你的对象对象都被销毁了,你引用啥呢?
  2. 为什么后置前面也要加const
    其实也可以不加,但是为了防止你使⽤i++++,连续两次的调用后置++重载符,为什么呢?
    原因:
    它与内置类型行为不一致;你无法获得你所期望的结果,因为第一次返回的是旧值,而不是原对象,你调用两次后置++,结果只累加了一次,所以我们必须手动禁止其合法化,就要在前面加上const。
  3. 处理用户的自定义类型
    最好使用前置++,因为他不会创建临时对象,进而不会带来构造和析构而造成的格外开销。

std::atomic

问题:a++ 和 int a = b 在C++中是否是线程安全的?
答案:不是!

a++:
从C/C++语法的级别来看,这是一条语句,应该是原子的;但从编译器得到的汇编指令来看,其实不是原子的。
其一般对应三条指令,首先将变量a对应的内存值搬运到某个寄存器(如eax)中,然后将该寄存器中的值自增1,再将该寄存器中的值搬运回a代表的内存中

mov eax, dword ptr [a] # (1)
inc eax # (2)
mov dword ptr [a], eax # (3)

现在假设a的值是0,有两个线程,每个线程对变量a的值都递增1,预想⼀下,其结果应该是2,可实际运行结构可能是1!是不是很奇怪?

int a = 0;
// 线程1(执⾏过程对应上⽂汇编指令(1)(2)(3))
void thread_func1()  {a++;}
// 线程2(执⾏过程对应上⽂汇编指令(4)(5)(6))
void thread_func2() {a++;}

我们预想的结果是线程1和线程2的三条指令各自执行,最终a的值变为2,但是由于操作系统线程调度的不确定性,线程1执行完指令(1)和(2)后,eax寄存器中的值变为1,此时操作系统切换到线程2执行,执行指令(3)(4)(5),此时eax的值变为1;接着操作系统切回线程1继续执⾏,执行指令(6),得到a的最终结果1。

int a = b
从C/C++语法的级别来看,这是条语句应该是原子的;但从编译器得到的汇编指令来看,由于现在计算机CPU架构体系的限制,数据不能直接从内存某处搬运到内存另外一处,必须借助寄存器中转,因此这条语句一般对应两条计算机指令,即将变量b的值搬运到某个寄存器(如eax)中,再从该寄存器搬运到变量a的内存地址
中:

mov eax, dword ptr [b]
mov dword prt [a], eax

既然是两条指令,那么多个线程在执行这两条指令时,某个线程可能会在第一条指令执行完毕后被剥夺CPU时间片,切换到另一个线程而出现不确定的情况。

解决办法
C++11新标准发布后改变了这种困境,新标准提供了对整形变量原子操作的相关库,即std::atomic,这是⼀个模板类型:

template<class T>
struct atomic:

我们可以传⼊具体的整型类型对模板进行实例化,实际上stl库也提供了这些实例化的模板类型

// 初始化1
std::atomic<int> value;
value = 99;

// 初始化2
// 下⾯代码在Linux平台上无法编译通过(指在gcc编译器)
std::atomic<int> value = 99;
// 出错的原因是这⾏代码调⽤的是std::atomic的拷贝构造函数
// ⽽根据C++11语⾔规范,std::atomic的拷贝构造函数使⽤=delete标记禁止编译器⾃动⽣成
// g++在这条规则上遵循了C++11语言规范。

待续


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1926536.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用Speedtest-Tracker跟踪上网速度(续)

什么是 Speedtest Tracker ? Speedtest Tracker 是一款自托管互联网性能跟踪应用程序&#xff0c;可针对 Ookla 的 Speedtest 服务运行速度测试检查。 之前老苏介绍的另一个 https://github.com/henrywhitaker3/Speedtest-Tracker 已被放弃。现在这个是积极维护的替代品&#…

repo sync同步出错解决

当出现下面提示时 e list of known hosts. Fetching: 100% (1167/1167), done in 44.619s info: A new version of repo is available warning: repo is not tracking a remote branch, so it will not receive updates Repo command failed: RepoUnhandledExceptionError …

element UI时间组件两种使用方式

加油&#xff0c;新时代打工&#xff01; 组件官网&#xff1a;https://element.eleme.cn/#/zh-CN/component/date-picker 先上效果图&#xff0c;如下&#xff1a; 第一种实现方式 <div class"app-container"><el-formref"submitForm":model&q…

Zabbix6.0监控Freeswitch状态

一、前提环境说明 1、最终实现Freeswitch监控指标信息&#xff1a; 2、环境需求&#xff1a; &#xff08;1&#xff09;需要使用Zabbix6.0及以上 &#xff08;2&#xff09;需要使用zabbix_agent2 二、实现步骤 1、zabbix_agent2添加监控键值 cat /etc/zabbix/conf.d/fr…

Git代码管理工具 — 3 Git基本操作指令详解

目录 1 获取本地仓库 2 基础操作指令 2.1 基础操作指令框架 2.2 git status查看修改的状态 2.3 git add添加工作区到暂存区 2.4 提交暂存区到本地仓库 2.5 git log查看提交日志 2.6 git reflog查看已经删除的记录 2.7 git reset版本回退 2.8 添加文件至忽略列表 1 获…

2.有限状态机

1. 标准三段式写法&#xff1a; 1.一段代表当前状态赋值&#xff1b; 2.一段代表下一个状态如何跳变 3.一段代表输出控制 一定要带上复位 前两段模板集合一样&#xff0c;第三段取决于具体需求 parameter IDLE_ST 4b0000&#xff1b; parameter TEST_ST 4b0001; ... re…

C++中链表的底层迭代器实现

大家都知道在C的学习中迭代器是必不可少的&#xff0c;今天我们学习的是C中的链表的底层迭代器的实现&#xff0c;首先我们应该先知道链表的底层迭代器和顺序表的底层迭代器在实现上有什么区别&#xff0c;为什么顺序表的底层迭代器更加容易实现&#xff0c;而链表的底层迭代器…

Ubuntu20.04 编译安装FFmpeg,出错分析以及解决方案

最近工程上需要对FFmpeg底层源码进行修改&#xff0c;需要重新编译&#xff0c;遇见不少坑&#xff0c;出篇教程记录一下。 文章目录 1.FFmpeg源码下载地址2.编译环境配置3.编译FFmpeg4.配置FFmpeg运行环境 1.FFmpeg源码下载地址 官方下载地址:Index of /releases (ffmpeg.or…

使用MovaXterm连接VMware的centos

一、确认局域网ip 检查虚拟机分配的网段是否一致 二、确认centos的ip 输入 ip addr 获得设备ip为192.168.153.130 三、用MovaXterm建立ssh连接 有同行可能会问&#xff0c;直接使用VMware操作centos系统不行吗&#xff1f;为什么要再多一步。 理由&#xff1a;1、测试环境c…

Zabbix6.0使用自带模板(MySQL by Zabbix agent 2)监控MySQL数据库

注意&#xff1a;Zabbix6.0使用MySQL by Zabbix agent 2 模板可直接监控MySQL数据库&#xff0c;只需要创建监控用户&#xff0c;模板宏中填写数据库账号信息即可&#xff0c;只需要3步&#xff0c;特简单&#xff01; 1、MySQL创建用户&#xff0c;用于收集数据库信息 creat…

HarmonyOS 界面开发基础篇

一、布局思路 ArkUI(方舟开发框架)是一套 构建 鸿蒙应用 界面 的框架。 构建页面的最小单位就是“组件”。 组件分类 基础组件&#xff1a;界面呈现的基础元素。 如&#xff1a;文字、图片、按钮等。容器组件&#xff1a;控制布局排布。 如&#xff1a;Row行、Column列等。…

MySQL篇:日志

前言 MySQL中常见的日志&#xff1a; 重做日志&#xff08;Redo Log&#xff09;&#xff1a; 用途&#xff1a; 保证事务的持久性&#xff0c;记录事务对数据库所做的所有修改。 内容&#xff1a; 记录事务对数据页的物理修改&#xff0c;而不是逻辑修改。 配置&#xff1a; …

一款IM即时通讯聊天系统源码,包含app和后台源码

一款IM即时通讯聊天系统源码 聊天APP 附APP&#xff0c;后端是基于spring boot开发的。 这是一款独立服务器部署的即时通讯解决方案&#xff0c;可以帮助你快速拥有一套自己的移动社交、 企业办公、多功能业务产品。可以 独立部署&#xff01;加密通道&#xff01;牢牢掌握通…

《C专家编程》杂谈

库函数调用和系统调用的区别 系统调用比函数调用要慢很多&#xff0c;因为还要切换到内核模式。

防火墙综合实验之NAT和智能选路

目录 前言&#xff1a; 一、实验题目 二、实验操作 需求一 需求二 需求三 需求四、需求五 需求六 需求七 ​编辑 需求八 需求九 需求十 需求十一 三、需求测试 前言&#xff1a; 本篇文章是延续上一篇文章&#xff0c;简单来说就是防火墙实验的完善和延续&#…

【CUDA】CUDA中缓存机制对计时的影响

笔者在阅读知乎上一个关于CUDA编程的专栏时&#xff0c;发现作者写的很多文章中都会附带计时的模块用于评估程序的运行效率&#xff0c;然而笔者发现&#xff0c;在运行这篇文章中的代码时时&#xff0c;得到的结果和作者的结果有较大差异&#xff0c;主要体现在&#xff1a;使…

《战甲神兵》开发者报告:游戏崩溃问题80%发生在Intel可超频酷睿i9处理器上——酷睿i7 K系列CPU也表现出高崩溃率

在Intel持续面临第13代和第14代CPU崩溃问题的背景下&#xff0c;近日&#xff0c;《战甲神兵》(Warframe)的开发者们于7月9日披露了游戏崩溃的统计数据&#xff0c;并描述了诊断该问题的过程。根据开发团队的说法&#xff0c;一名未进行超频且使用全新PC的员工&#xff0c;即便…

网络安全设备——EDR

网络安全中的EDR&#xff08;Endpoint Detection and Response&#xff0c;端点检测与响应&#xff09;是一种主动式的端点安全解决方案&#xff0c;它专注于监控、检测和响应计算机和终端设备上的安全威胁。以下是EDR的详细解释&#xff1a; 一、定义与功能 EDR是一种网络安…

【C++】入门基础(引用、inline、nullptr)

目录 一.引用 1.引用的定义 2.引用的特性 3.引用的使用场景 4.const引用 5.引用和指针的区别 二.inline 三.nullptr 一.引用 1.引用的定义 引用不是新定义一个变量&#xff0c;而是给已经存在的变量取一个别名&#xff0c;编译器不会给引用变量开辟内存空间&#xff0c…

(实测可用)(3)Git的使用——RT Thread Stdio添加的软件包,github与gitee冲突造成无法上传文件到gitee

★硬件资源&#xff1a;本文章以STM32L431RCT6做主控芯片做验证&#xff1b; ★IDE开发环境&#xff1a;RT Thread stdio&#xff1b; ★RT Thread 版本&#xff1a;V4.0.3 一、RT Thread Stdio加载软件包 1、如下图所示&#xff0c;通过RT Thread Stdio加载的软件包&#…