C++的一些特性(1)
C++的一些特性(1)
这一章讲一下 C++ 比 C 多的一些东西吧,不然有时候真的感觉 C 和 C++ 区别不大的。
一些其它的函数定义
内联函数
内联函数是指在调用的时候不使用 call,而是用函数的代码替换这里的函数调用,可以节省这里时间上的效率损失,常见于逻辑简单,需要频繁调用的函数,这里省下的时间开销就很大,在函数定义前加关键字 inline 可以把函数声明为内联。当然,逻辑复杂的函数也可以使用 inline,但是编译器不一定会听,编译器会综合考虑要不要对这个函数使用内联。
裸函数
在函数定义前加关键字 __deplspec(naked)
可以把函数声明为裸函数,裸函数是指什么都不具备的函数,不会自动给你压栈,也不会自动给你返回,什么都需要自己做。
一般来说,使用naked函数时需要注意以下问题:
- 函数必须显式返回。一般通过
__asm ret
的内嵌汇编指令返回。 - 不可以通过任何方式使用局部变量。若声明一个局部变量,并在代码中为其赋值,则会更改父函数中相应位置的局部函数的值。
- 只能通过esp引用参数。因为子函数继承了父函数的ebp寄存器,所以只能通过esp引用参数。
- naked 属性仅与函数的定义相关,不能在函数原型中指定。不能用于函数指针,不能用于数据定义。
比如 C 语言定义一个这样的函数
1 | _declspec(naked) void add(int a, int b) { |
在编译完成之后转到反汇编可以看到
相比不加 naked 定义,少了压栈和退栈以及返回的代码,也就是说各方面操作要我们内联汇编完成。
这个的用途我也是能想到的,就比如我要对一段代码进行 inline hook,一半情况下我的操作是把我的代码先转为字节码存到数组里面,再用 VirtualProtect 函数赋予可执行权限之后,jmp 跳转到字节数组里面,这就很不方便,因为我要改某些方面的代码就会很困难,而有了裸函数我可以直接写汇编代码然后用这个函数作为跳板执行,后期维护也比较方便。
类型推导
我们上面说过用 typeid 函数可以获取变量类型,同时我们需要定义一个跟一个变量类型相同的变量类型可以使用
1 | decltype(var1) var2; |
的形式去得到,这样我们就定义了一个跟 var1 类型一致的 var2 变量,这个变量类型是通过推导得到的。
类型推导还有一种方式是使用关键字 auto,比如我们经常用的,iterator,在遍历 vector 或者是 map 的时候都是写的 auto p,因为那个变量类型过于长,我们得这么写:vector<int>::iterator
不如一个 auto 省事,但是它们原理一致都是通过类型推导得到。
函数返回类型后置
这也是 C++ 的一个特性,似乎跟 python 差不多,我记得 python 是这样的写法。就是我们在定义函数的时候一般是返回值开头的,但是 C++ 运行你放后面,用这种写法:
1 | auto test()->int{ |
两者结合一下可以让模板变得更加灵活。
比如加法它不仅可以支持相同类型,也可以支持不同类型,那么我们来实现一个加法函数。
1 | template<typename T1,typename T2> |
这个 test 函数实现了加法,但是它更为灵活,不像之前一样两个变量类型只能返回其中一个,在这里无论是调用 test(p,k)
还是交换调用,最终都会返回一个 int*
类型,因为 int* + int
得出的变量类型就是 int *
。
当然前置也可以写的,只不过需要改改,没有那么灵活,因为我们在前面还没有定义上参数名,因此我们不能使用 decltype(a+b),我们只能用两个类型去实例化一个匿名对象相加,得到的结果给了 decltype 作为参数。
1 | template<typename T1,typename T2> |