C++摘要

初始化与赋值

在C++语言中,初始化与赋值是两个完全不同的操作:初始化是创建一个变量时赋予一个初始值。赋值是把对象的当前值去除,而以一个新的值来代替。
在这里插入图片描述
相应的,如果是定义于函数体外的内置类型没有初始化,则其值为0或者空串。
在这里插入图片描述

运气好程序崩溃,运气不好产生错误结果,很难发现及调试

初始化列表

在这里插入图片描述
构造函数后面冒号(:)的作用是表示后面是初始化列表,有三种使用场景:父类,类成员,由于const成员变量的值无法在构造函数内部初始化,因此只能在变量定义时赋值或使用初始化列表赋值。
注意两点:
1、构造函数列表初始化执行顺序与成员变量在类中声明顺序相同,与初始化列表中语句书写先后无关。
2、相对于在构造函数中赋值,初始化列表执行效率更高。

声明与定义

声明使名字为程序所知,一个文件想使用别处定义的名字则必须要声明,如果想声明而非定义就加extern,不然就是声明并定义;
定义能分配存储空间,也能赋值;
extern声明的时候如果包含初始值就不再是声明而是定义了。

当第一次使用到变量的时候再去定义并初始化它!
局部变量和全局变量可以重复但不应重复!

保护,私有,公有

保护:不可以类外访问,可以被继承(可以被派生类访问),它同private的区别就是在对待派生类的区别上。
私有不可以类外访问,也不能被继承。
公有类外也能访问。
在这里插入图片描述
如果希望基类的公有成员不被派生类的对象所访问,只能通过派生类自己的成员访问,则使用保护继承,否则用public继承。

指针和引用

int& ref=a;//ref就是引用,就是别名,本质是指针常量(指针类型的常量,即地址不变,即引用ref指向不变,指向a,但是ref使用的时候会自动解引用,像是变量a一样。用常量比如10赋值的时候要在最左边加const),编译器将其转化为int* const ref=&a(指向不变,ref的值就是a的地址,所以指向a(但是在使用的时候编译器会解引用就不是a的地址而是a的值了,见下)。
在这里插入图片描述

int* p=a;//指针p的值等于变量a的值(p不指向a,a
取址后才指向a比如int *p=&a,这样的话p就相当于低等的引用)

形参是引用的时候,对形参修改会改变实参,所以要对该形参左边加const防止误操作
在这里插入图片描述
该person型成员函数在person类中,传入person类对象p时,形参Person &p=p;即把对象p取了别名p。另外+操作只需要返回值就行了。

在这里插入图片描述
返回的是值的话,第一次递增后返回的是新的对象,即第二次操作的是新的对象,所以第一次输出的是新的对象

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
引用本身不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用,
AObject为指针的引用,是一个引用,传引用可以让传入的指针也为NULL(而非值传递),为什么不delete AObject再AObject=NULL?好像一样

this指针

在这里插入图片描述

句柄和文件描述符

一个是windows中的一个是Linux中的,用来操作内核对象,同时隐藏了内核对象的首地址,更加安全

智能指针

智能指针实际上是一个栈对象,并非指针,该栈对象将堆内存的生命周期帮到一起了。
在这里插入图片描述
string是std中的类,str是指向对象的指针。它指向的string类的对象,通过构造函数返回了一个指向堆区内存的指针,以作为构造函数(显式调用)的参数。即str这个指向对象的指针,通过构造函数传入指针,指向了堆区内存。
对象str在函数结束时会释放,但是所绑定的堆内存却没有析构函数来释放,而unique_ptr则可以。
operate*已重载,会对绑定的指向堆内存的指针进行解引用。
在这里插入图片描述
通过对象函数跨函数转移堆内存的所有权。
在这里插入图片描述
通过类模板显式声明一个对象ps(该对象封装了指针),该指针指向堆区的str字符串(string是std中的类,str是字符串)

虚函数和纯虚函数

