»
(18)C++14 智能指针及引用计数*
(19)C++14 多线程pthread*
(20)C++14 同步锁mutex*
(24)C++中的计时与等待*
(25)C++中的高精度计算*
(27)C++14 std::string*
(31)C++高级技能*
(32)C++的加速、加稳定
有位老师说过:很多人都会C++,但,用好C++的为数不多,甚至很少。这是多年来做C++代码过程中得到的经验总结,特别是做G-dis软件的时候。 在这里,要讲的是如何用好C++,让你的C++代码比别人的更快同时更稳定的方法。这篇文章后续还会不断更新,将以前用到的点补充到这里: 第一、用好引用传参,减少函数返回值空间的使用。 原因:C++中函数的返回值是一个右值空间,对于函数的返回过程,C++会消耗计算时间:拷贝构造右值空间、赋值函数的返回值给Label变量。 所以,函数尽量使用void,尽量使用引用传参的方式将函数需要返回的数据返回给引用参数;或者使用左值引用作为返回值,因为,返回值为左值引用时,返回值空间不做拷贝构造操作,而是直接扔对象。在数据脱离原有对象时,才返回数据指针。 第二、用好引用传参,减少函数传入参数时的拷贝构造。 原因:C++中对于函数的对象参数,如果传入的是一个变量而不是一个引用,程序会调用这个类的拷贝构造函数将实参拷贝到形参上。 所以,为了减少拷贝构造过程,建议尽量使用引用传参的方式向一个函数传入参数。 第三、C++了、不是C,一般代码尽量使用对象变量以及引用传参,而不是类似C语言的指针操作。 第四、数值计算要舍得多加括号,特别是带指针的数值计算和带位移的数值计算。 第五、注意用好多线程 这里不针对任何人的任何用法,只讲讲我对多线程的理解:多线程编程中尽量不要级联调用;对于父对象可能销毁的子对象,不能直接在多线程中使用。 对于类对象,很多人认为在析构函数以及get方法中加入Mutex锁,就可以解决返回值的空指针异常的问题。其实不然。看下面的例子: class Parent{ private: std::mutex locker; Child *child; public: ~Parent(){ locker.lock(); delete child; child=nullptr; locker.unlock(); } Child* getChild(){ Child* result; locker.lock(); result=this->child; locker.unlock(); return result;//看似没问题,因为加了锁,顶多返回nullptr } } //以下threadLoop函数是一个线程的循环函数, void threadLoop(){ //使用错误1 parent->getChild()->showResult(); //使用错误2 if((Child* c=parent->getChild())!=nullptr){//这里可能不是nullptr c->showResult();//程序运行到这里,别的线程可能已经销毁parent对象 } } 在上面的代码中,threadLoop是一个线程的循环函数,这个程序连续箭头调用会有很大的风险:因为,在threadLoop线程退出前,Parent对象可能已经在别的线程中销毁了,而销毁Parent后,threadLoop线程中已经通过getChild()方法得到的Child* c就是野指针。 当然,一般程序代码中的threadLoop中基本不会销毁parent对象。所以,出问题也只会出现在关闭程序时的销毁parent对象以及threadLoop线程之间。 第六、C++中链式箭头调用本身不会导致内存访问错误,但要注意链式调用中的空指针的处理。 第七、网络通信协议中,struct结构体中要注意数据格子的字节对齐。 struct network_data{ unsigned char a2;//4字节 int a3;//4字节 unsigned char a1;//8字节 long long a5;//8字节 unsigned char a6;//8字节 }; sizeof(network_data)的结果为32。 memcpy(dst,src,sizeof(network_data))会多出3+7+7个字节的空隙,因为struct中,属性字节数会以填1、2、4、8格子的方式自动对齐到格子。这对于跨平台、跨系统之间的数据交换是很友好的,但,对于网络传输,这会浪费很多空白数据字节的网络传输时间。 所以,要注意memcpy结构体数据到字符串数组时,字符串数组中会留有很多空白数据字节。也所以,网络传输过程中,很少直接使用memcpy结构体数据到字符串数组。替代的方法是一个属性一个属性的添加到字符串数组,或者memcpy结构体数据到字符串数组后,delete掉中间的空白数据字节。这是十几年前的一个“网络传输挨个拼接”的总结。这个问题,如果开发人员使坏删了源代码,一般人发现不了。 在这一点中,可以搜索C99中结构体的解决方案:“结构体1字节对齐”。 第八、循环服务中必须加入usleep(1)。为了能够让CPU yield、能够让出CPU时间片、能够让CPU时间片轮转,从而减少CPU的高负载问题、降低连续判断循环条件导致的高负载。 sleep或usleep会让当前进程进入休眠状态,释放 CPU 时间片。 而,sched_yield()只是让当前线程暂时让出CPU,但并不会阻塞线程。如果当前线程立即被重新调度,那么会形成持续的循环,仍然导致CPU占用率很高。