[杂学笔记]结构体大小的内存对齐规则、继承与组合的区别、New和malloc的区别、define与const的区别、如何保证线程安全、乐观锁与悲观锁
目录
1.结构体大小的内存对齐规则
2.继承与组合的区别
3.New和malloc的区别
4.define与const的区别
5.如何保证线程安全
6.乐观锁与悲观锁
1.结构体大小的内存对齐规则
内存对齐是计算机内存安排数据存储位置的一个策略,他的目的是为了提高数据访问的一个效率,因为计算机的硬件在读取内存的时候,通常是按照4/8字节来读取的,由系统来定,内存对齐可以让数据存储符合硬件的读取要求。
对齐规则:第一个数据成员的地址和结构体起始地址相同。后续的元素,每个元素的存储的起始位置受到两个数值的影响,第一个是该元素字段的大小,第二个是内置的对齐数大小,两个取小的值,作为该元素的对齐数。例如int类型4字节,内置的对齐数是8字节,那么他存储的起始位置必须是4的整数倍,对于char类型1字节,那么就挨着上一个元素存储即可。最后结构体本身的对齐,结构体的总大小必须是其内部最大对齐数的整数倍。
2.继承与组合的区别
A is B是继承关系,是一种类与类之间的层次关系,子类可以复用父类的代码,并且在此基础之上添加新的属性和方法,或者重写重定义父类的内容。这种关系体现"是一个"的语义,即子类对象可以被看做是父类对象的一种特殊类型。优点在于代码的复用、扩展性强可以实现多态。缺点在于耦合度较高,而且继承关系是静态的,一旦确定了就很难改变,所以灵活性较低。
A and B是组合关系,组合/聚合都体现出了"有一个"的语义,代表的是整体与部分的关系。组合在于部分不能离开整体而独立存在,而聚合则没有该限制。在实现上就是一个类包含另一个类的对象作为其成员变量。优点在于低耦合、灵活度高,缺点是代码的复杂度增加。
继承:父类是一个animal动物类,他的子类是猫狗等动物。组合/聚合:一个类是汽车类,另一个类是发动机类,那么汽车类内部就需要包含发动机对象。
3.New和malloc的区别
- new和delete属于C++的关键字运算符,而malloc与free属于是C语言的库函数
- new在申请内存的时候不需要显示的计算空间大小,也不需要将返回值进行强转
- new在申请内存空间的时候,会将对象进行初始化,对于内置类型会进行值得初始化
- new在无法分配空间得时候,会抛出异常,而malloc会直接崩溃
new和delete相当于是malloc和free的封装。在使用new运算符的时候,会调用operator new函数,同时调用对象的构造函数进行初始化处理。delete函数则是调用operator delete函数,并调用析构函数。
4.define与const的区别
define和const都可以用于定义常量,但是define是C的预处理指令,会在预处理阶段进行代码的替换操作,而const属于C/C++的关键字,用于定义常量,存放在对应的字符常量区。
define属于替换没有类型检查、没有作用域的限制,没有实际的内存分配,不会记录到符号表当中,所以在调式的时候看不到宏名称。而const有类型检查、有作用域的限制、会实际的分配内存空间,会将该变量记录到符号表当中,方便调试。
5.如何保证线程安全
最基本的是使用互斥锁来实现线程安全,确保同一时间只有一个线程访问该临界资源,保证了线程的安全,但是这种方式会非常的耗时。一个线程获取锁资源之后,其他资源都必须进行阻塞等待。还有读写锁,在读写操作的时候,读取可以多个线程、写入操作的时候需要只有一个线程进入临界区进行操作,才可以避免数据的二义性问题。
还可以使用原子操作,原子操作就是把一个操作设置为计算机CPU一次指令就可以执行的操作,那么就不会涉及到线程安全的问题了。例如常见的无锁队列,底层就是采用的C++标准库的std::atomic原子操作类进行实现的,一些常见的数据结构都可以涉及为无锁的模式。又例如信号量机制,他可以控制一组资源的访问,底层维护了一个计数器,线程在访问的时候需要先申请信号量资源才可以去临界区进行访问。
6.乐观锁与悲观锁
互斥锁其实就是基于悲观锁的并发控制理念,他很悲观,会认为一定会有其他线程和自己并发访问临界资源造成数据不一致的问题,所以在访问临界区之前就进行加锁操作。相当于避免了一切可能发生的问题。
悲观锁适用于写操作比较频繁的一个场景下,多线程并发修改临界资源的时候,可以更好的保护数据的一致性。
乐观锁则是认为很少有线程能和自己进行并发访问进行修改数据的操作,所以在访问临界资源的时候,不会先加锁,而是直接操作,在最终提交确认修改的时候,会检查数据在操作期间是否被其他线程修改过,没有的话就直接提交成功,如果被修改过的话,就根据具体的策略进行处理,例如重试或者抛异常等,也就是真的遇到问题在去解决。
乐观锁适用于读操作频繁,写操作较少的场景。性能会好一些,但是实现上来说复杂。