【C++】C++避坑经验谈:数组、vector
2016-12-06 15:41
267 查看
要给新人培训C++,奈何大家的时间是分散的,所以在这里开坑写文章了。这里只是个人经验,如果我也坑了的话请勿喷。
一、指针和数组很危险?
流行的说法是C++尽量不要用指针和数组。指针会出现各种bug。当然只是说尽量不要用,不是说不用。在小型项目和追求速度的项目中,用指针并没有太大问题。注意释放并置零指针、不要用悬垂指针、避免数组越界,指针用起来并没有太大问题。但是遇到中大型项目,经验丰富的程序员会出现指针错误使用的情况,是很正常。
在项目中,我们会尽量减少指针使用,比如:函数传参使用引用传递;使用智能指针;仿照STL的迭代器,对指针进行包裹。
注意的是,如果只是临时使用指针,很快就会处理掉这个指针的话,用一用根本无关紧要,不能实行教条主义。另外需要注意的是,智能指针std::shared_ptr包含的两个对象,如果对象内部有指向对方的std::shared_ptr指针,是会出错的,原因可以从std::shared_ptr的实现——引用计数中去思考。按照传统的方法,相互指向对方的指针,其中一方可以使用std::shared_ptr指针,另一方需要使用std::weak_ptr指针。在开源三维图像库VTK中,vtk的两大类对象集合:processObject和dataObject内部有相互指向对方的指针。VTK是processObject包含指向dataObject的智能指针,dataObject内部直接使用指向processObject的普通指针。这是一种合理的方法。
二、图像存储用数组还是std::vector?
官方的说法是多用std::vector。然而对于图像处理的程序员来说,图像数据应该使用数组来存储。
Debug模式用来进行程序调试,寻找详细的出错信息;Release模式可以用glog日志、printf输出代码信息等方法来获取出错信息,但明显不如Debug模式靠谱。release程序的std::vector有时候可以达到原生数组的速度,一些第三方STL库内部就是用数组来实现std::vector的;然而在Debug模式下,各平台上std::vector比数组慢多了。图像处理程序中,快速遍历图像是基本操作之一,如果我们要用Debug模式调试程序,显然用遍历std::vector存储的图像,会慢的发狂。因此对于追求速度的一些实现,数组是优先选择。
然而前面说了,C++要少用普通指针。因此我们一般需要用C++类对指向图像数据的数组进行包裹。OpenCV库的C和C++模式分别使用lpImage和Mat两种数据格式,其中lpImage是用C-struct结构体对图像数据指针和其他信息的包装,但是依然需要使用专门的函数来删除它;Mat是基于C++ 类class/struct封装的图像数据,使用方法跟基本数据类型如int一样,基本无需关心指针和内存方面的问题。C++类使用构造函数来自动创建图像数组和相关信息,析构函数可以在退出作用域时自动调用来删除数据。这使得C++可以构造资源管理类来管理大量的数据。
PS:在C++中,struct和class的区别在于,struct没有指定的成员都是public,而class没有指定的成员都是private;C++的struct包含默认构造函数和默认析构函数,而C语言的struct仅仅是一个数据集合,两者是不同的。
三、std::vector什么时候使用?
与之相关的问题是:如何选择STL的容器?
从问题二中我们看到,std::vector不能用在需要高速读写遍历的情况下。除去上面的情况,在不需要频繁插入删除数据序列头部和中间元素的情况下,不需要使用关联容器的情况下,数组统统用std::vector来实现。
std::vector有强大的错误检查,用lazy-operation来实现快速尾部数据扩容。在基本确定数据数量的情况下,我们可以使用resize/reserve函数来预分配内存,提高速度。另外如果只是少量的中间数据的插入、删除,std::vector的速度并没有想象中那么差,无需顾忌书中说的std::vector在中间数据的操作性能很不好啊之类的。
下面这种情况,我们基本可以忽略std::vector对中间数据操作性能很差的特性,看如下代码(C++11):
输出结果为:
7
00000000002B6B28:4.2
5
00000000002B6B28:4.2
这说明嵌套vector,外层vector保存的是内层vector的指针引用,在删除内层vector元素时,外层vector只是删除了指向内层vector的指针引用并调用了该内层vector元素的析构函数,其他内层vector元素在内存中的位置根本就没有动,因此性能开销不大,可以放心用。
一、指针和数组很危险?
流行的说法是C++尽量不要用指针和数组。指针会出现各种bug。当然只是说尽量不要用,不是说不用。在小型项目和追求速度的项目中,用指针并没有太大问题。注意释放并置零指针、不要用悬垂指针、避免数组越界,指针用起来并没有太大问题。但是遇到中大型项目,经验丰富的程序员会出现指针错误使用的情况,是很正常。
在项目中,我们会尽量减少指针使用,比如:函数传参使用引用传递;使用智能指针;仿照STL的迭代器,对指针进行包裹。
注意的是,如果只是临时使用指针,很快就会处理掉这个指针的话,用一用根本无关紧要,不能实行教条主义。另外需要注意的是,智能指针std::shared_ptr包含的两个对象,如果对象内部有指向对方的std::shared_ptr指针,是会出错的,原因可以从std::shared_ptr的实现——引用计数中去思考。按照传统的方法,相互指向对方的指针,其中一方可以使用std::shared_ptr指针,另一方需要使用std::weak_ptr指针。在开源三维图像库VTK中,vtk的两大类对象集合:processObject和dataObject内部有相互指向对方的指针。VTK是processObject包含指向dataObject的智能指针,dataObject内部直接使用指向processObject的普通指针。这是一种合理的方法。
二、图像存储用数组还是std::vector?
官方的说法是多用std::vector。然而对于图像处理的程序员来说,图像数据应该使用数组来存储。
Debug模式用来进行程序调试,寻找详细的出错信息;Release模式可以用glog日志、printf输出代码信息等方法来获取出错信息,但明显不如Debug模式靠谱。release程序的std::vector有时候可以达到原生数组的速度,一些第三方STL库内部就是用数组来实现std::vector的;然而在Debug模式下,各平台上std::vector比数组慢多了。图像处理程序中,快速遍历图像是基本操作之一,如果我们要用Debug模式调试程序,显然用遍历std::vector存储的图像,会慢的发狂。因此对于追求速度的一些实现,数组是优先选择。
然而前面说了,C++要少用普通指针。因此我们一般需要用C++类对指向图像数据的数组进行包裹。OpenCV库的C和C++模式分别使用lpImage和Mat两种数据格式,其中lpImage是用C-struct结构体对图像数据指针和其他信息的包装,但是依然需要使用专门的函数来删除它;Mat是基于C++ 类class/struct封装的图像数据,使用方法跟基本数据类型如int一样,基本无需关心指针和内存方面的问题。C++类使用构造函数来自动创建图像数组和相关信息,析构函数可以在退出作用域时自动调用来删除数据。这使得C++可以构造资源管理类来管理大量的数据。
PS:在C++中,struct和class的区别在于,struct没有指定的成员都是public,而class没有指定的成员都是private;C++的struct包含默认构造函数和默认析构函数,而C语言的struct仅仅是一个数据集合,两者是不同的。
三、std::vector什么时候使用?
与之相关的问题是:如何选择STL的容器?
从问题二中我们看到,std::vector不能用在需要高速读写遍历的情况下。除去上面的情况,在不需要频繁插入删除数据序列头部和中间元素的情况下,不需要使用关联容器的情况下,数组统统用std::vector来实现。
std::vector有强大的错误检查,用lazy-operation来实现快速尾部数据扩容。在基本确定数据数量的情况下,我们可以使用resize/reserve函数来预分配内存,提高速度。另外如果只是少量的中间数据的插入、删除,std::vector的速度并没有想象中那么差,无需顾忌书中说的std::vector在中间数据的操作性能很不好啊之类的。
下面这种情况,我们基本可以忽略std::vector对中间数据操作性能很差的特性,看如下代码(C++11):
vector<vector<double> > a; for (int i = 0; i < 7; ++i) { vector<double> b = { i+0.1, i+0.2, i+0.3 }; a.push_back(b); } cout << a.size() << endl; double *p = &a[4][1]; cout <<p << ":" <<*p << endl; a.erase(a.begin()); a.erase(a.begin()); cout << a.size() << endl; cout << p << ":" << *p << endl;
输出结果为:
7
00000000002B6B28:4.2
5
00000000002B6B28:4.2
这说明嵌套vector,外层vector保存的是内层vector的指针引用,在删除内层vector元素时,外层vector只是删除了指向内层vector的指针引用并调用了该内层vector元素的析构函数,其他内层vector元素在内存中的位置根本就没有动,因此性能开销不大,可以放心用。
相关文章推荐
- C++学习心得(数组,vector容器,typedef,volatile)
- C++中巧得数组长度和vector的理解
- C++学习vector与数组优缺点
- c++将整个数组中的元素转移到vector模板类对象中
- C++vector对象转数组
- [C++]vector嵌套实现2D数组
- 对C++中用vector创建数组对象的认识
- 【C++ STL】细数C++ STL 的那些事---vector (动态数组)
- c++中bool数组与bitset,vector<bool>的使用与占用空间大小对比
- C++中的数组array和vector,lambda表达式,C字符串加操作,C++中新类型数组(数组缓存),多元数组,new缓冲
- C++中巧得数组长度和vector的理解
- c++ vector数组对象的使用
- 【C++ STL】细数C++ STL 的那些事---vector (动态数组)
- c/c++实现的一个动态分配内存的结构体数组(类似vector)
- C++ 数组array与vector的比较
- C++ 数组array与vector的比较
- C++ vector多维数组初始化及清零
- 使用C++ vector类创建动态数组
- c++之Vector容器数据两种取值方法(指针访问,数组下标访问)
- C++中的快速排序(使用vector和数组的不同)