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占用率很高。
«
——张人杰·www.v-signon.com学习者共勉
返回上一页
备案号:京ICP备19038994号-2
个人作品网站:www.up-task.com 主办:个人 English
网站内容如有侵权,请联系删除:1307776259@qq.com