【c++】0.C++笔记
1.DISALLOW_COPY_AND_ASSIGN
2.延時函數delay()、sleep()、usleep()
3.opencv在圖片中繪圖常使用的幾個函數
4.opencv中 cv::Mat 與 cv::Rect 在一起使用的情況
5.imshow()顯示圖像幀時暫停鍵的用法
6.opencv播放視頻 以及 設置任意鍵為暫停鍵
7.opencv旋轉圖片
8.const和constexpr相關知識
9.a.cc文件中調用另一個b.cc文件中的函數
10.視頻編碼格式與封裝格式,opencv2.4.8不能讀取h265編碼格式的視頻文件
11.當使用imshow顯示每一幀畫面時,出現卡頓的解決辦法
12.cv::VideoWriter的使用,把圖片寫進video
13.C++11多線程join()和detach()的理解
14.獲取鼠標動作進行相應處理
15.刪除容器中指定元素
15.1 map 的insert和emplace方法
15.2 map的erase(iter)需注意,和vector不一樣
16.sprintf的用法
17.std::shared_ptr、std::make_shared、 .get() 、.data()、void *p 的用法、裸指針
18.選擇使用memcpy_s,strcpy_s還是選擇strcpy,memcpy? memset()的用法
19.C++強制類型轉換(dynamic_cast,static_cast, const_cast, reinterpret_cast)
20.編譯proto的問題
21.比較大小時用減法越界的bug問題
22.攝像頭焦距和視場角
23.RGB和BGR的轉化,通道分離與合并
24.重寫、覆蓋、using、typedef
25.靜態成員靜態成員函數/變量、單例模式
26.proto相關用法
27.深拷貝、淺拷貝、拷貝構造函數 之間的關系
1.DISALLOW_COPY_AND_ASSIGN
有時候,進行類體設計時,會發現某個類的對象是獨一無二的,沒有完全相同的對象,也就是對該類對象做副本沒有任何意義.
因此,需要限制編譯器自動生動的拷貝構造函數和賦值構造函數.一般參用下面的宏定義的方式進行限制,代碼如下:
// A macro to disallow the copy constructor and operator= functions // This should be used in the priavte:declarations for a class #define DISALLOW_COPY_AND_ASSIGN(TypeName) \TypeName(const TypeName&); \TypeName& operator=(const TypeName&)class Test { public:Test(int t);~Test(); private:DISALLOW_COPY_AND_ASSIGN(Test); };聲明私有的拷貝構造函數和賦值構造函數,但不去定義實現它們,有三方面的作用:1.聲明了拷貝構造函數和賦this數,阻止了編譯器暗自創建的專屬版本.2.聲明了private,阻止了外this它們的調用.3.不定義它們,可以保證成員函數和友元函數調用它們時,產生一個連接錯誤.上述解決方法,面對在成員函數和友元函數企圖拷貝對象時,會產生連接器錯誤@zhu.hz的注釋:
(1)由于是在private下,外部調用不了,所以就不能進行copy和賦值操作,即不能進行 Test A(obj) 或 Test A=obj 的copy操作(拷貝構造),和 A=obj 或 A(obj) 的assign操作(拷貝賦值),【其中obj是先通過Test obj;得到的】。(2)他與單例模式不是一個概念,單例模式只允許有一個實例,而這個可以允許有多個對象,但是多個對象都是不一樣的,不允許進行拷貝構造和賦值構造,即不允許任何兩個對象一樣。(3)拷貝構造是在構造對象時候就使用另一個對象對他進行賦值。拷貝賦值是在構造對象完成之后,再對他進行賦值操作。一個是在創建他這個對象的時候就賦值了,另一個是在對象構造完成之后才對他進行賦值。畢竟只有這兩種方式可以對另一個對象進行賦值。這么賦值是為了初始化數據成員,而類本身的初始化數據可能不如用另一個對象的數據更合適。比如一個person類只有兩個成員height和weight,兩個數據成員在構造函數時初始化為0, 如果兩個人身高體重都一樣,就可以先創建其中一個對象后,直接拷貝構造或者拷貝賦值把先創建的對象賦值給另一個人,肯定比直接構造一個對象不進行賦值的初始化數據更好。這個例子太簡單,實際中肯定不可能這么簡單。=================================================================
2.延時函數delay(),sleep(),usleep()
#include <time.h> /* 調用時務必加上該頭文件 */ // 自定義的一個延時函數delay() void delay(int seconds) // 參數必須為整型,表示延時多少秒 {clock_t start = clock();clock_t lay = (clock_t)seconds * CLOCKS_PER_SEC;while ((clock()-start) < lay);}void delay(double seconds) // 參數為雙精度浮點型。這個函數是我修改的上面那個函數,重載一下。{double start = clock();double lay = (double)seconds * CLOCKS_PER_SEC;while ((clock()-start) < lay);} 百度Apollo里面用的是延時毫秒usleep(unsigned int __useconds)和延時秒sleep(unsigned int __seconds)函數,他們的輸入參數也都是整數,不能為浮點數,比如不能sleep(0.5)來表示延時0.5s。 但是當函數設定的計時器到期,或者接收到信號、程序發生中斷都會導致程序繼續執行。=================================================================
3.opencv在圖片中繪圖常使用的幾個函數
opencv中的(0,0)坐標是在圖像的左上角。
一般會將 cv::getTextSize() 與 cv::putText() 結合使用.
cv::getTextSize() //實際繪制文字之前,使用cv::getTextSize()接口先獲取待繪制文本框的大小,以方便放置文本框。返回值為cv::Size。設返回值為size,可以通過size.width和size.height來獲取文本框的寬和高.cv::putText() //在圖像上繪制文字cv::rectangle() //在Mat上畫矩形矩形框,如果要填充矩形,需要把thickness設為-1。詳解看后面。cv::Rect //定義一個矩形,有成員變量x,y,weight,height,如Rect rect1(256, 256, 128, 128),這樣用是構造函數;cv::Point() //定義一個2D的點:如Point point = Point(10, 8);或者 Point point;//創建一個2D點對象 point.x = 10;//初始化x坐標值 point.y = 8;//初始化y坐標值 cv::Scalar(255, 255, 0) //一般作為值來設置顏色。他是一個由長度為4的數組作為元素構成的結構體,Scalar最多可以存儲四個值,沒有提供的值默認是0。 // 關于cv::Scalar的更詳細資料可以參考 https://blog.csdn.net/liuweiyuxiang/article/details/76929534cv::Size cv::getTextSize(const string& text, // 待繪制的文字int fontFace, // 字體類型 (如 cv::FONT_HERSHEY_PLAIN, cv::FONT_HERSHEY_SIMPLEX)double fontScale, // 尺寸因子,值越大文字越大int thickness, // 線條寬度int* baseLine);void cv::putText(cv::Mat& img, // 待繪制的圖像const string& text, // 待繪制的文字cv::Point origin, // 文本框的左下角坐標(x,y),記住是左下角,不是左上角,要和cv::rectangle()的左上角區分開int fontFace, // 字體類型 (如cv::FONT_HERSHEY_PLAIN, cv::FONT_HERSHEY_SIMPLEX)double fontScale, // 尺寸因子,值越大文字越大cv::Scalar color, // 線條的顏色(RGB)int thickness = 1, // 線條寬度int lineType = 8, // 線型(4鄰域或8鄰域,默認8鄰域)bool bottomLeftOrigin = false // true='origin at lower left');對于在圖片中畫矩形框的函數,C++中opencv對void cv::rectangle()重載了兩種用法: // pt1 矩形的一個頂點; pt2 矩形對角線上的另一個頂點; color 線條顏色 (RGB) 或亮度(灰度圖像 ) // thickness 組成矩形的線條的粗細程度。取負值時(如 CV_FILLED)函數繪制填充了色彩的矩形。 line_type 線條的類型。見cvLine的描述。 shift 坐標點的小數點位數。 // 兩個函數的不同之處在于第二個函數把第一個函數中的兩個對角線定點改為了cv::Rect。 void cv::rectangle(cv::Mat& img, cv::Point pt1, cv::Point pt2, const cv::Scalar& color, int thickness=1, int lineType=8, int shift=0) void cv::rectangle(cv::Mat& img, cv::Rect rec, const cv::Scalar& color, int thickness=1, int lineType=8, int shift=0 )// 下面通過一個示例,來看看 cv::getTextSize()與cv::putText()相結合的妙用:
{//創建空白圖用于繪制文字cv::Mat image = cv::Mat::zeros(cv::Size(640, 480), CV_8UC3);//設置藍色背景image.setTo(cv::Scalar(100, 0, 0));//設置繪制文本的相關參數std::string text = "Hello World!";int font_face = cv::FONT_HERSHEY_COMPLEX; double font_scale = 2;int thickness = 2;int baseline;//獲取文本框的長寬cv::Size text_size = cv::getTextSize(text, font_face, font_scale, thickness, &baseline);//將文本框居中繪制cv::Point origin; origin.x = image.cols / 2 - text_size.width / 2;origin.y = image.rows / 2 + text_size.height / 2;cv::putText(image, text, origin, font_face, font_scale, cv::Scalar(0, 255, 255), thickness, 8, 0);//顯示繪制結果cv::imshow("image", image);cv::waitKey(0);return 0; }=================================================================
4.opencv中 cv::Mat 與 cv::Rect 在一起使用的情況
// cv::Mat frame; // cv::Rect rect;//創建的新圖像img是fram的一部分,具體的范圍rect指定,此構造函數也不進行圖像數據的復制操作,新圖像img與frame共用圖像數據。cv::Mat img(frame, rect); //cv::Mat::Mat(cv::Mat const&, cv::Rect_<int> const&) 這種方式構造函數創建的img,他的兩個參數都不能修改,因為是const,修改后就會出錯。cv::Mat img1(img2); //拷貝構造img1(rect); //從img1中截取rect區域,返回cv::Mat。=================================================================
5.imshow()顯示圖像幀時暫停鍵的用法
嚴格按照下面這種方式和順序不會有問題,例如在imshow后面直接跟cv::waitKey(1)后再char c=...會有問題。cv::namedWindow("light_object", cv::WINDOW_NORMAL);cv::resizeWindow("light_object", 640, 480);cv::imshow("light_object", frame);char c = static_cast<char>(cv::waitKey(50));if (c == ' ') {cv::waitKey(0);}=================================================================
6.opencv播放視頻 以及 設置任意鍵為暫停鍵
【該方法不實用,可能會暫停不了,時靈時不靈】
對于cv::VideoCapture的使用可以參考 https://blog.csdn.net/guduruyu/article/details/68486063
【不推薦】使用下面這種方法,因為我在apollo上使用根本捕獲不了按鍵,我目前還不知道是什么原因,必須使用以上方式把cv::waitKey(1)先賦值給一個char變量才行。
if(cv::waitKey(3) ==32) //空格鍵暫停cv::waitKey(0); if(cv::waitKey(3) ==9){ //tab鍵快進30幀,設置不了使用右箭頭for(int i=0;i<30;i++) //快進30幀cap >> img; //快進多少幀,就把這一句執行多少次}=================================================================
7.opencv旋轉圖片
Python版本的圖片旋轉:
//重要的是看注釋看懂原理,c++版本的是同樣原理,函數接口功能一樣,只不過函數接口的參數可能不一樣。img=cv2.transpose(img) //對矩陣做轉置后,并非旋轉了90度。需要再配合翻轉才行// # 上面一句搭配下面一句話是逆時針旋轉90度// # img=cv2.flip(img, 0, img) #第二個參數是flipcode。 flipcode=0,則在X軸上做鏡像,如果flipcode=1,則在Y軸上做鏡像,如果flipcode=-1則在兩個軸同時作鏡像// # 搭配這句話就是順時針旋轉90度img=cv2.flip(img, 1, img)C++版本的旋轉:
cv::Mat img; // 以下二者結合使用就是:順時針旋轉90度cv::transpose(img, img); //對矩陣做轉置后,并非旋轉了90度。需要再配合翻轉才行cv::flip(img, img, 1); // 第三個參數是flipcode,flipcode=0,則在X軸上做鏡像,如果flipcode=1,則在Y軸上做鏡像,如果flipcode=-1則在兩個軸同時作鏡像.=================================================================
8.const和constexpr相關知識
記憶法: const是常量,*是指針,const *按順序念就是【常量指針】,* const 按順序念就是【指針常量】。const int * p; //p是常量指針,p是const int *,const限定的是int,即指針指向的地址中存儲的值(int型),所以這種寫法不能修改指針指向的地址的中存儲的值,但是可以修改指針的指向。 int * const p; //p是指針常量,p是int*,const限定的是指針,即不能修改指針指向,但是可以修改指針指向的地址中存儲的值,類中的this指針指向成員函數所屬的類對象,并且this指針只能在類的成員函數中調用。 this指針的本質,就是指針常量, 即【類名 * const this】,this指針的指向不能修改,如 this=NULL 是不合法的;但是this->m_Age=30是合法的,其中m_Age是類成員變量。const 類名 * const this 常函數:在類的成員函數的括號后面,{}前面,加const就是常函數。常函數內不允許修改成員變量,除了mutable定義的成員變量。在常函數中不能修改指針指向的值,如this->m_Age=30不合法。可以在變量前加上mutable關鍵字mutable后就可以修改了。如果類成員函數func()定義為常函數:int func()const{...}即在類的成員函數后面加const,相當于【const 類名 * const this】中的第一個const,修飾的是this指針,this指針指向的值也不能修改;又由于本來this指針就不能改變指向,所以這種寫法就既不能改變this指針的指向,也不能改變this指針調用的成員變量的值,即在該函數中this->m_Age=30和 this = NULL 都不合法。常對象:初略記了一下,并不完整。 常對象只能調用常成員函數,常對象不允許修改成員變量,但是并不是說常對象所屬的類的成員函數都是常成員函數,他也包括非常成員函數。 1.常函數成員既可以使用常數據成員也可以使用非常數據成員; 2.只有常成員函數才可以操作常對象,即常對象只能調用常成員函數。class Person{public:int m_Age;mutable int m_Height; //加上mutable關鍵字后就可以對const限定不能修改值的變量進行賦值了。 }; void test() {const Person person1; //不能修改常對象person1的各個成員變量的值。person1.m_Age = 30; //不合法person1.m_Height = 180; // 合法 }const有兩個作用,1.表示只讀;2.表示常量。C++11中可以使用const來表示只讀,用constexpr表示常量表達式。 常量表達式可以在編譯階段就直接計算出來,提高程序運行效率。而非常量表達式只能在程序運行時計算出來。可以使用constexpr限定函數也為常量表達式,前提是該函數中只能操作常量。假如該函數中使用了任意非常量的操作,例如for(it i=0;i<10;++i)循環語句,constexpr就會被忽略。=================================================================
9. a.cc文件中調用另一個b.cc文件中的函數
(1)兩個.cc文件(或者.h文件)不要互相#include,否則會出錯。 (2)每個.cc(.h)文件一般只定義一個class,一個a.cc(a.h)文件要使用到另一個b.cc(b.h)文件中的函數func時候,首先在a.h中包含b.h頭文件#a.h,然后使用class_name在a.h中的類中聲明一個對象bb_,然后在a.cc中使用bb_.func()這種用法。//a.hclass a{};//b.hclass b{void func();};a.cc中要使用b.h中的func(),需要在class a{}中這么聲明:class a{b bb_;} 然后在需要使用到b.h中的func()中的地方這么使用:bb_.func();=================================================================
10.視頻編碼格式與封裝格式,opencv2.4.8不能讀取h265編碼格式的視頻文件
(1)編碼格式是編碼格式,封裝格式(也可以說容器)是封裝格式。編碼格式有H.265、JPEG、MPEG-4 Video等,封裝格式有MP4、AVI等。 opencv2.4.8不支持讀取h.265編碼格式,opencv3.4可以讀取h.265編碼格式的視頻。所以還是簡單粗暴的這么干吧: ffmpeg -i source_video.mp4 -vcodec mpeg4 final_video.mp4(2)轉換MP4到avi如果有模糊,那是因為碼率設置不當,視頻信息有損失。 因此使用MP4Box直接把h265的封裝格式轉換為avi封裝格式,速度相當于拷貝,肯定不存在改變編碼方式,此時MP4的編碼格式還是H265。 安裝MP4Box可以參考https://blog.csdn.net/tianlong_hust/article/details/9273875,安裝時間稍微有點長sudo apt-get install libmp4v2-dev MP4Box -add 3_20_0_50_26_camera72.h265 -fps 25 -new 3_20_0_50_26_camera72.avi=================================================================
11.當使用imshow顯示每一幀畫面時,出現卡頓的解決辦法
讀取每一幀在哪個{}中,imshow()就應該在哪個{}中,必須出現在同等級的{}中,不能出現在他下面的二級{{imshow()}}中。否則,當二級{}不符合,執行不到二級{}里面的imshow()時候,就會出現畫面卡頓。
=================================================================
12.cv::VideoWriter的使用,把圖片寫進video
//注意:輸出的視頻名稱中必須有數字,否則會報錯,CAP_IMAGES: can't find starting number (in the name of file):....,可以查看源碼,這個錯誤不查看源碼根本就找不到問題所在。cv::VideoWriter video_writer_;int frame_count_;//初始化 if (FLAGS_use_test_mode) {// int codec = CV_FOURCC('M', 'J', 'P', 'G');int codec = cv::VideoWriter::fourcc('M', 'J', 'P', 'G');const std::string output_video_file = FLAGS_camera_name + ".avi"; //視頻名稱必須包含數字video_writer_.open(output_video_file, codec, 10, cv::Size(1920, 1080));if (!video_writer_.isOpened()) {SERROR << "Can't create output video file: " << output_video_file;return;}}//當程序運行到這里時,就開始把圖片寫進video.if (FLAGS_use_test_mode) {SRETURN_IF(FLAGS_skip_frame_num <= 0);if ((frame_count_ < FLAGS_max_save_frame_count * FLAGS_skip_frame_num) &&((frame_count_ % FLAGS_skip_frame_num) == 0)) {cv::Mat rgb_img=frame;if ((rgb_img.cols != 1920) || (rgb_img.rows != 1080)) {cv::Mat dst_img;cv::resize(rgb_img, dst_img, cv::Size(1920, 1080), 0, 0,cv::INTER_NEAREST);video_writer_.write(dst_img);} else {video_writer_.write(rgb_img); //圖片寫進視頻}}if (frame_count_ > FLAGS_max_save_frame_count * FLAGS_skip_frame_num) {video_writer_.release();}++frame_count_;} // if FLAGS_use_test_mode=================================================================
13.C++11多線程join()和detach()的理解
原文鏈接:https://blog.csdn.net/qq_36784975/java/article/details/87699113
上面這個鏈接的博客中介紹得很詳細.
join()函數,是一個等待線程完成函數,主線程需要等待子線程運行結束了才可以結束.
detach()函數,稱為分離線程函數,使用detach()函數會讓線程在后臺運行,即說明主線程不會等待子線程運行結束才結束.
總結
join()函數是一個等待線程函數,主線程需等待子線程運行結束后才可以結束(注意不是才可以運行,運行是并行的),如果打算等待對應線程,則需要細心挑選調用join()的位置
detach()函數是子線程的分離函數,當調用該函數后,線程就被分離到后臺運行,主線程不需要等待該線程結束才結束.
=================================================================
14.獲取鼠標動作進行相應處理
int main(){cv::imshow("EnvFusion", img); //這里顯示一個窗口/*<!-- 在窗口上進行鼠標操作就使用 cv::setMouseCallback()--><!-- 可進行的操作有 滑輪滾動,左鍵按下,右鍵按下,鼠標移動,還可以組合,比如鼠標左鍵按下并且鼠標移動,就是鼠標在窗口中拖動 --><!-- 該函數也會獲取鼠標點擊的x,y -->*/cv::setMouseCallback("EnvFusion", OnMouseAction); }// <!-- 鼠標操作回調函數 -->void OnMouseAction(int event, int x, int y, int flags, void *ustc) {double value;float step = 0.02;switch (event) {case CV_EVENT_MOUSEWHEEL:value = cv::getMouseWheelDelta(flags);if (value > 0)map_scale = map_scale * 0.9;else if (value < 0)map_scale = map_scale * 1.1;if (map_scale > 20) map_scale = 20;if (map_scale < 0.05) map_scale = 0.05;break;case CV_EVENT_LBUTTONDOWN:MousePress(x, y);map_down = true;prept = cv::Point(x, y);break;case CV_EVENT_LBUTTONUP:map_down = false;break;default:break;}if (map_down == true && event == CV_EVENT_MOUSEMOVE) //左鍵按下且鼠標移動{cv::Point curpt = cv::Point(x, y);cv::Point dpoint0 = curpt - prept;dpoint += dpoint0;prept = curpt;} else {dpoint = cv::Point(0, 0);}} void MousePress(int x, int y) {<!-- 這里的x,y是傳進來的鼠標點擊位置的坐標 --><!-- 一般是用來判斷鼠標點擊的坐標(x,y)是不是在某個范圍內,是的話就進行某種操作 --> }=================================================================
15.查找與刪除map和vector容器中指定元素
(1) map
查找map的關鍵字:
std::map<std::string,int> map_name_;
auto iter = map_name_.find("key_name"); if (iter != map_name_.end()) {//找到了該關鍵字,進行的操作 }刪除map的指定key值
有兩種方法:
方法1:直接刪除key
map_name_.erase("key_name");
方法2:使用迭代器刪除
auto iter = map_name_.find("key_name"); if (iter != map_name_.end()) {map_name_.erase(iter); //刪除 }(2) vector
刪除vector的指定元素"123"
方法1:使用迭代器
不同于map(map有find方法),vector本身沒有find這一方法.
方法2:使用 std::remove_if
std::vector<std::string> vct_name_; vct_name_.erase(std::remove_if(vct_name_.begin(),vct_name_.end(),[](std::string str) { return str == "123"; }),vct_name_.end());15.1 map 的insert和emplace方法
參考地址: https://www.cnblogs.com/khacker/p/10479801.html
對于std::map、std::unordered_map的insert(std::make_pair(key, value))和emplace(std::make_pair(key, value))重復插入同一個key的操作,二者都不會替換原先的key對應的value值,只有索引[]操作會改變value。
15.2 map的erase(iter)需注意,和vector不一樣
https://blog.csdn.net/zhangyueweia/article/details/50293965
#include <iostream> #include <map> #include <string> #include <vector>// g++ -std=c++11 main.cpp -o mainint main() {// std::vector可以直接刪除iter后不影響遍歷std::vector<std::string> name_vct = {"Alibaba", "Baidu", "CMD", "DDS","Ella"};std::vector<std::string>::iterator it = name_vct.begin();while (it != name_vct.end()) {std::cout << "*it= " << *it << std::endl;if (*it == "CMD") {name_vct.erase(it);std::cout << "erase后 *it= " << *it << std::endl;} else {++it;}}std::cout << std::endl;std::map<std::string, int> name_age_map = {{"AAA", 21}, {"Bob", 22}, {"Cool", 23}, {"Daisy", 24}};/** 下面這種方式會出錯。 std::map 刪除iter后繼續遍歷會造成double free **//*auto it0 = name_age_map.begin();while (it0 != name_age_map.end()) {std::cout << "key= " << it0->first << std::endl;if (it0->first == "Bob") {name_age_map.erase(it0);//當這條語句執行完后,it1就是一個非法指針,如果再執行++就會出錯. std::cout<< "erase后 it->first= " << it0->first << std::endl; } else {++it0;}}*//** --方法1* std::map刪除iter需要這么使用,使用一個臨時變量保存迭代器后,將迭代器自增1**//*while (it1 != name_age_map.end()) {std::cout << "key= " << it1->first << std::endl;if (it1->first == "Bob") {auto iter_tmp = it1;++it1;name_age_map.erase(iter_tmp);std::cout << "erase后 iter_tmp->first= " << iter_tmp->first <<std::endl; std::cout << "erase后 it->first= " << it1->first <<std::endl;} else {++it1;}}*//** --方法2* std::map刪除iter需要這么使用,it2 = name_age_map.erase(it2);**//*auto it2 = name_age_map.begin();while (it2 != name_age_map.end()) {std::cout << "key= " << it2->first << std::endl;if (it2->first == "Bob") {it2 = name_age_map.erase(it2);std::cout << "erase后 it->first= " << it2->first << std::endl;} else {++it;}} *//** --方法2* std::map刪除iter需要這么使用,name_age_map.erase(it3++);**/auto it3 = name_age_map.begin();while (it3 != name_age_map.end()) {std::cout << "key= " << it3->first << std::endl;if (it3->first == "Bob") {name_age_map.erase(it3++);std::cout << "erase后 it->first= " << it3->first << std::endl;} else {++it3;}} }=================================================================
16.sprintf的用法
double db=10.123456; char aaa[20]; std::sprintf(aaa,"qqq:%.1f",db); //aaa[]就變成了qqq:10.1,保留一位小數 cv::putText(img, aaa, cv::Point(100 , 100),cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(255, 255, 0), 2); //在img上顯示文字內容 std::string str(aaa); //str就變成了qqq:10通常情況下,需要批量命名文件時候,會用到
char pic[50]; static int count = 0; if (count % 10 == 0) {//下面的track_id是一個整型變量。picture/目錄需要自己新建。不想自己手動創建可以百度查詢如何使用linux下c++創建文件夾。sprintf(pic, "picture/id_%d_%d.png", track_id, count); cv::imwrite(pic, rgb_img); //這句話會把命名的文件存在picture目錄下。 } ++count;=================================================================
std::shared_ptr、std::make_shared、 .get() 、.data()、void *p 的用法、裸指針
(1)shared_ptr能夠記錄對象被引用的次數,主要被用來管理動態創建的對象的銷毀。
shared_ptr是一種智能指針(smart pointer)。shared_ptr的作用有如同指針,但會記錄有多少個shared_ptrs共同指向一個對象。
這便是所謂的引用計數(reference counting)。一旦最后一個這樣的指針被銷毀,也就是一旦某個對象的引用計數變為0,這個對象會被自動刪除。這在非環形數據結構中防止資源泄露很有幫助。
(2)如果事先知道所需內存空間,使用靜態內存是最簡單的解決方案。
但是,在程序設計的過程中,往往會遇到需要開辟一個未知大小的內存空間,該空間根據程序所需發生大小的變化,此空間稱為動態內存。
程序設計中使用動態內存的原因可能如下:
(1)程序不知道自己需要多少對象;
(2)程序不知道所需對象的準確類型;
(3)程序需要在多個對象之間共享數據
shared_ptr的用法:可以指向特定類型的對象,用于自動釋放所指的對象。
make_shared的用法:make_shared在動態內存中分配一個對象并初始化它, 返回指向此對象的shared_ptr,與智能指針一樣,make_shared定義在頭文件memory中;
當要用make_shared時,必須指定想要創建的對象類型,定義方式與模板類相同,在函數名之后跟一個尖括號,在其中給出類型;
如make_shared<int>p3 = make_shared<int>(42)
一般采用auto定義一個對象來保存make_shared的結果,如auto p1 = make_shared<int>(42);
(3) std::shared_ptr::get() 返回存儲的指針,指向 shared_ptr 對象解引用的對象,通常與其擁有的指針相同。
(4)C++ vector.data()返回指向vector中第一個數據的指針,或空vector之后的位置
(5) void的字面意思是“無類型”,void *則為“無類型指針”,void *可以指向任何類型的數據。
void指針指向的數據類型未定,將其值賦給其他值時要類型轉換,但是任何類型的指針都可以直接賦值給void*,無需進行強制類型轉換
參考https://blog.csdn.net/qq_33890670/article/details/79964262
(6) 裸指針,一般指的就是類似 int *p;這種方式!!
一般情況下,可以用智能指針替換裸指針。
參考:https://blog.csdn.net/qq_38684512/article/details/103421567
=================================================================
18.選擇使用memcpy_s,strcpy_s還是選擇strcpy,memcpy? memset()的用法
memcpy_s,strcpy_s函數明確的指定了目標內存的大小,能夠清晰的暴露出內存溢出的問題,而普通的strcpy,memcpy則不會。
為了保證內存拷貝有足夠的空間,防止筆誤,【盡量使用memcpy_s代替memcpy】。
(1)memcpy_s()的用法:
memcpy_s 復制src 的 count 字節到 dest;如果dest或 src是null指針,或者 numberOfElements 為緩沖區太小,會拋出異常。
void Raw2Mat(CameraPublishDecodedStruct src_yuv_img,cv::Mat &frame) {std::shared_ptr<hiai::ImageData<u_int8_t>> input_image =std::make_shared<hiai::ImageData<u_int8_t>>();input_image->size = src_yuv_img.raw_data.size();// input_image->data = std::shared_ptr<u_int8_t>(new // u_int8_t[src_yuv_img.raw_data.size()]); // memcpy_s(input_image->data.get(), src_yuv_img.raw_data.size(),// src_yuv_img.raw_data.data(), src_yuv_img.raw_data.size());//上面注釋的兩句話可以用下面這句代替input_image->data = std::shared_ptr<u_int8_t>(const_cast<u_int8_t *>(src_yuv_img.raw_data.data()), [](u_int8_t *p) {});input_image->height = src_yuv_img.height;input_image->width = src_yuv_img.width;cv::Mat rgb_img(input_image->height, input_image->width, CV_8UC3);cv::Mat yuyv_img =cv::Mat(input_image->height * 3 / 2, input_image->width, CV_8UC1,static_cast<void *>(input_image->data.get()));cv::cvtColor(yuyv_img, rgb_img, cv::COLOR_YUV420sp2RGB);frame = rgb_img; }(2) memset()函數的用法
memset函數詳細說明
1)void *memset(void *s,int c,size_t n)
總的作用:將已開辟內存空間 s 的首 n 個字節的值設為值 c。
2) memset()函數常用于內存空間初始化。如:
3)memset可以方便的清空一個結構類型的變量或數組。 如:
struct sample_struct{char csName[16];int iSeq;int iType;};對于變量:struct sample_strcut stTest;一般情況下,清空stTest的方法:stTest.csName[0]='/0';stTest.iSeq=0;stTest.iType=0;用memset就非常方便:memset(&stTest,0,sizeof(struct sample_struct));如果是數組:struct sample_struct TEST[10];則memset(TEST,0,sizeof(struct sample_struct)*10);#include <mem.h>void* memset(void* s, int c, size_t n){unsigned char* p = (unsigned char*) s;while (n > 0) {*p++ = (unsigned char) c;--n;}return s;}memset()的函數, 它可以一字節一字節地把整個數組設置為一個指定的值。memset()函數在mem.h頭文件中聲明,它把數組的起始地址作為其第一個參數,第二個參數是設置數組每個字節的值,第三個參數是數組的長度(字節數,不是元素個數)。其函數原型為:
void *memset(void*,int,unsigned);
其中void*表示地址。
例如,下面的代碼用數組做參數傳遞給標準函數memset(),以讓其將數組設置成全0:
memset()的第一個實參是數組名,數組名作參數即數組作參數,它僅僅只是一個數組的起始地址而已。
在函數memset()棧區,從返回地址往上依次為第1,2,3個參數。第1個參數中的內容是main()函數中定義的數組ia1的起始地址。第2個參數是給數組設置的值(0),第3個參數是數組的長度(50*2)。函數返回時,main()函數的數組中內容全置為0。
19.C++強制類型轉換(dynamic_cast,static_cast, const_cast, reinterpret_cast)
參考 https://blog.csdn.net/muyuyuzhong/article/details/82699374
20.編譯proto的問題
重裝protoc可參考 https://blog.csdn.net/u013498583/article/details/74231058
查看當前protoc版本: protoc --version
查看protoc安裝位置:which protoc
查找protoc相關文件:sudo find / -name protoc
編譯proto文件
protoc caffe.proto --cpp_out=./ 生成caffe.pb.h、caffe.pb.cc文件
protoc caffe.proto --python_out=./ 生成caffe_pb2.py文件
假如環境中裝了一個以上的protoc版本,可使用protoc雙擊tab鍵,會彈出所有版本(protoc protoc_2.6.1,其中protoc是默認版本,protoc --version是查看當前默認protoc版本)。
如果我們要使用非默認版本protoc編譯proto,可以protoc_2.6.1 caffe.proto --cpp_out=./這種方式編譯。
在編譯MobileNet-YOLO工程時,使用protoc protoc_2.6.1編譯后的 caffe.pb.h 、caffe.pb.cc 文件粘貼到 caffe\include\caffe\proto即可。
21.無符號整型變量比較大小時用減法越界的bug問題
無符號(unsigned)和有符號(signed)兩種類型(float 和double 總是帶符號的),在除char以外的數據類型中,默認情況下聲明的整型變量都是有符號的類型;char在默認情況下總是無符號的。在除char以外的數據類型中,如果需聲明無符號類型的話就需要在類型前加上unsigned。
當兩個無符號整型變量如std::uint64_t x1,x2;比較大小時,應使用if(x1>x2)這種方式,別用if(x1-x2>0),因為對于無符號整形變量,二者相減是不會為負數的.
只有符號變量直接相減來比較二者的差值。
如果使用了無符號整型變量,可先限定條件if(x1>x2),然后再使用減法delta=x1-x2.
22.攝像頭焦距和視場角
攝像頭焦距和視場角:
廣角鏡頭: 視場角FOV越大,焦距越小,則視野越大,物體越小,看的越近,遠處的物體看不清;
長焦鏡頭: 視場角FOV越小,焦距越大,則視野越小,物體越大,看的越遠,近處的物體看不到;
比亞迪和106等mdc設備車使用視場角命名的攝像頭,如28度,60度,100度,182度. 28度看的最遠.
福田1和福田2使用焦距命名的攝像頭:如6mm,12mm,25mm. 25mm攝像頭看的最遠.
23.RGB和BGR的轉化,通道分離與合并
opencv中默認讀取的圖片格式是BGR,并非RGB.
下面是opencv的更直接BGR轉RGB方法:
cv::cvtColor(bgr_img, rgb_img, cv::COLOR_BGR2RGB);
下面是通道的分離與合并:
std::vector<cv::Mat> MatVct_1(3);cv::split(src_img, MatVct_1); //這句話把src_img分離為三個Matstd::vector<cv::Mat> MatVct_2; MatVct_2.push_back(MatVct_1[0]); //如果這里[0]改為[2],下面的[2]改為[1],就是bgr轉rgb了MatVct_2.push_back(MatVct_1[1]);MatVct_2.push_back(MatVct_1[2]);cv::merge(MatVct_2, dst_img); //這句話把MatVct_2合并為一個3通道的Mat24.重寫、覆蓋、using、typedef
父類如果定義某非虛函數func1(int,int),子類定義了函數func1(double),那么子類不能再調用func1(2,3),因為子類只要定義了父類同名函數,不管他們參數類型和個數是否不同,都會隱藏父類的同名函數,相當于覆蓋了父類的所有同名函數。
如果自己又要自己定義還同名函數,又想使用父類的這同名函數,c++11可以通過在子類中使用using 父類名::func1;即可。
注意,重寫和覆蓋不是一個意思。重寫是虛函數在子類中重新定義,使用override,override也可以不寫,加上override是規范,語義更清晰明白它是重寫虛函數。
覆蓋是子類定義同名函數覆蓋掉父類同名函數。
同理,如果父類有幾個復雜的構造函數,子類想繼承父類所有構造函數,c++11可以在子類中使用 using 父類名::父類名;如:
class Base{public:Base();Base(const Base &){// 很復雜的一些初始化語句};func1(int,int); };class Child : Base{public:using Base::Base; //加上這句話,子類就繼承了父類的所有構造函數using Base::func1(); // 加上這句話,就可以使用基類被覆蓋(隱藏)的所有同名函數func1了。func1(); //該定義會直接覆蓋掉基類所有同名函數func1. 解決辦法就是使用上面的using Base::func1(); };using 的用法:
using namespace std; using namespaceA::namespaceB::func; // 調用命名空間B下的func()函數,該聲明語句不要寫成func(),不能加括號。using anotherName = int; //取別名。一般用在某類型特別長的時候,取個短點的別名。注意區分typedef,typedef和using在取別名上作用基本一樣,只是順序不同,如:typedef int anotherName;
struct 和 class的作用基本一樣,但是常規用法是,struct 訪問類型默認是public,并且一般用在把一些變量封裝成一個結構體變量。class就是通常的用法。
25.靜態成員靜態成員函數/變量、單例模式
靜態成員變量和靜態成員函數 是屬于類,不是屬于對象。
非靜態成員變量和非靜態成員函數,是屬于對象的,如果沒有實例化對象,那么他們是不存在的。
所以調用靜態成員函數時,既可以使用 類名::靜態成員函數 的方式來調用,也可以使用對象.靜態成員函數函數 的方式來調用。
this是指向對象的,沒有把類實例化成對象的話,是沒有this指針的,所以,在靜態成員函數的定義中,不能使用this指針。
非靜態成員變量屬于對象,不屬于類,沒有實例化對象,就不存在非靜態成員變量。靜態成員變量是在編譯階段生成,而非靜態成員變量是在運行時生成。
靜態成員函數屬于類,沒有this指針,所以它不能對非靜態成員變量直接進行操作,但是靜態成員函數可以調用非靜態成員函數(非靜態成員函數雖然可以操作靜態成員變量,但是應該禁止),
例如單例模式的公共接口靜態成員函數Instance()就可以調用所有成員函數。
即,靜態成員函數可以被類直接調用,也可以被對象來調用。非靜態成員函數既可以被靜態成員函數調用,也可以被非靜態成員函數調用。
對于單例模式,是把類A的構造函數私有化,然后私有化一個靜態對象static A a;并提供一個公有的靜態函數static A *Instance(){return a;},該函數返回值為靜態A對象a。
class A{public:static A &Instance(){ //引用類型,在外部調用其他public函數方法 A::Instance().func();return a;}//static A *Instance(){ //指針類型,在外部調用其他public函數方法 A::Instance()->func();// return a;//}void func(){std::cout<<"調用func()";}private:A();A(const A &);A& operator=(const A&);static A a; };這樣在外部就只能使用A::Instance()來調用成員函數,并且只能這樣調用,返回的是類A的靜態對象a,這樣就能保證只有一個實例。
如果要調用類A的非靜態函數func(),可以使用A::Instance()->func()
26.proto相關用法
例如,有 proto_name.proto文件,把這個 proto_name.proto文件編譯后,會產生 proto_name.pb.h 和 proto_name.pb.cc, 生成的.h文件中的class都繼承自::google::protobuf::Message類,Message類提供了一些方法可以檢查或者操作整個message,包括:
bool IsInitialized() const; // 檢查是否所有required變量都已經初始化;string DebugString() const; // 返回message的可閱讀的表示,主要用于調試程序;void CopyFrom(const Person& from); // 使用一個message的值覆蓋本message;void Clear(); // 清空message的所有成員變量值。每個message類都提供了寫入和讀取message數據的方法,包括
bool SerializeToString(string* output) const; // 把message編碼進output。 bool ParseFromString(const string& data); // 從string解碼到messagebool SerializeToArray(char* buf,int size) const; // 把message編碼進數組buf.bool ParseFromArray(const char* buf,int size); // 把 buf解碼到message。此解碼方法效率較ParseFromString高很多,所以一般用這種方法解碼。bool SerializeToOstream(ostream* output) const; // 把message編碼進ostreambool ParseFromIstream(istream* input); // 從istream解碼到message備注:發送接收端所使用的加碼解碼方法不一定非得配對,即發送端用SerializeToString 接收端不一定非得用ParseFromString ,可以使用其他解碼方法。用法1:對外接口的消息結構的兼容性
當某個對外接口的消息結構 msgA 中的某個變量的結構總是要根據需求而頻繁增刪修改的時候,一般會在這個消息結構 msgA 中定義一個 std::string proto_data 的變量,
這樣只需要修改 proto_name.proto文件中的內容,而不需要總是修改 msgA(尤其是當修改msgA可能需要修改框架協議的時候,總之就是代價比較大),使用proto方法來解決這個問題就特別方便。
原始結構:
struct msgA{MsgHeader header;bool is_ok;structBBB msgbbb; //當這個結構體 structBBB 增刪修改結構比較麻煩時,會造成頻繁修改對外接口 msgA 的麻煩。 }方法:
先定義一個 proto_name.proto文件,把這個 proto_name.proto文件編譯后,會產生 proto_name.pb.h 和 proto_name.pb.cc,
對其中定義的變量賦值是使用 set_xxx(111) 函數進行賦值,賦值完成后再序列化之后進行打包發送;
發送之前使用 proto_name.SerializeToString(&data) 把該 結構序列化為string類型,然后再發送。
原始結構 修改為:
下面的函數,我們封裝了proto自帶的SerializeToString和ParseFromString方法
// msg轉換成string template <class T> void MsgToString(const T msg, std::string &data) {msg.SerializeToString(&data); }// 將string轉換成msg template <class T> void StringToMsg(const std::string data, T &msg) {msg.ParseFromString(data); }用法2:
當我們需要使用配置文件來給許多變量進行賦值的時候,就可以把這些變量全都定義在 config.proto中,然后建立一個config.pb.txt文件,在config.pb.txt文件中對config.proto中的變量進行設置。
后續進行一些讀取config.pb.txt文件中的內容,然后需要的地方獲取這些內容。
代碼涉及到文件讀取解析proto等等的內容,有點復雜,暫時不做展開,這里只是指出有這個用法。
27.深拷貝、淺拷貝、拷貝構造函數 之間的關系
關于拷貝構造、深拷貝、淺拷貝參考https://blog.csdn.net/qq_29344757/article/details/76037255
淺拷貝只拷貝指針,不新開辟內存。深拷貝會另外開辟一塊內存,內容和拷貝的對象一樣。
所謂拷貝構造,傳入的參數限定于是同一類之前創建的對象,用它來初始化新建的對象。
拷貝構造主要就是把別的對象的成員變量的值賦值給自己的成員變量。或者說,直接新開辟一段內存,然后把傳入的對象的成員變量的值賦值給自己。并不能直接把其他對象直接復制給自己,沒有這種用法。
對于傳入的參數是例如int類型之類的構造函數,他不叫拷貝構造函數,它只是簡單的構造函數。
默認的拷貝構造函數,對于指針變量是淺拷貝,對于非指針變量是深拷貝。這樣在析構時會造成double free的錯誤。所以必須自己定義拷貝構造函數,在里面對指針變量新分配內存后,再把別的對象的指針里面的值賦給它。
默認拷貝賦值函數也是淺拷貝。
所以類的成員變量中有指針變量時,必須對拷貝構造函數和拷貝賦值函數重新定義。
拷貝構造的作用是防止淺拷貝。
因為如果我們不使用深拷貝而使用淺拷貝的話,對象a淺拷貝對象b,當淺拷貝的對象b析構后,b所指向的內存已經釋放,那么a所指向的內存也被釋放了,當a自己再析構的時候就析構不了了。
還有就是淺拷貝時,任何一個對象對該值進行修改都會影響另一個對象中的值。
默認拷貝構造函數定義舉例:
class A{ public: //默認拷貝構造函數為A(const A& a){tmp1=a.tmp1;//深拷貝,不同對象tmp1的地址不一樣ptr=a.ptr;//淺拷貝,因為ptr為指針變量}private:int tmp1;int *ptr; };我們自己需要重新定義的拷貝構造函數:
A(const A& a){tmp1=a.tmp1;//深拷貝,不同對象tmp1的地址不一樣ptr= new int;*ptr=*(a.ptr);//深拷貝,因為他們的ptr的地址不一樣了。 }移動構造函數
移動構造函數就是右值引用構造函數。他是為了實現淺拷貝,復用其他對象中的資源(堆內存),延長其他臨時對象的生命周期。
A(A&& a):ptr(a.ptr){a.ptr=nullptr;//調用移動構造函數,會先把a對象的指針變量ptr先賦值給自己的指針變量ptr,然后把a.ptr指向空指針,這樣a在析構的時候就不會把a.ptr本來指向的內容給釋放了。這樣自己的ptr指針還是指向那塊內存。注意,指針指向的那塊內存的值是通過*p=?的方式來修改的,所以修改指針指向并不是修改指針指向的內存的值,不要混淆。 }另外,關于拷貝構造函數和移動構造函數,他們傳入的形參都一樣,怎么知道調用哪個呢?程序會判斷這個形參是不是臨時對象,如果是臨時對象,就會調用移動構造函數。
注意上面說的傳入的形參,也可以是通過類似A a=func()這種使用方式。其中
A func(){A a;return a; //返回一個臨時對象。 }所謂拷貝構造,傳入的參數限定于是同一類之前創建的對象,用它來初始化新建的對象。
如果類中有指針類型的成員變量,那就必須定義拷貝構造函數,否則你讓別的對象的指針成員變量給他賦值,就是淺拷貝,有內存風險。
`void test(int a, int b){ }`test是函數的首地址,他是一個函數,類型是void()。 &test表示一個指向函數test這個對象的地址,他是一個指針類型是void(*)()。 所以test和&test所代表的地址值是一樣的,但是類型不一樣。總結
以上是生活随笔為你收集整理的【c++】0.C++笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Tools】python环境操作笔记
- 下一篇: 【opencv】1.opencv安装、编