【无标题】学习笔记

总结
1、vs中添加头部作者等信息在C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\ItemTemplates\CSharp\Code\2052下找到Class和interface文件夹,分别对应头部注释和接口注释,找到文件中的.cs文件在里面添加相应的注释信息,即可在创建文件时生成这些信息(修改信息需要设置code文件夹的权限问题)
2、 #ifndef CONNECTIONGEOMETRY_H
#define CONNECTIONGEOMETRY_H
#endif
作用:防止头文件被重复引用,会提高编译的效率
ifndef xxxx 如果这个xxxx没有引用,define xxxx则引用这个头文件 endif否则不需要引用
#ifndef<标识>后面的标识理论上是命名自由的,一般是头文件名大写,前面加下划线,将.也变成下划线
3、c++中的私有和公有成员
公有成员在任何地方都可以访问,私有成员在类外不能直接访问,只能在class{}中访问。实例化一个对象可以直接使用(对象.公有成员)来访问公有成员;如果访问私有成员只有通过公有成员当做媒介来访问(对象.公有函数(),公有函数返回私有成员值)
4、c++类中的this指针
(1)在创建对象时,系统会为每个对象分配独立的存储空间,同一个类定义n个对象,则有n组同样大小的空间以存放对象中的数据成员,而成员函数在内存中只有一份代码段,同一个类的不同对象在调用自己的成员函数时,都是调用的同一份函数代码,this指针用来保证对象调用成员函数对数据成员处理是针对自己的数据成员,不是其他对象的数据成员。
(2)不管成员函数在类内定义还是类外定义,内存中都只有一份代码段,每个成员函数都有一个this指针,它的值是当前被调用的成员函数所在的对象的起始地址。在调用成员函数时,系统隐式的将对象的起始地址传递给成员函数,使this指针得到当前对象的地址。成员函数对数据成员的引用,就是按照this的指向找到对象的数据成员,对该数据成员操作。
(3)成员函数中数据成员名前面隐含有“this->”的指向
5、静态成员(成员前面加static)(静态数据成员(变量),静态成员函数)
(1)静态数据成员被类的所有对象共享,静态数据成员在内存中也只占一份空间,静态数据成员的值对所有对象都是一样的。静态数据成员不属于某个对象,在为对象分配的空间中不包括静态数据成员所占的空间。静态数据成员在所有对象之外单独开辟空间,在类中定义了静态数据成员,即使不定义对象,也为静态数据成员分配空间。静态成员变量存放全局区。
(2)静态数据成员的定义只能在类外进行初始化,静态数据成员在程序编译时被分配空间,在程序结束时才释放空间。(静态数据成员在类中只是声明,在类外叫定义,不能叫初始化,定义是分配内存,初始化是赋初值。在类中的声明不在类外定义,编译时并不会分配内存,只有定义之后编译才会分配内存。)
(3)(静态数据成员属于整个类,不属于某个对象,如果在类中初始化,会导致每个对象都包含该静态数据成员,是矛盾的。)
(4)静态成员函数没有之前说的隐藏的this指针,静态成员函数可以通过类名直接访问,普通成员函数不能通过类名直接访问,需要创建对象来访问。静态成员函数也可以通过对象访问,静态成员函数只能访问静态成员变量和静态成员函数。
6、静态变量和全局变量
(1)全局变量的作用域是整个项目,在一个源文件中定义就可以作用于所有源文件,其他不包含全局变量定义的文件需要使用extern关键字再次声明这个全局变量。
(2)全局变量,静态全局变量,静态局部变量都是在静态存储区(全局数据区)中分配空间的,而局部变量是在栈上分配空间的。
(3)全局变量和静态变量生命周期和程序生命周期一样,在程序结束后操作系统回收空间。
(4)全局变量的作用域是整个项目,而静态全局变量是当前程序文件,静态局部变量是当前函数体内。全局变量和静态全局变量、静态局部变量默认值都是0,也就是在没有初始化变量值的时候默认为0。
(5)静态局部变量具有局部作用域只对定义自己的函数可见,只被初始化一次,自从初始化一次之后直到程序运行期间都在。静态全局变量具有全局作用域作用于定义它的程序文件但是不能作用于项目中的其它文件,而全局变量可以,静态全局变量可以被多次初始化。
int Fun(void){
//声明一个静态局部变量
static int num = 0;
++num;
return num;
}
for(int i = 0; i < 10; i++){
cout<<Fun()<<" ";
}
/*
输出结果是
1 2 3 4 5 6 7 8 9 10
只被初始化一次,静态局部变量。
7、extern关键字
(1)变量
extern int a;//声明一个全局变量a
int a;//定义一个全局变量a
extern int a=0;//定义一个全局变量a并给初值
int a =0;//定义一个全局变量a并给初值
第三个等于第四个,都是定义一个可以被外部使用的全局变量,并给初值。但是定义只能出现一处,不管是int a;还是extern int a=0;还是int a=0;都只能出现一次,而extern int a可以出现很多次。
当你要引用一个全局变量的时候,你就要声明,extern int a;这时候extern不能省略,因为省略了就变成int a;这是一个定义不是声明。
(2)函数
int fun(void)
{
return 0;
}//定义了一个全局函数,有函数体,extern被省略
int fun(void);//声明函数 省略了extern,完整写是extern int fun(void);
定义的时候用extern,说明这个函数是可以被外部引用的,声明的时候用extern说明这是一个声明。定义函数要有函数体,声明函数没有函数体,所有函数定义和声明时可以将extern省略。
对变量而言,变量的声明有两种情况:
一种是需要建立存储空间的,不用加extern;
另一种是不需要建立存储空间,需要加extern 。
如果你想在本源文件中使用另一个源文件的变量,就需要在使用前用extern声明该变量,或者在头文件中用extern声明该变量;
对函数而言,如果你想在本源文件中使用另一个源文件的函数,就需要在使用前声明该函数,声明函数加不加extern都没关系,所以在头文件中函数可以不用加extern。
8、动态绑定
c++中的动态绑定通过虚函数实现,虚函数是通过一张虚函数表(virtualtable)实现的,拥有虚函数的类在实例化时会创建一张虚函数表。这个表中记录了虚函数的入口地址,当派生类对虚函数进行重写时,虚函数表中相关虚函数的地址就会被替换,以保证动态绑定时能够根据对象的实际类型调用正确的函数。
9、=default,=delete
加上=default之后,编译器会给我们默认生成函数体,减轻工作量
在函数名称后面加上=delete,可以使我们禁用一些不需要编译器生成的默认函数,还可以避免因为数据类型原因导致的错误的函数调用。
10、函数后加const(这个函数不修改对象内部状态)
(1)因为常常有些函数不改变数据成员,也就是说这些函数是“只读”函数,而一些函数要修改类数据成员的值,如果把不改变数据成员的函数都加上const关键字标识,提高了代码的可读性,并且提高了代码的可靠性。已经定义了const的成员函数,一旦企图修改数据成员的值,编译器就会报错。
(2)加了const的成员函数可以被非const对象和const对象调用,但不加const的成员函数只能被非const对象调用。
11、mutable(这个成员变量不算对象内部状态)
比如,你搞了个变量,用来统计某个对象的访问次数。它变成什么显然并不影响对象功能,但编译器并不知道,它仍然会阻止一个声明为const的函数修改这个变量。
把这个计数变量声明为mutable,编译器就明白了,这个变量不算对象内部状态,修改它并不影响const语义,所以就不需要禁止const函数修改它了。
12、Qt中的事件
(1)事件(event)是由系统或者Qt本身在不同的时刻发出的
(2)事件和信号槽的区别:
信号由具体的对象发出,然后会马上交给由connect()函数连接的槽进行处理;
而对于事件,Qt使用一个事件队列对所有发出的事件进行维护,当新的事件产生时,会被追加到事件队列的尾部。前一个事件完成后,取出后面的事件进行处理。但是,必要的时候,Qt的事件也可以不进入事件队列,而是直接处理。
所以说,信号一旦发出,对应的槽函数一定会被执行。但是,事件则可以使用“事件过滤器”进行过滤,对于有些事件进行额外的处理,另外的事件则不关心。
13、override
override关键字用于重载一个虚函数,用法和Q_DECL_OVERRIDE类似。如果重载的虚函数没有任何的重载操作,编译器会报错。
14、lambda
(1)[captures] (params) specifiers exception -> ret {body}
[captures] —— 捕获列表,它用于捕获当前函数作用域的零个或多个变量,变量之间用逗号分隔。
{params} —— 可选参数列表,其语法与普通函数参数列表一致。如果不需要参数,则可以忽略此项。
specifiers —— 可选限定符,可选值为mutable。其意义是可以在函数体内修改按值捕获的变量。
exception —— 可选异常说明符,可以使用noexcept来指明lambda是否会抛出异常。
ret —— 可选返回值类型,lambda使用返回类型后置的语法来表示返回类型。如果没有返回值,则可忽略此部分。如果不指定返回类型,则编译器会根据代码实现为函数推导一个返回类型。
{body} —— 表达式的函数体,此部分与实现普通函数体一致。
(2)关于lambda表达式的作用域,有四个重点:
第一,捕获列表的变量有两个作用域,一是lambda定义的函数作用域,二是表达式函数体所在代码的作用域;
第二,在表达式函数体内,默认情况下,被捕获变量是只读属性。如需修改,则要添加mutable标识;
第三,在表达式函数体内,修改被捕获变量的值,不影响原始变量的值;
第四,被捕获变量必须是非静态局部变量。
(3)捕获列表支持三种特殊情况,下面分别说明:
[this] —— 捕获this指针,可以在类内的lambda表达式函数体内使用,以访问成员变量和方法;
[=] —— 捕获当前作用域内全部变量的值,包括this。当变量较多时,可使用此符号;
[&] —— 捕获当前作用域内所有变量的引用,包括this。
15、typedef
(1)定义一种类型的别名
注意typedef并不是简单的宏替换
在这里插入图片描述
在这里插入图片描述

(2)定义struct结构体别名
在这里插入图片描述
格式:struct 结构体名 对象名;

格式:别名 对象名; POINT是结构体别名
在这里插入图片描述

(3)用typedef来定义与平台无关的类型
比如,某一平台支持int类型,而不支持long类型。
则可以使用typedef定义一个支持类型的别名,程序中使用该别名声明变量。
这样,我们使用极小的修改,typedef int REAL; 就可以实现类型的平台无关性。
(4)为复杂的声明定义一个简单的别名
typedef int (*A) (char, char);
A是我们定义的别名,表示的是一个指向函数的指针,该函数有两个char类型的参数,返回一个int类型的值。则A类型的对象可以指向任何符合上述规则的函数。
(5)注意事项
在这里插入图片描述

p2是我们定义的别名,而不是系统固有类型,编译器在编译时,会认为p2是常量,不可修改
16、std::unique_ptr
(1)std::unique_ptr是所谓的智能指针的一种,主要目的是为了解决原生指针安全性不足的弊端。std::unique_ptr<类型> 变量名称{};
c++14之前的初始化:std::unique_ptr intPtrB{ new int{ 15 } };
c++14之前的初始化:std::unique_ptr ptrA{ std::make_unique (5)};
(2)智能指针和普通指针的区别
1.普通指针可以使用p[0]访问,智能指针不能使用p[0]访问
2.std::unique_ptr指针具有唯一性;当智能指针A指向一块内存时,其余的智能指针就不能再指向这块内存;
3.普通指针释放delete[] a;只释放了内存,没有释放指针a=nullptr;智能指针p.reset(); 即释放了内存,也释放了指针;
4.std::unique_ptr指针因为具有唯一性,因此不能被复制,但是可以转移。转移后,原先的智能指针会被设置为nullptr。转移后的指针变量 = std::move(转移前的指针变量);
17、MIME数据
可以把mime数据看做是描述文件本身的数据
以图片为例,一般有很多办法去实现识别图片类型,但是维护起来困难
在这里插入图片描述

使用QMimeType中的MIME可以表示一个文件或数据类型
18、std::shared_ptr
(1)基本原理:就是记录对象被引用的次数,当引用次数为0的时候,也就是最后一个指向该对象的共享指针析构的时候,共享指针的析构函数就把指向的内存区域释放掉。它所指向的资源具有共享性,即多个shared_ptr可以指向同一份资源,并在内部使用引用计数机制来实现这一点。
(2)初始化:
1、int *aa = new int(10);shared_ptrp1(aa);
2、std::shared_ptr foo = std::make_shared (10);
(3)使用陷进:
1、不能直接将其它指针赋值给shared_ptr
shared_ptr p1 = new int(10); //不能隐式转换,类型不匹配
2、切记避免使用独立两个独立的shared_ptr来存储同一个指针
// 由于p1和p2是两个不同对象,但是管理的是同一个指针,这样容易造成空悬指针,比如p1已经将aa delete了,这时候p2里边的aa就是空悬指针了
int *aa = new int(10)
shared_ptr p1(aa);
shared_ptr p2(aa);
3、shared_ptr作为被保护的对象的成员时, 小心因循环引用造成无法释放资源
例如testA中有一个shared_ptr保护的testB成员,testB中又有一个shared_ptr保护的A成员,当他们各自调用自己的成员函数时就造成循环引用,这样他们的析构函数就无法执行了。
4、多线程环境中使用共享指针的代价非常大,为保证线程安全需要加锁,要考虑因为 share_ptr维护引用计数而造成的上下文切换开销
5、shared_ptr有默认的删除器,但是只负责删除new出来的内容,管理的资源不是new分配的内存,则需要自己定义一个删除器,另外也不支持数组内存回收(默认删除器只是简单的delete xx)
6、不要不加思考地把指针替换为shared_ptr来防止内存泄漏,shared_ptr并不是万能的,使用它们是需要一定的开销的
19、explicit
explicit关键字作用于单个参数的构造函数,如果构造函数有多个参数,但是从第二个参数开始,如果各参数均有默认赋值,也可以应用explicit关键字防止隐式转换。
Class A;类中有初始化int m=1;
A a; a.getm();返回1;
接着写a=2;a.getm();返回2;就是进行了隐式转换
防止调用函数时,发生隐式转换
20、c++头文件中include和class类名的区别
假设有个Date类:
在这里插入图片描述

有个Task类的定义要用到Date类,有两种写法
1、
在这里插入图片描述
2、
在这里插入图片描述

第一种采用了前置声明,第二种采用include加入定义,两种都能通过编译,但是第一种写法更好,假如Date类中的私有成员变量发生改变,第一种方式不需要重新编译,第二种方式需要重新编译,如果第二种与其他头文件有依赖关系,就会引发一连串的重新编译,而且第二种include会容易出现重复include问题,所以最好在.cpp文件中include头文件,在.h中前置声明类。
但是有些情况是不能用前置声明代替#include,如下
在这里插入图片描述

会编译错误,因为在Date d定义了一个Date类型变量,编译器为d分配内存空间的时候必须要知道d的大小,必须要包含定义Date类的头文件。这时可以采用指针来代替:
在这里插入图片描述

指针的大小是固定的,在32位机上是4字节,在64位机上是8字节
21、什么是序列化,为什么要用序列化?
(1)序列化(Serialization):将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
(2)当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需把这个对象转换为字节序列,才能在网络上传送;接收方则需把字节序列再恢复为对象。把对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为对象的过程称为对象的反序列化。因此,序列化的目的就是为了跨进程传递格式化数据
(3)什么情况下需要序列化?
通过上面我想你已经知道了凡是需要进行“跨平台存储”和”网络传输”的数据,都需要进行序列化。
本质上存储和网络传输都需要经过把一个对象状态保存成一种跨平台识别的字节格式,然后其他的平台才可以通过字节信息解析还原对象信息。
(4)序列化的方式
序列化只是一种拆装组装对象的规则,那么这种规则肯定也可能有多种多样,比如现在常见的序列化方式有:
JDK(不支持跨语言)、JSON、XML、Hessian、Kryo(不支持跨语言)、Thrift、Protostuff、FST(不支持跨语言)
(5)序列化技术选型的几个关键点
序列化协议各有千秋,不能简单的说一种序列化协议是最好的,只能从你的当时环境下去选择最适合你们的序列化协议,如果你要为你的公司项目进行序列化技术的选型,那么主要从以下几个因素。
1、协议是否支持跨平台
如果你们公司有好多种语言进行混合开发,那么就肯定不适合用有语言局限性的序列化协议,要不然你JDK序列化出来的格式,其他语言并没法支持。
2、序列化的速度
如果序列化的频率非常高,那么选择序列化速度快的协议会为你的系统性能提升不少。
3、序列化出来的大小
如果频繁的在网络中传输的数据那就需要数据越小越好,小的数据传输快,也不占带宽,也能整体提升系统的性能。
22、大端和小端
(1)高字节和低字节
举个例子,1个int的整数123456789
二进制表示:0000 0111 0101 1011 1100 1101 0001 0101
十六进制表示: 07 5B CD 15
按照从右向左的方向,二进制中,0101是低字节,最左边的0000是高字节;十六进制中,15是低字节,07是高字节
(2)高地址和低地址
假设将int整型在内存中的起始地址(首个字节存储位置)为0x1000,那么另外三个字节就存储在0x1001~0x1003
低位————————————————》高位
0x1000 0x1001 0x1002 0x1003
0x1000就是低地址,0x1003就是高地址
(3)大端和小端
大端:高字节存放在低地址,低字节存放在高地址
小端:低字节存放在低地址,高字节存放在高地址
23、char和varchar区别
Char长度不可变,varchar长度可变
定义一个char[10]和varchar[10],如果存进去的是‘abcd’,那么char所占的长度依旧是10,除了‘abcd’外,后面跟6个空格,而varchar就立马把长度变成4,取数据的时候,char类型的要用trim()去掉多余的空格,而varchar不需要。
Char的存储速度比varchar要快很多,因为长度固定,方便程序的存储与查找,但是char也付出了空间的代价。
Char的存储方式对英文字符(ASCll)占用1个字节,对一个汉字占用两个字节,而varchar的存储方式是对每个英文字符占用2个字节,汉字也占用2个字节,两者的存储数据都非unicode的字符数据
24、int16,int24,int32
Int16=short;int24=int;int32=long

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值