1. malloc vs calloc
参数不一样这是谁都可以看出来的,呵呵。重要的是calloc会将申请的内存初始化为0,malloc不会
2.setjump & longjump
可以通过setjump,然后调用longjump将栈帧退回到setjump。这对函数能够实现goto不能完成的跨函数跳跃。不过在使用在对函数的时候,要注意寄存器值的变化是否会变化到setjump时的状态,对于静态变量和变量是不一样的
3. vfork vs fork
fork大家用的会多点,对于这个函数,应该用的比较少哦。但是对于fork后会立刻执行exec的情景,这个函数是很适用的。它跟fork之间的区别是:vfork保证子进程先运行,在它调用exec或者exit之后,父进程才可能被调度运行。而且重要的是子进程不会复制父进程的数据,在调用exec之前,是直接用父进程的数据进行运行(可以修改父进程的变量哦)
4. execve 系列
总共6个函数,用于执行一个新的进程程序,这6个函数参数的区别在于是用数组还是。。。,以及是否带环境变量。还有个要注意,只有execve是系统调用,其他都是库函数,最终还是调execve
5. c++的访问修饰符
c++的访问修饰符,只是控制是否可以访问。 不适用于重写,父类private的方法,子类也是可以重写的
6. 命名空间的问题
子类A继承ns::B, 访问B不需要引用ns::,好神奇
7. 类中有指针,而且是类自己申请的
你可能要在析构函数里面释放,而且你需要重写赋值运算操作和拷贝构造函数,无论你是设成不可拷贝,还是深拷贝指针
8. 方法重写overrid
重写基类的方法,可以改变返回值,但是返回值必须是基类返回值的子类
9. 类的静态常量成员初始化
可以在类里面初始化,也可以在cpp文件里面初始化,但是有时候在类里面初始化会报找不到定义,所以还是在cpp文件里面初始化吧
10. 宏 vs 命名空间
宏是不受命名空间保护的,宏放在命名空间里面还是外面都是一样的,全局的
11. 根据左值返回相应的类型
通过一个内部类,重载各种类型转换操作,可以实现根据左值返回对应的类型,有个应用场景,是将字符串转换为需要的类型,比如从xml文件里面读取需要的类型
12. const 引用会导致复制
const 引用在初始化时如果类型不一致会导致复制一份,而且编译不会报错,比如:
const uint64_t& a = b; (int b = 3)
其实a跟b指向的不是同一个内存对象,那么b变了,a的值还是不变的
13. inline可以避免重复定义
如果在头文件里面写函数的实现定义,那么在链接的时候就很容易出重复定义的错误,将函数申明为inline就没问题了
14. 静态成员初始化
全局静态成员和类的静态成员是在main之前初始化,但是你不能控制他们的初始化顺序。函数的局部静态成员在调用时才会进行初始化,如果要保证局部静态成员在main之前初始化,需要一点点技巧,参见boost的singleton实现:
template<typename T> class Singleton { private: struct obj_creator { obj_creator() { Singleton<T>::Instance(); } inline void do_nothing() const {} }; static obj_creator create_obj_; // 通过类静态成员来触发函数调用,来初始化局部静态成员 public: static T* Instance() { static T obj_; create_obj_.do_nothing(); // 据说这个调用是为了防止编译器优化,待研究,有高手的话,请指教 return &obj_; } };
15. 表达式计算的顺序未定义
表达式中各个节点的计算顺序是未定义的,比如
int result = a[0] + 2 * a[++i] + 3 * a[++i];
16. 编译时发现说类没申明,但是实际又声明了,找不到原因的话,看看是不是头文件的define冲突了,就是2个头文件的define是一样的,导致另外一个头文件没有加载
17. 类的析构函数默认是非virtual的,如果子类的析构函数是virtual,基类不是,当delete一个指向子类的基类指针时,会core dump