C++容器的遍历与程序的性能分析
c++11之后,c++可能是受到java等语言的启发,引入了更多的遍历容器的方式,但是,如果使用方式不对,可能会导致程序的性能低下。本例就使用了多种遍历方式来访问list的元素。并演示了其中的区别。
这段代码演示了如下C++的特点:
1、对list等容器的元素来遍历的时候,如果元素是非指针类型,使用迭代器的速度要比使用 for( auto ele:list_name) 的方式快。for( auto ele:list_name)的方式,ele是个临时变量,这种方式不能修改list_name中的元素本身。但是使用for( auto & ele:list_name)可以修改list_name中的元素本身,for( auto ele:list_name)的性能与迭代器的性能相当;
2、例子中计算性能的方式不太严谨,可以去掉每个循环中的循环体和增加循环次数来更加精确的演示速度的快慢。
3、如果list等容器的元素类型是非指针类型,使用一个变量名字去添加元素的时候,会在堆中产生临时变量,这些临时变量会放在容器中。
4、如果list等容器的元素类型是指针类型,使用迭代器和for( auto ele:list_name) 和 for( auto & ele:list_name) 的性能相当,因为遍历这些元素不会产生临时变量。
5、在析构对象的时候,堆中的对象,是先创建的先析构,栈中的对象析构顺序符合栈的特点:后创建的先析构,即“弹栈式”的析构。
6、本例使用了list容器,而没有使用vector,使用vector的话,如果元素是非指针类型,则vector本身创建的时候,就会有很多次的构造和析构,这是因为vector的元素的地址是连续的。一旦内存中的某个地址装不下当前的元素的时候,系统会去寻找更大的地址来装更多的元素,这个地址的改变就要涉及到对象的复制构造和析构。而list则没有这个缺点,它是“链表式”的保存元素。
7、如果追求性能,需要使用list,且元素类型须是指针类型。
// g++ -std=c++11 list_for.cpp -o list_for #include <iostream> #include <string> #include <list> #include <chrono> #define STD_COUT std::cout<<__FILE__<<":"<<__LINE__<<"@"<<__FUNCTION__<<"\t" class ClassA { public: ClassA() { STD_COUT <<this<<" default constructor"<<std::endl; m_iNum = 0; } ClassA(const ClassA & a) { STD_COUT <<this<<" copy constructor 1"<< std::endl; this->m_iNum = a.m_iNum; this->str_msg_ = a.str_msg_; } ClassA operator = (const ClassA & a){ STD_COUT <<this<<" copy constructor 2"<<std::endl; this->m_iNum = a.m_iNum; this->str_msg_ = a.str_msg_; return *this; } virtual ~ClassA() { STD_COUT <<this<< std::endl; m_iNum = 0; } virtual void showMsg() { STD_COUT << "from ClassA " <<this<< std::endl; } virtual void callMsg() { this->showMsg(); } public: int m_iNum; std::string str_msg_; }; int main(int argc, char ** argv){ std::cout <<"=============在栈中创建一个对象================= "<<(void*)&main << std::endl; ClassA class_a1; std::list<ClassA> myList2; for(int i =0; i< 5;i++){ if(class_a1.str_msg_.length() < 2000){ class_a1.str_msg_.append("this is s string"); } class_a1.m_iNum = i; std::cout <<"=============加入一个元素,这里会产生复制构造出临时变量放到list中================="<< std::endl; myList2.push_back(class_a1); } // 当前微秒 long long micro_seconds1 = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); std::cout <<"当前微秒\t"<< micro_seconds1 << std::endl; for(std::list<ClassA>::iterator itr = myList2.begin(); itr != myList2.end() ; itr++){ std::cout <<"元素中的地址 "<<(void*)&(*itr)<< std::endl; } long long micro_seconds2 = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); std::cout <<"当前微秒\t"<< micro_seconds2 << " 使用迭代器的时间差 "<<micro_seconds2-micro_seconds1 << std::endl; micro_seconds1 = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); std::cout <<"当前微秒\t"<< micro_seconds1 << std::endl; for(auto ele: myList2){ std::cout <<"元素中的地址 "<<(void*)&(ele)<< std::endl; } micro_seconds2 = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); std::cout <<"当前微秒\t"<< micro_seconds2 << " 使用 auto 的时间差 "<<micro_seconds2-micro_seconds1 << std::endl; micro_seconds1 = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); std::cout <<"当前微秒\t"<< micro_seconds1 << std::endl; for(auto & ele: myList2){ std::cout <<"元素中的地址 "<<(void*)&(ele)<< std::endl; } micro_seconds2 = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); std::cout <<"当前微秒\t"<< micro_seconds2 << " 使用 auto & 的时间差 "<<micro_seconds2-micro_seconds1 << std::endl; std::cout <<"=============在堆中创建一个对象=================" << std::endl; std::list<ClassA*> myList; for(int i =0; i< 5;i++){ ClassA* class_a2 = new ClassA(); if(class_a2->str_msg_.length() < 2000){ class_a2->str_msg_.append("this is s string"); } class_a2->m_iNum = i; myList.push_back(class_a2); } // 当前微秒 micro_seconds1 = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); std::cout <<"当前微秒\t"<< micro_seconds1 << std::endl; for(std::list<ClassA*>::iterator itr = myList.begin(); itr != myList.end() ; itr++){ std::cout <<"元素中的地址 "<<(void*)&(*itr)<< std::endl; } micro_seconds2 = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); std::cout <<"当前微秒\t"<< micro_seconds2 << " 使用迭代器的时间差 "<<micro_seconds2-micro_seconds1 << std::endl; micro_seconds1 = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); std::cout <<"当前微秒\t"<< micro_seconds1 << std::endl; for(auto ele: myList){ std::cout <<"元素中的地址 "<<(void*)&(ele)<< std::endl; } micro_seconds2 = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); std::cout <<"当前微秒\t"<< micro_seconds2 << " 使用 auto 的时间差 "<<micro_seconds2-micro_seconds1 << std::endl; micro_seconds1 = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); std::cout <<"当前微秒\t"<< micro_seconds1 << std::endl; for(auto & ele: myList){ std::cout <<"元素中的地址 "<<(void*)&(ele)<< std::endl; } micro_seconds2 = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); std::cout <<"当前微秒\t"<< micro_seconds2 << " 使用 auto & 的时间差 "<<micro_seconds2-micro_seconds1 << std::endl; std::cout <<"=============手动析构堆中的变量=================" << std::endl; for(auto & ele: myList){ delete ele; } std::cout <<"=============程序结束,自动析构栈中的变量=================" << std::endl; return 0; }
代码输出结果略,可以通过结果分析上述说明。