virtual void foo(){}
virtual void foo()=0{}

  1. 定义一个函数为虚函数,这个函数是必须实现的,哪怕是空实现,它的作用就是为了能让这个函数在它的子类里面可以被覆盖。而子类中的virtual可写可不写。
  2. 虚函数在子类里面也可以不重载的;但纯虚函数必须在子类去实现。通常把很多函数加上virtual,是一个好的习惯,虽然牺牲了一些性能,但是增加了面向对象的多态性,因为很难预料到父类里面的这个函数不在子类里面不去修改它的实现
  3. 定义一个函数为纯虚函数,才代表函数没有被实现。
    定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。
  4. 带纯虚函数的类叫虚基类,这种基类不能直接生成对象,而只有被继承,并重写其虚函数后,才能使用。这样的类也叫抽象类。

虚析构函数

只有当一个类被用来作为基类的时候,才把析构函数写成虚函数

函数模板与类模板

在这里插入图片描述
在这里插入图片描述
声明的函数用到了通用数据类型就是函数模板;声明的类用到了通用数据类型就是类模板。

仿函数

仿函数(Functor)又称为函数对象(Function Object)是一个能行使函数功能的类。仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载 operator() 运算符。因为调用仿函数,实际上就是通过类对象调用重载后的 operator() 运算符。
在这里插入图片描述
在这里插入图片描述
注意Say()是临时对象,可以Say()(a,b)进行调用,也可以先定义一个对象s,然后s(a,b)调用
在这里插入图片描述

常用迭代器

在这里插入图片描述

继承中构造和析构顺序

总结:继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反

注册回调与回调

注意:内核提供了注册回调函数接口(接口指内核已经实现了功能,即捕捉到SIG的时候回调应用层的回调函数),而应用层只需要指定内核产生的哪个信号,并传入不同回调函数就可实现不同功能,我们可以写很多,然后传入某个即可。注册回调函数不仅调用了回调函数,还可以为调用加上一些条件
而中断的服务函数,已经在startup文件注册好了(在注册的地方通常也写了服务函数的实现),在中断发生时直接调用(已使能),并没有向我们提供注册接口,不过它week导出,所以我们能修改同名函数的具体内容来实现功能

类的静态成员函数

类的静态成员函数和普通函数的函数指针的区别在于,他们是不依赖于具体对象的,所有实例化的对象都共享同一个静态成员,所以静态成员也没有this指针的概念。

VScode的跳转

能直接跳转到子类或进行选择的,则必为纯虚函数。若跳转后还是子类且不能选择,则要注意可能是虚函数被子类重写

在一个类中用另一个类的方法

1.在类中声明另一个类的指针,Init的时候new创造实例,不同的类对象有其他的类对象。
2.直接在Init的时候用另一个类的方法CreateInstance进行构造,但是这样不同的类对象不能重复构造,只能构造一个,因为只有一个全局静态指针。

函数名前加const,函数名后加const

名字前:返回的指针不能修改
名字后:不允许通过this修改成员变量的值

暂无标题

父类指针指向子类的时候,如果所有子类都一定要实现,则声明为纯虚。如果不是虚函数,则跑不了子类的非虚函数。
基类里面通常会创建线程,跑整个流程,子类则可以重写以实现不同的业务。
拿到一个程序,先看整体框架流程,具体某个函数实现,从想要的结果往回看。
在计算机中,以小段存储,低地址权值低,大端模式符合人的习惯(因为从左往右左边认为是低地址),网络字节序要求发送的第一个字节(低地址)是权值高的,所以要以大端模式发送。
定义指针却不初始化,则为野指针会导致内存崩溃
野指针的地址是非法的,NULL为空指针,可用来初始化但不可以访问。

char *p=“Hello World”;
你这里只定义了指针变量,编译器就为指针变量开辟了空间。而没有空间来存放"Hello World",所以只能存放在静态区。
如果
char pi[]=“Hello World”;
char *p = pi;
此时"Hello World"就存放于pi所指向的数组里 指针p所指向的就不是静态区的了。

