3. C++ Expert
Expert C++
也称为成员指针,不是this指针!一般地,指向成员的指针定义语法如下:
// 指向数据成员的语法type ClassName::*your_pointer_name = &ClassName::member;// 指向成员函数的语法type (ClassName::*your_pointer_name)(parameters list) [const] = &ClassName::method;
我们可以看到这里的语法设置的合理性,既然是类的,那就加个类解析符即可。使用成员指针有几点要注意:
1)成员指针的使用不能违背类的访问原则,即其只能访问类的公有成员部分;
2)当成员函数为 const 时,成员指针的定义后面也要加上const;
3)成员指针其本身相当于一个变量,需要存储空间(但有些特殊,后续会再探讨);
4)成员指针不能作算术运算(暂时的实验结果);
5)成员指针必须通过对象来访问,访问的运算符有 .* 和 ->*;
6)访问成员函数时,整体需要用括号括起来,即 (object.*pfunc)() 或 (object->*pfunc)();
7)当访问重载函数时,语法形式不变,编译器会帮我们自动选择。
A a;int A::*pData = &A::a;a.*pData = 1; // 等同于 a.a=1pData = &A::b;a.*pData = 2; // 等同于 a.b=2void (A::*pFunc)() = &A::func;(a.*pFunc)(); // 等同于 a.func()A* p = new A; // 对指针类型对象来说p->*pData = 3; // 访问成员指针需要用->(p->*pFunc)();
成员指针有点特殊,它不是一般的指针。对于指向数据的成员指针,其大小和一般指针一样,但其含义不太一样。对于指向成员函数的成员指针,在32位平台上它占8个字节,而在64位平台上占16个字节,这个我们可以通过 sizeof 很容易测得。
成员指针到底有何不同?在 Itanium C++ ABI 里有这样一段解释:
一个指向数据成员的指针,它的类型是 ptrdiff_t,它里面存储的是该数据成员相对对象在内存中地址的偏移,其大小和对齐属性和 ptrdiff_t 一样(8字节)。-1表示是空指针。
一个指向成员函数的指针拥有两个域(16字节),其含义如下:
- ptr: 对于非虚函数,这个域是个简单的函数指针(在当前基本的 Itanium psABI 约定中,这个指针存储函数的地址)。对于虚函数,其值是 1 加上该函数在虚函数表中的偏移(以字节为单位),其类型也是 ptrdiff_t。0 表示空指针,它和下面的 用于调整的域 adj 的值无关。
- adj: 这个域是用来调整 this 指针的,其类型也是 ptrdiff_t。
ptrdiff_t 在内部的定义如下:typedef long int ptrdiff_t;
likely/unlikely
原子操作的来源是因为代码语句可能会拆成几条CPU指令执行,在多线程时带来不确定性,而引入不可分割的原子操作就能保证结果确定
在C++11的中有两种做法: 目前只提供了基本类型的原子操作
1. 模拟, 比如说对于一个atomic类型,我们可以给他附带一个mutex,操作时lock/unlock一下,这种在多线程下进行访问,必然会导致线程阻塞; 2. 有相应的CPU层级的对应,这就是一个标准的lock-free类型;
使用原子操作模拟互斥锁的行为就是自旋锁,互斥锁状态是由操作系统控制的,自旋锁的状态是程序员自己控制的;要搞清楚自旋锁我们首先要搞清楚自旋锁模型,常用的自旋锁模型有:
2. CAS, Compare-and-swap,对应atomic的compare_exchange_strong 和 compare_exchange_weak,这两个版本的区别是:Weak版本如果数据符合条件被修改,其也可能返回false,就好像不符合修改状态一致;而Strong版本不会有这个问题,但在某些平台上Strong版本比Weak版本慢 [注:在x86平台我没发现他们之间有任何性能差距];绝大多数情况下,我们应该优先选择使用Strong版本;
我针对这两种模型分别实现了两个版本的自旋锁,最终代码可以在性能测试章节中找到,这里我们要注意以下问题:
LOCK时自旋锁是自己轮询状态,如果不引入中断机制,会有大量计算资源浪费到轮询本身上;常用的做法是使用yield切换到其他线程执行,或直接使用sleep暂停当前线程.
无锁编程
如果看了CAS实现的自旋锁代码会发现其有些别扭:每次都需要去重置exp的状态为false;CAS虽然也能实现自旋锁,但通常被我们用来进行无锁编程; 什么是无锁编程呢,让我们以一个例子开始: