一
函數的參數傳遞可以簡單分類為“傳值”和“傳引用”。
聲明函數時,形參帶引用“&”,則函數調用時,是把實參所在的內存直接傳給函數所開辟的棧內存。在函數內對形參的修改相當於對實參也進行修改。
聲明函數時,形參不帶引用,則函數調用時,是把實參拷貝一份作為形參。從內存上看,存在兩個存放相同變量的區域,分別是實參和形參。在函數中對形參的修改,都不會對實參產生影響。函數退出后,形參所在的棧內存全部銷毀。
二
對智能指針shared_ptr的通俗理解。
shared_ptr之所以能夠做到在指針生命周期結束后自動銷毀,得益於兩個經典設計:
1)用類封裝裸指針得到智能指針,在類的析構函數中,執行指針的delete操作;
2)類內有兩個成員變量,一個是裸指針,一個是計數器;
當計數器計數歸零時,智能指針自動調用其析構函數,銷毀指針,釋放內存。
三
智能指針作為函數的參數。
1)智能指針作為函數的傳值參數,執行new操作
main函數片段如下,
PointCloudPtr targetCloudPtr; targetCloudPtr = lidar_->GetNewCloud(); if (targetCloudPtr == 0) return; PointCloudPtr processTarPtr(new PointCloudType); ExtrPlanes_->run_Plane_Extraction(targetCloudPtr, processTarPtr); std::cout << "process target point cloud size : " << processTarPtr->points.size() << std::endl;
聲明了一個PCL的智能指針 processTarPtr ,執行new操作之后, processTarPtr 中便有了一個 PointCloudType 的裸指針成員變量,同時計數器+1,此時計數器=1.
函數 run_Plane_Extraction 的定義如下,
void Extr_PLANEFEATURE::run_Plane_Extraction(PointCloudPtr inPtr, PointCloudPtr outPtr) { PointCloudPtr MLS_pointcloud(new PointCloudType); // // 給MLS_pointcloud賦值 // outPtr = MLS_pointcloud; }
實參processTarPtr將拷貝一份作為形參,同時計數器+1,此時計數器=2.
在函數中對形參的修改都不會對實參產生影響。因此,函數執行完畢,outPtr所指向的棧內存全部銷毀。
此時,在main函數中讀取processTarPtr指針的值,返回值為0。此時程序並不會崩潰。等到這個0值對main函數中其他操作產生影響時,程序才會報錯。
Invalid or empty point cloud dataset given! Invalid or empty point cloud dataset given! [ndtMap-2] process has died [pid 25597, exit code -8, cmd /home/gordon/fase_ws/devel/lib/ddd_wall_mapping/ndtMap __name:=ndtMap __log:=/home/gordon/.ros/log/d150b30c-aecf-11e9-8009-9cda3e942fe1/ndtMap-2.log]. log file: /home/gordon/.ros/log/d150b30c-aecf-11e9-8009-9cda3e942fe1/ndtMap-2*.log
2)智能指針作為函數的傳值參數,不執行new操作
main函數做如下修改,即不執行智能指針的new操作。
PointCloudPtr targetCloudPtr; targetCloudPtr = lidar_->GetNewCloud(); if (targetCloudPtr == 0) return; PointCloudPtr processTarPtr; ExtrPlanes_->run_Plane_Extraction(targetCloudPtr, processTarPtr); std::cout << "process target point cloud size : " << processTarPtr->points.size() << std::endl;
聲明了一個智能指針而不進行new操作,那么這個指針一直個是空指針,即null。計數器一直為0.
函數run_Plane_Extraction的調用結果與上面例子相同。回到main函數,訪問空指針processTarPtr,程序立即崩潰。報錯如下,
ndtMap: /usr/include/boost/smart_ptr/shared_ptr.hpp:648: typename boost::detail::sp_member_access<T>::type boost::shared_ptr<T>::operator->() const [with T = pcl::PointCloud<pcl::PointXYZI>; typename boost::detail::sp_member_access<T>::type = pcl::PointCloud<pcl::PointXYZI>*]: Assertion `px != 0' failed. [ndtMap-2] process has died [pid 15323, exit code -6, cmd /home/gordon/fase_ws/devel/lib/ddd_wall_mapping/ndtMap __name:=ndtMap __log:=/home/gordon/.ros/log/d150b30c-aecf-11e9-8009-9cda3e942fe1/ndtMap-2.log]. log file: /home/gordon/.ros/log/d150b30c-aecf-11e9-8009-9cda3e942fe1/ndtMap-2*.log
3)智能指針作為函數的傳引用參數
為解決上述問題,函數 run_Plane_Extraction 對形參的修改能夠等同於對形參進行修改。即,我們希望能夠在函數 run_Plane_Extraction 結束后,將其中的形參outPtr所指向的內存保存下來。
對於上述2個例子,其修改方式都一樣,把函數 run_Plane_Extraction 的形參改成傳引用參數。
void Extr_PLANEFEATURE::run_Plane_Extraction(PointCloudPtr inPtr, PointCloudPtr& outPtr) { PointCloudPtr MLS_pointcloud(new PointCloudType); // // 給MLS_pointcloud賦值 // outPtr = MLS_pointcloud; }
傳引用參數,把實參所在的內存直接傳遞給函數作為形參,對形參的修改等同於對實參的修改。
函數 run_Plane_Extraction 結束以后,不會銷毀實參指向的內存!
問題成功解決!