如果将成员函数内的某个局部变量定义为静态变量,该类的所有对象在调用这个成员函数时将共享这个变量。类的静态成员变量实际上只有一个,它不随着类的实例的创建/销毁而增加/减少。它不是类的真正成员,并不是类的一部分。(就想全局变量一样)

vector容器是不能够通过下标操作添加元素的。原因是必须是已经存在的元素才能够用下标操作符进行索引,否则,通过下标操作进行赋值,是不会添加任何元素的。

BCD是用4位二进制表示一位十进制,也就是说用一位十六进制表示一位十进制,电话号码用字符串传输长度比BCD大一倍

由于大小端的关系,可以分为用于上报的结构体(放大端数据),和无需上报的没有内容要求的结构体(放小端数据)

public主要是对象被别的对象调用了,或是回调

类的非静态成员函数yy()中定义了一个静态变量,那么它的不同的实例也是公用一个存储空间

new和malloc

new和malloc的区别
都在堆区开辟空间,若堆区空间不足失败,malloc返回一个空指针,new抛出异常throw bad_alloc
malloc是函数,只是开辟空间,但无对象,只有一个动作。
new是标识符,关键字,运算符(可以重写)申请开辟空间,调动构造函数构建对象,两个动作。

delete和free
free释放空间
delete调动指针指向的对象的析构函数,然后释放空间,注销对象

倘若我们不给指针变量给他赋值,而是直接操作这个指针变量,编译器会报错的,因为编译器检测到该指针变量没有初始化,就会认为这是语法错误。所以,使用初始化后的指针变量才是有意义的。

自定义结构体数据较大或在运行时才能确定大小的数据放在堆区,new 出来的都要delete。

delete 一次以后,p成了野指针,它作为地址的值还是有效地没还可以访问它以前指向的内存,不过那片内存被重新格式化了;

delete 一次以后,不能再次delete,否则会报错;再free一次时,由于该指针已经没有堆空间与之对应了,所以再次编译器将会提示出错(注意成为了野指针,不再指向堆空间,所以可以赋值为NULL)。但是,这片被格式化了的空间的值不一定是NULL

头文件在当前目录用"",否则"“和<>都行。其中,”"需要指明路径,<>用于库的
在这里插入图片描述

long long赋值给char,则大于char的值会溢出。

static数据成员在类外定义和初始化是为了保证只被定义和初始化一次,static const int可以在类里面初始化,是因为它既然是const的,那程序就不会再去试图初始化了。

有符号数char如0xff在参与运算会自动当作0xffffffff
%d是当作int打印;0xffffffff有符号直接赋值-1,无符号则溢出,强转为-1

在这里插入图片描述

字节对齐

  • 在处理数据校验长度(基本都设置为1),指针操作,节省空间时用到

#pragma pack (n) 作用:C编译器将按照n个字节对齐。
#pragma pack () 作用:取消自定义字节对齐方式。

#pragma pack (push,1) 作用:是指把原来对齐方式设置压栈,并设新的对齐方式设置为一个字节对齐

#pragma pack(pop) 作用:恢复对齐状态

因此可见,加入push和pop可以使对齐恢复到原来状态,而不是编译器默认,可以说后者更优,但是很多时候两者差别不大

如:

#pragma pack(push) //保存对齐状态

#pragma pack(4)//设定为4字节对齐

相当于 #pragma pack (push,4)

__attribute__关键字主要是用来在函数或数据声明中设置其属性。给函数赋给属性的主要目的在于让编译器进行优化。
attribute ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,是GCC特有的语法。这个功能是跟操作系统没关系,跟编译器有关,gcc编译器不是紧凑模式的

应用层和底层交互方式

1.进程确定一些肯定要用到的函数,通过服务函数接口传递给插件(应用层确定肯定要用到的函数给底层就不行)
2.应用层可以得到底层的服务函数接口,里面有底层的一个个注册函数,然后应用层就可以方便调用各注册函数!(比较合理)
3.底层传出运行状态,让应用层根据状态做决策(不一定好使)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值