Objc源码中的C++知识点

最近看objc源码,有一些C++的知识点,对我这样的小白有点困难,因此整理了一份使用到的c++知识点,供自己查阅,也给有同样困难的同学一点帮助。
内容来自各种百度,大部分引用都在,可以去看看原文,会更全面一些
~ 不定期补充~

线程局部存储(Thread Local Storage,TLS)

位置:

//NSObject.mm
static inline AutoreleasePoolPage *hotPage() 
{
   AutoreleasePoolPage *result = (AutoreleasePoolPage *)
       tls_get_direct(key);//定义在下面
   if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
   if (result) result->fastcheck();
   return result;
}
//objc-os.h
static inline void *tls_get_direct(tls_key_t k)
{ 
   ASSERT(is_valid_direct_key(k));

   if (_pthread_has_direct_tsd()) {
       return _pthread_getspecific_direct(k);
  } else {
       return pthread_getspecific(k);
  }
}

功能:value的副本存储于一数据结构中,并将其与调用线程以及key相关联;它主要是为了避免多个线程同时访存同一全局变量或者静态变量时所导致的冲突,尤其是多个线程同时需要修改这一变量时。为每一个使用该全局变量的线程都提供一个变量值的副本,每一个线程均可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。而从全局变量的角度上来看,就好像一个全局变量被克隆成了多份副本,而每一份副本都可以被一个线程独立地改变。

SideTables

// 定义SideTablesMap,是一个静态全局变量,一个ExplicitInit的对象,ExplicitInit中保存了_storage[StripedMap]数组
// sizeof(SideTablesMap) = 4096
static objc::ExplicitInit> SideTablesMap;

static StripedMap& SideTables() {
  // 从全局变量中获取到_storage数组,所以叫SideTables没毛病~
  // c和c++中,数组和指针的界限并不明显,数组的0,其实就是指向数组的指针。
    return SideTablesMap.get();
}

new

new其实就是告诉计算机开辟一段新的空间,但是和一般的声明不同的是,new开辟的空间在堆上,而一般声明的变量存放在栈上。通常来说,当在局部函数中new出一段新的空间,该段空间在局部函数调用结束后仍然能够使用,可以用来向主函数传递参数。另外需要注意的是,new的使用格式,new出来的是一段空间的首地址。

引用&

位置:

// objc-weak.mm
weak_referrer_t &ref = entry->referrers[index];
 ref = new_referrer;
 entry->num_refs++;

功能:引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。

【例1】:int a; int &ra=a; //定义引用ra,它是变量a的引用,即别名

声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&ra与&a相等。

constexpr

为了使函数获取编译时计算的能力,你必须指定constexpr关键字到这个函数。

  constexpr int multiply (int x, int y) {    return x * y; } // 将在编译时计算 const int val = multiply( 10, 10 ); 

c++强制转换

c++除了能使用c语言的强制类型转换外,还新增了四种强制类型转换:static_cast、dynamic_cast、const_cast、reinterpret_cast,主要运用于继承关系类间的强制转化;

  • static_cast相当于传统的C语言里的强制转换,该运算符把expression转换为new_type类型,用来强迫隐式转换,例如non-const对象转为const对象,编译时检查,用于非多态的转换,可以转换指针及其他,但没有运行时类型检查来保证转换的安全性

  • dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换(cross cast)。在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的.在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。dynamic_cast是唯一无法由旧式语法执行的动作,也是唯一可能耗费重大运行成本的转型动作。

  • const_cast,用于修改类型的const或volatile属性。

  • reinterpret_cast new_type必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,再把该整数转换成原类型的指针,还可以得到原先的指针值).reinterpret_cast意图执行低级转型,实际动作(及结果)可能取决于编辑器,这也就表示它不可移植

weak_entry_t->mask的可能的值(3 7 15 31 63 127 255 511 1023 2047 ...), 最大数量是 weak_entry_t->mask+1,

ExplicitInit

参照代码结构,定义了一个实例变量_storage[sizeof(Type)] , 存储模板类型。

//我们不能使用C++静态初始化器初始化某些全局变量,因为libc在C++初始化器运行之前调用我们。我们也不想要一个全局指针由于额外的间接目标指向某些全局变量的指针。
//
//明确地/懒散地用操作硬件的方式完成它。
template 
class ExplicitInit {
   alignas(Type) uint8_t _storage[sizeof(Type)];

public:
   template 
   void init(Ts &&... Args) {
       new (_storage) Type(std::forward(Args)...);
  }

   Type &get() {
       return *reinterpret_cast(_storage);
  }
};

功能:explicit用来防止由构造函数定义的隐式转换。 参考C++中explicit关键字的作用

补充:std::forward(Args)...

功能:std::forward用于实现完美转发,所谓完美转发(perfect forwarding),是指在函数模板中,完全依照模板的参数类型,将参数传递给函数模板中的另外一个函数。C++11 std::move和std::forward

补充:C++中的reinterpret_cast关键字

功能:reinterpret_cast是四种强制转换中功能最为强大的,它可以暴力完成两个完全无关类型的指针之间或指针和数之间的互转,比如用char类型指针指向double值。它对原始对象的位模式提供较低层次上的重新解释(即reinterpret),完全复制二进制比特位到目标对象,转换后的值与原始对象无关但比特位一致,前后无精度损失。**

id obj = *--page->next;

