![](https://img-blog.csdnimg.cn/20201014180756916.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
C++学习
文章平均质量分 61
c++学习的一些心得笔记
explore翔
安徽某985小硕,记录日常学习生活,欢迎大家交流指教。
展开
-
C++新特性总结
创建,你可以把 Provider 想象成一个异步任务的提供者,Provider 在某个线程中设置共享状态的值,与该共享状态相关联的 std::future 对象调用 get(通常在另外一个线程中) 获取该值,如果共享状态的标志不为 ready,则调用 std::future::get 会阻塞当前的调用者,直到 Provider 设置了共享状态的值(此时共享状态的标志变为 ready),std::future::get 返回异步任务的值或异常(如果发生了异常)而迭代器的类型有时候比较复杂,书写起来很麻烦;原创 2023-05-08 20:05:58 · 1185 阅读 · 0 评论 -
C++的类、模板等相关知识点
一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);第二,存在二义性问题,通常可以将派生类对象的地址赋值给基类对象,实现的具体方式是,将基类指针指向继承类(继承类有基类的拷贝)中的基类对象的地址,但是多重继承可能存在一个基类的多份拷贝,这就出现了二义性,不知道变量是从哪个基类继承的。友元可以是一个函数,该函数被称为友元函数;原创 2023-05-04 11:59:41 · 729 阅读 · 0 评论 -
类中的构造/析构函数、拷贝构造函数和赋值操作符使用||虚拟继承和虚基类原理
一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);第二,存在二义性问题,通常可以将派生类对象的地址赋值给基类对象,实现的具体方式是,将基类指针指向继承类(继承类有基类的拷贝)中的基类对象的地址,但是多重继承可能存在一个基类的多份拷贝,这就出现了二义性。当一个类的对象向该类的另一个对象赋值时,就会用到该类的赋值函数。原创 2022-12-13 15:34:34 · 270 阅读 · 0 评论 -
C++关键字
普通的成员函数一般都隐含了一个this指针,this指针指向类的对象本身,因为普通成员函数总是具体的属于某个类的具体对象的。但是,由于new调用malloc分配内存,所以一般来说,分配的也就是堆上的,但是operator new是可以重载的,如果改成静态变量等,那么这时二者实际就不一样了。但这样一来,变量已经不再属于函数本身了,不再仅受函数的控制,给程序的维护带来不便。看,其实new里面有一个operator new的函数是封装了malloc的,用来分配内存,然后再调用构造函数初始化,析构函数析构。原创 2022-12-13 10:52:19 · 188 阅读 · 0 评论 -
构造函数和析构函数与虚函数之间的关系
原则:1、构造函数不能为虚函数;2、析构函数需要是虚函数;背景知识;虚函数:虚函数作用是为了实现多态。比如在基类搞一个虚函数(纯虚函数,相当于接口),在不同的派生类中具体实现或重写。为什么要用基类的指针指向派生类对象呢,就是为了实现多态性(向不同对象发送统一消息,不同对象行为不同)。基类的指针指向派生类对象,只能调用从基类中继承的数据,而不能操作派生类中独有的数据,那这不就没有意义吗?所以加了虚函数,只要有虚函数,就不一样了,这样,基类指针的同一函数应用于不同派生类就是不同行为,实现多态。原因:析原创 2022-12-08 10:57:03 · 271 阅读 · 0 评论 -
C++经典内存管理之new,malloc的区别及底层原理
如果经过多次调用,空闲区域七零八碎,找不到符合要求的区域,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大的内存块。所以,C++中,认为new分配的内存是自由存储区,这个不等于堆,堆是操作系统中的概念。如果实在没有空闲内存了,那么会返回NULL指针,所以我们需要判断返回的是否是NULL指针,以此判断是否请求内存成功。并且,free释放的是指针指向的内存空间,指针变量一直存在,直到程序结束。new返回的是指定类型的指针,不需要强制转换;原创 2022-10-26 15:55:57 · 2269 阅读 · 0 评论 -
函数栈的变化过程
比如存储函数参数的值(movl $1 %rbp-4就是将参数1放到栈底的上一个位置,栈是高地址向低地址增长的,所以要减),将值赋给寄存器等。C++调用函数在内存管理中的过程就是新建一个函数栈,里面保存了函数返回值,寄存器的备份(rbp),局部变量,函数参数。1、首先,会将原函数main函数的返回地址压入栈中,返回地址就是add函数下一条指令的地址,因为函数返回后就要执行这个指令了。2、将原函数main的rbp栈底指针入栈,因为函数返回后还要维护main函数的栈底指针,main函数并没有执行完成。原创 2022-09-06 14:44:22 · 1219 阅读 · 0 评论 -
关于类的继承
为每个对象添加一个隐藏成员虚函数表(vtbl)的指针(vptr).虚函数表就是一个存储很多虚函数地址的数组,派生类没有重新定义虚函数时,用的还是基类的虚函数。调用虚函数时,先查看存储在对象中vtbl的地址vptr,然后转向这个表,看看是第几个虚函数,获得地址,前往这个地址的虚函数并执行。虚函数这么好,但是构造函数不能是虚函数:原因是,派生类不继承基类的构造函数,而是调用它,所以没有意义,并且,虚函数实现需要对象中的vptr,而对象创建在构造函数完成之后,矛盾了。析构时先执行派生类的析构函数,再执行基类的。原创 2022-08-23 16:26:40 · 150 阅读 · 0 评论 -
类的基础介绍
C++是一种面向对象的编程。OOP特性:抽象、封装、继承、多态。通过类来实现。本节先介绍抽象和封装。实现一个类要明确的是它的基本数据单元,以及数据的初始化、更新、报告,即一些方法,这些都是现实任务抽象出来的。其实类相比于结构体来说看起来只是多了方法这一部分,但这一部分却能够很好提供封装,因为我们只能通过方法来获取数据。类主要分成类声明(也叫类接口)写在头文件中,而将具体的方法实现代码写在源代码文件中。关于public protected privatepublic一般实现组成类接口的成员函数,这样才能在用原创 2022-06-13 17:14:12 · 115 阅读 · 0 评论 -
变量的内存持续性
C++中存在四种不同的方案存储数据。1.自动存储持续性。这部分主要是函数定义中声明的变量和参数。在函数执行时创建,结束后销毁。通常用栈来实现。压站出站,最后进栈的最先弹出,简化参数传递。2、静态存储持续性。包括在函数外部定义的变量(因为可以被外部文件引用,也称为外部变量)和关键字static定义的变量(只有本文件能用)。一般放在固定的内存区域3.线程存储持续性thread_local 跟线程生命一样长。4动态存储持续性。 new delete 放在堆上如果多个文件都使用一个外部变量,只要其中一原创 2022-06-12 16:56:55 · 110 阅读 · 0 评论 -
函数基础、函数重载和函数模板
使用函数的三部曲:函数原型,函数定义,函数调用。为了大家有个直观的认识,举个例子。#include<stdio.h>double func(int); //函数原型,可以省略具体参数符号int main(){ int x= func(1); //函数调用 return x; }double func(int x) //函数定义{return x*x;}重点关注一下函数原型。为什么需要函数原型?感性认识一下,函数原型的意义大概就是声明func是一个函数,当原创 2022-05-10 15:40:44 · 189 阅读 · 0 评论 -
C++基础之指针
指针赋予了C++语言对内存更大的控制权,用不好也会导致内存泄漏等问题。先过一遍指针的基本知识。首先要理解指针是什么?它是一种特殊的变量(指针就是指针变量的简便表示),和char,float是类似的。不过它存储的是数据的地址。同样指针需要声明和初始化;int * ptr;ptr==&a (a是一个int变量)先看第一句,int* 代表是一个指向int 类型数据的指针变量,double*就是指向double类型数据的指针变量。这里我们需要分辨一个概念,就是指针变量本身大小是固定的,都是四字节(原创 2022-04-24 14:35:14 · 2737 阅读 · 0 评论 -
C++基础之数组和字符串
最近准备开始复习C++基础知识。把一些细碎但重要的知识点记录下来,方便帮助大家以及自己复习。我先从复合类型的数组开始说起。这里指的是简单的一维数组。数组初始化几种方式:int A[4]={1,2,3,4} 对int B[4] 对,可以不赋值C[4]={1,2,3,4} 错 ,只有定义的时候才能初始化A=B 错,不能将一个数组赋给另一个数组float D[5]={1.2,2.3} 对 提供的值可以少于元素数目,其余的会设置为0.当然,STL中提供了更方便的模板类vector,C++11中也有原创 2022-04-22 19:59:04 · 940 阅读 · 0 评论 -
为什么要把成员变量设置为private,non-member,non-friend函数和member函数区别
为什么不能把成员变量设置为public或protected呢。我们给出以下几个原因1、首先,为了语法的一致性,接口的一致性,我们需要把成员变量设置为private。因为我们知道public是可以任意访问的,而private需要通过成员函数来访问,那么我们就可以同一访问成员变量和访问成员函数的形式,统一为成员函数(也就是加小括号)。2、把成员变量设置为private可以实现更加细微的权限控制,比如对某个变量我们实现只读访问,也就是成员函数只有return语句,只写访问,有传参和赋值语句,等等。3、这也是转载 2021-07-25 19:12:26 · 1537 阅读 · 0 评论 -
传值和传引用,返回引用等问题
1.宁以pass-by-reference-to-const替换pass-by-value什么么说传值的方式对于一些情况不好,看个例子:bool f(student s);student p;bool pisok=f§;当我们传值时,调用端s是复制了p,那么,就需要调用一次student的copy构造函数和析构函数,并且,如果student继承自person的话,还得调用person的copy构造函数。那么这么多构造函数实现效率会很低。而pass-by-reference-to-const可以很好原创 2021-07-20 22:03:28 · 222 阅读 · 0 评论 -
new,delete的运用
1、成对使用new,delete要采用相同的形式new,delete都是在堆中动态分配内存内存要用的,需要自己收回资源。new会产生两个动作:1、分配内存,2. 调用构造函数。delete也是两个动作,1、调用析构函数,2. 释放内存。那么,当我们分配数组对象时,如何知道调用多少次构造和析构函数呢?编译器内部会额外分配空间存储对象的数量,当对数组对象调用构造和析构函数时就可以根据这个数量值来进行循环处理了。那么,delete时需要判断是否是数组对象,数组对象的话必须使用[],这样delete就知道原创 2021-07-17 11:01:19 · 550 阅读 · 0 评论 -
以对象管理资源
1、以对象管理资源原创 2021-07-14 10:25:29 · 88 阅读 · 0 评论 -
operator=的一些知识
10、operator=返回一个引用指向操作符=左边的实参。x=y=z=5这种连续赋值的问题,其实采用的是右结合律。先让z=5,然后传递到左边。这个时候,=就必须可以返回一个引用。比如z=5,最右边的等号会返回z的引用,也就是5.当然,也可以不返回引用,返回用z初始化的临时对象,,再执行y= z的临时对象.这样会调用构造函数和析构函数,需要代价。所以,一般最好知道等号会返回一个引用,指向等号左边的实参。2、等号处理自我赋值问题比如:class W(…);W w;w=w;...原创 2021-07-13 10:07:43 · 1264 阅读 · 0 评论 -
互斥量--mutex
Mutex 又称互斥量,C++ 11中与 Mutex 相关的类(包括锁类型)和函数都声明在 头文件中,所以如果你需要使用 std::mutex,就必须包含 头文件。Mutex 系列类(四种)std::mutex,最基本的 Mutex 类。std::recursive_mutex,递归 Mutex 类。std::time_mutex,定时 Mutex 类。std::recursive_timed_mutex,定时递归 Mutex 类。Lock 类(两种)std::lock_guard,与 M转载 2021-05-13 14:11:03 · 165 阅读 · 0 评论 -
构造 析构(2)
1、为多态基类声明virtual析构函数假如我们有一个时间的多态基类,他还有很多派生类,比如原子钟,机械钟,电子钟等等。我们可以用一个基类指针指向一个派生类的对象,之后我们要delete这个指针,然而,我们只删除了基类的那一部分,派生类的部分还存在,这就成为了一个局部销毁的对象,造成资源泄露。我们的办法就是声明一个virtual的基类析构函数。然而并不是所有基类都是为了多态目的,因此不能把不具有多态性质的基类(比如STL,STRING)声明virtual析构函数,为什么呢,因为虚函数会额外增加虚表指针原创 2021-03-18 14:19:27 · 62 阅读 · 0 评论 -
传递数组参数时最好使用地址传递
在把数组作为参数传递给函数时,有值传递(by value)和地址传递(by reference)两种方式。在值传递方式中,在声明和定义函数时,要在数组参数的尾部加上一对方括号([]),调用函数时只需将数组的地址(即数组名)传递给函数。例如:void func(int[]);声明voidfunc(int a[]) 定义{…}int main(){int x[10];func(x);//调用return 0;}在值传递方式中,数组x将被复制一份,复制所得的数组将被存放在栈中,然后由fu原创 2021-03-13 21:45:33 · 4139 阅读 · 0 评论 -
构造/析构/赋值运算(1)
1、了解C++默默编写并创调用的那些函数当我们写出一个类如下:class empty();如果你自己没声明,编译器就会为他声明一个copy构造函数,一个copy assignment操作符和一个析构函数,如果你连构造函数都没声明,那么编译器还会为你声明一个default构造函数,所有这些函数都是public且inline的。也就是说,相当于写下了下面这些代码: class empty{ public: empty(){...} //default构造函数 empty(const empty&原创 2021-03-13 15:32:20 · 62 阅读 · 0 评论 -
C++------确定对象使用前已先被初始化
在C++中,读取为初始化的对象会导致程序直接结束运行,更闹人的是如果读入一些“半随机”Bits,最终会导致不可预测的程序行为,调试也很困难。但是,对象的初始化动作何时一定发生,何时不一定发生,这些规则是很复杂的。比如,array数组内容不一定会初始化,因为会增加运行成本,而STL里面的vector却有保证会初始化。1、所以我们最佳处理办法就是:永远在使用对象之前先初始化它。对于无任何成员的内置类型,我们需要手工初始化:int x=0对于内置类型以外的其他东西,需要用构造函数初始化,确保每一个构造函数原创 2021-03-08 15:40:24 · 325 阅读 · 0 评论 -
数组 指针 引用 动态数组
因为我本身是学过这些的,这一遍是复习和总结,不会面面俱到,希望和大家一起分享。1、数组的创建和初始化1.1、数组是一种完全静态的数据结构,在创建时,数组的维度必须是一个在编译时就可以确定的整形常量表达式(和switch的case后面的表达式那样)。举个例子:int arr1[2]是合法的,const int cnum=4; int arr2[cnum],int arr3[cnum+1],都是合法的,const常量在编译时确定了但是 int num=5;int arr[num],这是不合法的,因为n原创 2021-02-21 12:45:05 · 469 阅读 · 0 评论 -
存储单位、预处理和宏和const、vector和string、定义和声明
@一些基本的、重要的、容易忽视的概念##存储单位关于存储单位,我们需要知道什么是位、字节、字,这样才知道基本数据类型存储空间的意义。首先位(比特)Bit,代表一个二进制数字,0或者1,计算机存储的就是一大串有特殊意义的0和1,这是最小的存储单位。而字节(byte)的大小是8个位,即1byte=8bit,这是固定不变的,为什么要有这个单位呢,因为各种信息在计算机中存储处理至少需要1个字节,比如我们常见的char最小存储单位是一个字节,short和汉字是两个字节。需要注意的是,我们平常说的数据类型占用的存原创 2021-02-08 22:12:30 · 279 阅读 · 0 评论