转完之后是这个样子的 id obj = *(--(page->next));

  1. 先拿到page->next 指针

  2. 然后让page->next 指针自减,

  3. 然后再取 自减后的page->next的地址指向的值。

//减完之后,next指向如下(测试数据),相差8,就是一个指针的size
nextid *0x10280a0e80x000000010280a0e8
nextid *0x10280a0e00x000000010280a0e0

__builtin_expect(EXP, N)

意思是高速编译器 EXP==N的概率很大。

#define fastpath(x) (__builtin_expect(bool(x), 1)) // x==true 的概率很大
#define slowpath(x) (__builtin_expect(bool(x), 0)) // x==false 的概率很大
if (fastpath(x)) dosomething; // 等价于 if (x) dosomething;
if (slowpath(x)) dosomething; // 等价于 if (x) dosomething;
//也就是说,fastpath(),执行 if 后面的语句的机会更大,使用 slowpath(),执行 else 后面的语句的机会更大。通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着起面的代码,从而减少指令跳转带来的性能上的下降。

__builtin_addcl(lhs, rhs, carryin, carryout);

GCC 的builtin 函数,用来操作二进制;这个是二进制+

__builtin_addcl(lhs, rhs, carryin, carryout);// lhs+rhs+carryin ,当carryout=1是越界
0b0000000000011101100000000000000100000000010010100010000000111001
+
0b0000000100000000000000000000000000000000000000000000000000000000
=
0b0000000100011101100000000000000100000000010010100010000000111001
 carryout是0

_c11_atomic

我们要把一个原子对象的值加载到一个普通的,即非原子对象中,我们不能直接用=操作符,而是应该用下面的atomic_load函数,它是将原子对象复赋值给一个普通的变量,如果要把一个普通的变量存储到一个原子对象当中去,我们也是用atomic_store,将一个普通变量的值存储到原子对象当中去。另外,对于atomic_store,我们对原子对象初始化的时候不要用这个,应该用前面提到的方法初始化atomic_init

C11标准是规定了五种可用以原子对象的算术逻辑操作,加、减、或、异或、与,在使用这五个操作的时候,也就是操作数类型不能是atomic_bool类型,对这种类型不支持。这五种操作运算对应到C11原子操作接口,都是属于宏函数,因为每一个编译器实现都会不太一样,分别是atomic_fetch作为前缀,add就是加法,sub就是减法,or就是或,xor就是异或,and就是与

obj原来的值替换为旧值与obj和之间的按位XX操作结果arg,并返回obj先前保存的值。操作是读取 - 修改 - 写入操作。第一个版本根据命令对内存进行访问memory_order_seq_cst,第二个版本根据内存访问内存访问order

/**
* obj 指向要修改的原子对象的指针
* arg 按位 XX 存储在原子对象中的值
* order 此操作的内存同步排序:所有值都是允许的 
*/
__c11_atomic_fetch_or(volatile A * obj,M arg,memory_order order) //或
__c11_atomic_fetch_and(volatile A * obj,M arg,memory_order order) //与

atomic

__c11_atomic_compare_exchange_weak((_Atomic(uintptr_t) *)dst, &oldvalue, value, __ATOMIC_RELAXED, __ATOMIC_RELAXED);

引用: 无锁同步-C++11之Atomic和CAS
store是原子写操作,而load则是对应的原子读操作。
exchange允许2个数值进行交换,并保证整个过程是原子的。
而compare_exchange_weak和compare_exchange_strong则是著名的CAS(compare and set)。参数会要求在这里传入期待的数值和新的数值。它们对比变量的值和期待的值是否一致,如果是,则替换为用户指定的一个新的数值。如果不是,则将变量的值和期待的值交换。
weak版本的CAS允许偶然出乎意料的返回(比如在字段值和期待值一样的时候却返回了false),不过在一些循环算法中,这是可以接受的。通常它比起strong有更高的性能。

atomic_thread_fence

按照指示order,建立非原子和宽松原子访问的内存同步排序,而无需关联的原子操作。例如,memory_order_release在线程A 的围栏之前发生的所有非原子和宽松的原子存储都将与线程B在memory_order_acquire围栅之后制作的相同位置的非原子和放宽的原子加载同步。

is_trivially_copyable

template 
class is_trivially_copyable {
}

T 是可平凡复制对象则为 true ,否则为 false (公开静态成员常量)

哈希表之二次探测法

若当前key与原来key产生相同的哈希地址,则当前key存在该地址后偏移量为(1,2,3...)的二次方地址处

lambda表达式

C++ 11 中的 Lambda 表达式用于定义并创建匿名的函数对象,以简化编程工作。 Lambda 的语法形式如下:

[函数对象参数] (操作符重载函数参数) mutable 或 exception 声明 -> 返回值类型 {函数体}

可以看到,Lambda 主要分为五个部分:[函数对象参数]、(操作符重载函数参数)、mutable 或 exception 声明、-> 返回值类型、{函数体}.

使用位置:

static void load_categories_nolock(header_info *hi) {
   bool hasClassProperties = hi->info()->hasCategoryClassProperties();

   size_t count;
 // &。函数体内可以使用 Lambda 所在范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是引用传递方式(相当于是编译器自动为我们按引用传递了所有局部变量)。
   auto processCatlist = [&](category_t * const *catlist) {
    ...
  };

   processCatlist(hi->catlist(&count));
   processCatlist(hi->catlist2(&count));
}

你可能感兴趣的:(Objc源码中的C++知识点)