2016年9月11日
cmake學(xué)習(xí)(一)靜態(tài)庫與動態(tài)庫構(gòu)建
(.so)共享庫,shared object:節(jié)省空間,在運(yùn)行時(shí)去連接,如果執(zhí)行機(jī)器上沒有這些庫文件就不能執(zhí)行。
(.a)靜態(tài)庫,archive:靜態(tài)庫和程序化為一體,不會分開。
通過 ldd命令可以查看一個可執(zhí)行程序所依賴的的共享庫。
使用環(huán)境變量LD_LIBRARY_DIRECTORY可以指定共享庫位置
一、編譯共享庫:
ADD_LIBRARY(hello SHARED ${SHARED_LIBRARY})
二、添加靜態(tài)庫:
ADD_LIBRARY(hello STATIC ${STATIC_LIBRARY})
因?yàn)槟J(rèn)規(guī)則是不能有相同名字的共享庫與靜態(tài)庫,所以當(dāng)生成靜態(tài)庫的時(shí)候(so后綴),共享庫會被刪除,因?yàn)橹荒茉试S一個名字存在,相同名字的會被替代(hello),所以需要通過SET_TARGET_PROPERTIES()來解決這個問題,例子:
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
cmake在構(gòu)建一個target的時(shí)候,會刪除之前生成的target,一樣是通過設(shè)置SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)來達(dá)到目的
三、動態(tài)庫的版本號:
同樣是通過SET_TARGET_PROPERTIES()來設(shè)置
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
VERSION:動態(tài)庫版本
SOVERSION:API版本
最后生成的結(jié)果是:
libhello.so.1.2
libhello.so.1->libhello.so.1.2
libhello.so->libhello.so.1
四、安裝:
INSTALL(TARGETS hello hello_static
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
INSTALL(TARGETS hello.h
DESTINATION include/hello)
其他常用的屬性 PERMISSIONS:設(shè)置權(quán)限;RATTERN:設(shè)置正則表達(dá)式
Summary:
ADD_LIBRARY():添加一個庫,共享庫,靜態(tài)庫,模塊
SET_TARGET_PROPERTIES():設(shè)置輸出名稱,版本號,解決相同target被刪除的問題
GET_TARGET_PROEERTIES():與SET功能相對
cmake學(xué)習(xí)(二)常用變量和常用環(huán)境變量
一、變量的引用方式是使用“${}”,在IF中,不需要使用這種方式,直接使用變量名即可
二、自定義變量使用SET(OBJ_NAME xxxx),使用時(shí)${OBJ_NAME}
三、cmake的常用變量:
CMAKE_BINARY_DIR,PROJECT_BINARY_DIR,_BINARY_DIR:
這三個變量內(nèi)容一致,如果是內(nèi)部編譯,就指的是工程的頂級目錄,如果是外部編譯,指的就是工程編譯發(fā)生的目錄。
CMAKE_SOURCE_DIR,PROJECT_SOURCE_DIR,_SOURCE_DIR:
這三個變量內(nèi)容一致,都指的是工程的頂級目錄。
CMAKE_CURRENT_BINARY_DIR:外部編譯時(shí),指的是target目錄,內(nèi)部編譯時(shí),指的是頂級目錄
CMAKE_CURRENT_SOURCE_DIR:CMakeList.txt所在的目錄
CMAKE_CURRENT_LIST_DIR:CMakeList.txt的完整路徑
CMAKE_CURRENT_LIST_LINE:當(dāng)前所在的行
CMAKE_MODULE_PATH:如果工程復(fù)雜,可能需要編寫一些cmake模塊,這里通過SET指定這個變量
LIBRARY_OUTPUT_DIR,BINARY_OUTPUT_DIR:庫和可執(zhí)行的最終存放目錄
PROJECT_NAME:你猜~~
四、cmake中調(diào)用環(huán)境變量
1.Using $ENV{NAME} : invoke system environment varible.
We can use "SET(ENV{NAME} value)" as well. note that the "ENV" without "$".
2.CMAKE_INCLUDE_CURRENT_DIR equal to INCLUDE_DIRECTORY(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
五、其他的內(nèi)置變量
1.BUILD_SHARED_LIBS:set the default value when using ADD_LIBRARY()
2.CMAKE_C_FLAGS: set compiler for c language
2.CMAKE_CXX_FLAGS: set compiler for c++ language
六、區(qū)分debug和release
在工程目錄下,cmake -DCMAKE__BUILD_TYPE=DEBUG(RELEASE),再執(zhí)行make
七、指定編譯32bit或64bit程序
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")
cmake學(xué)習(xí)(三)常用指令
一、基本指令:
INCLUDE_DIRECTORIES(${includedir}) #-I。
LINK_DIRECTORIES(${libdir}) #-L
TARGET_LINK_LIBRARIES(helloworld ${linkflags}) #-l
ADD_DEFINITIONS(${cflags}) #-D
1、ADD_DEFINATIONS:向C/CPP添加宏定義,相當(dāng)于gcc中的-D,參數(shù)之間用空格分割
2、ADD_DEPENDICIES(target_name, depend_name):定義target對其他target的依賴關(guān)系
3、AUX_SOURCE_DIRECTORY(dir VARIBLE):把目錄下的所有源文件保存在變量中,基本用來創(chuàng)建源文件列表
4、ADD_EXECUTABLE:指定目錄,生成執(zhí)行文件
5、EXEC_PROGRAM:外部調(diào)用指令,可移執(zhí)行任何外部命令,后面加參數(shù),例子如下:
EXEC_PROGERAM(ls ARGS"*.c" OUTPUT_VARIBLE LS_OUTPUT RETURN_VALUE LS_RVALUE)
IF(not LS_RVALUE)
MESSAGE(STATUS "xxx")
ENDIF(not LS_RVAULE)
PS.這里執(zhí)行l(wèi)s *.c指令,執(zhí)行成功的話,返回0。
6、FILE指令:
FILE(WRITE file_name "content")
FILE(APPEND file_name "content")
FILE(READ file_name varible)
FILE(WRITE file_name "content")
7、FIND_系列指令:
LIBRARY( name path):
FIND_LIBRARY(Xorg X11 /usr/lib64)
IF(not Xorg)
MESSAGE(STATUS "no Xorg")
ENDIF(not Xorg)
FILE( name path)
PATH( name path)
PROGRAM( name path)
PACKAGE( [major.minor][QUIET][NO MODULE][[REQUIRED][COMPONTS][componts....]])
最后一條,用來調(diào)用放在CMAKE_MODULE_PATH下的Find.cmake模塊,也可以自定義Find模塊
首先通過SET(CMAKE_MODULE_PATH /home/...)來指定位置
8、控制指令:
IF(expression),ELSE(expression),ENDIF(expression)
express舉例:
否定:空,0,N,NO,OFF,F(xiàn)ALSE,NOTFOUND或_NOTFOUND
肯定:COMMAND cmd,EXISTS dir/file,variable MARCHES regex等等等等還有很多~~~隨用隨查吧
cmake學(xué)習(xí)(四)模塊的使用和自定義模塊
FIND_PACKAGE
每一個模塊都會產(chǎn)生如下變量
_FOUND
_INCLUDE_DIR
_LIBRARY or _LIBRARIES
如果_FOUND為真,把_INCLUDE_DIR加入到INCLUDE_DIRECTORIES中,_LIBRARY加入到TARGET_LINK_LIBRARIES中。
編寫屬于自己的FindHello模塊:
1.FIND_PATH(HELLO_INCLUDE_DIR hello.h /usr/include/hello /usr/local/include/hello)
2.FIND_LIBRARY(HELLO_LIBRARY_DIR NAMES hello PATH /usr/lib /usr/local/lib)
IF(HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
SET(HELLO_FOUND TRUE)
ENDIF(HELLO_INCLUDE_DIR)
3.FIND_PACKAGE([major.minor][QUIET][NO_MODULE]
[[REQUIRED|COMPONENTS][componets...]])
QUIET參數(shù):去掉輸出信息
REQUIRED參數(shù):共享庫是否是工程必須的,如果是必須的,那么找不到
如果在src中想調(diào)用hello模塊中的內(nèi)容
FIND_PACKAGE(HELLO)
為了可以讓工程找到FindHELLO.cmake
在主工程的CMakeList.txt中,SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_PATH}/cmake)
通過設(shè)置FIND_PACKAGE(HELLO QUIET)可以去掉輸出信息
Cmake CMAKE_BUILD_TYPE specification
That’s because no build type has been specified to CMake. The build type is a feature most IDE have, it allows you to compile your program in “debug” mode, for easily single-stepping through it with a debugger, or in “release” mode, with speed optimization enabled.
To fix this you simply need to specify a build type in the CMakeLists.txt file, in this way:
if( NOT CMAKE_BUILD_TYPE )
set( CMAKE_BUILD_TYPE Debug CACHE STRING
"Choose the type of build, options are: None Debug Release RelWithDebInfo
MinSizeRel."
FORCE )
endif()
when cmake is run without specifying the build type using -D CMAKE_BUILD_TYPE, it is the Debug mode that is selected as the default.
2016年6月28日
SDL是一個輕量級的,用C語言開發(fā)的多媒體庫。它包含了圖像繪制、文字繪制、事件處理、聲音播放等模塊。因?yàn)镾DL的易用以及它的擴(kuò)展庫的完整性,很多2D游戲都使用SDL開發(fā),其中就包括這幾年大熱的移動平臺上的游戲《憤怒的小鳥》。
然后說說從個人角度上看SDL的特點(diǎn)。
1.跨平臺。確確實(shí)實(shí)是跨了N個平臺,甚至包括NDS這種平臺。有了SDL,你甚至可以在windows、linux、Android上任意移植你的游戲。當(dāng)然,前提是你目標(biāo)平臺的編譯器認(rèn)識你的代碼( ̄▽ ̄)”。有了SDL泥甚至可以只用c語言開發(fā)安卓游戲喲。
2.開源。3.SDL2.0繪圖效率很高。事實(shí)上相較之SDL1.2我個人比較喜歡SDL2.0的原因也是在此。個人感覺(其實(shí)我沒看過源碼)SDL1.2應(yīng)該是個跟當(dāng)年的DirectDraw差不多的東西,像素填充什么的,有相當(dāng)程度上是要磨CPU的。而SDL2.0從繪圖方式上就革新了,拋棄了之前的surface與clip的模式,把實(shí)際繪制的東西改為了Texture,而把之前的surface改為了創(chuàng)建Texture的一個臨時(shí)環(huán)節(jié)。而texure,顧名思義,其實(shí)就是DirectX、OpenGL這些底層的3D硬件加速API的貼圖。
當(dāng)下DirectX、OpenGL標(biāo)準(zhǔn)下的顯卡的渲染管線無非就是:1.把頂點(diǎn)(可以理解成坐標(biāo))傳給顯卡 。2.把texture傳給顯卡。 3.告訴顯卡怎么處理這些數(shù)據(jù)(shader)。 4.顯卡把東西給你顯示出來。而基于DirectX、OpenGL(移動平臺是OpenGL ES)的SDL2.0,正是恰好地利用了當(dāng)下顯卡的能力。
4.SDL可以用作3D圖像引擎和底層DirectX/OpenGL API的中間層。當(dāng)然,其實(shí)如果把SDL這樣用的話,那就真是很薄的一層了:)
5.易用。這是相對而言的,比如在windows上,你用了SDL這個庫之后,基本就不用去理會Windows那些又臭又長用不著的參數(shù)又多的API了。我不是在討論信仰問題也不是要詆毀windows,我是在客觀陳述windows api那個要初始化一個窗口必須要堆100行代碼的設(shè)定實(shí)在是打擊初學(xué)者積極性的事實(shí)。
SDL_image、SDL_ttf、SDL_mixer、SDL_net 外部擴(kuò)展庫,也是不錯的選擇。性能沒有測試,僅從寫代碼角度上來說,個人感覺2.0將操作給弄得復(fù)雜了。
1.2---------------------------------------
只有SDL_Surface的概念,屏幕是surface,圖片,文字等都是surface,
只要將準(zhǔn)備好的各種圖片,貼到屏幕里去(SDL_BlitSurface);再刷一下屏幕(SDL_Flip全局的、或SDL_UpdateRect局部的)就ok了。。。
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Surface* screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);
SDL_Surface* bmp = SDL_LoadBMP("back.bmp");
SDL_BlitSurface(bmp, 0, screen, 0);
SDL_Flip(screen);
while(SDL_WaitEvent(&e)) {
switch(e.type) {
case SDL_QUIT:
return;
}
}
SDL_Quit();
2.0---------------------------------------
整出了SDL_Window,SDL_Renderer,SDL_Texture新的3個東西。
并且我要畫一張圖,先要得到surface,然后轉(zhuǎn)換為texture,再臨時(shí)貼到renderer,最后才刷屏。
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window* window = SDL_CreateWindow("hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
SDL_Surface* surface = SDL_LoadBMP("back.bmp");
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, 0, 0);
SDL_RenderPresent(renderer);
while(SDL_WaitEvent(&e)) {
switch(e.type) {
case SDL_QUIT:
return;
}
}
SDL_Quit();
其實(shí),我真心不覺得提出SDL_Window,SDL_Renderer,SDL_Texture這些概念先進(jìn)性在哪里?可能是與openGL的概念保持一致吧。
而且從維護(hù)的角度出發(fā),不管sdl2.0性能提升了多少,如果接口本身不需要改動,不是更加好么?
---------------------------------------------
最后我發(fā)現(xiàn),同樣是渲染的窗口
1.2用SDL_Flip(screen);之后被其他窗口擋住之后,回來畫面還是在的;
2.0用SDL_RenderPresent(renderer);之后被其他窗口擋住之后,回來畫面就不在了;
2015年12月6日
摘要: OpenCV從1.0到現(xiàn)在的3.0,變化還是相當(dāng)大的。大趨勢是從C結(jié)構(gòu)層次到C++類層次的轉(zhuǎn)變。先從OpenCV底層的圖像數(shù)據(jù)結(jié)構(gòu)談起,1.0時(shí) 圖像數(shù)據(jù)結(jié)構(gòu)是IplImage,之后是cvmat,之后2.2中出現(xiàn)了CvvImage,之后就是cv::mat,2.3之后CvvImage就被廢棄了。
opencv中對圖像的處理是最基本的操作,一般的圖像類型為Ipl...
閱讀全文
2015年3月17日
摘要: 問題:Update字段來自子查詢或者來自其他表字段
Update 語句
Update 語句用于修改表中的數(shù)據(jù)。
語法:UPDATE 表名稱 SET 列名稱 = 新值 WHERE 列名稱 = 某值SQL update select語句
最常用的update語法是:
UPDATE <table_name> SET <column_name1> = <value&...
閱讀全文
2014年8月22日
永遠(yuǎn)不要用#include包含不必要的頭文件
如果只需要流的前置聲明,應(yīng)該優(yōu)先使用#include<iosfwd>
只需要前置聲明時(shí),絕不要用#include包含相應(yīng)的頭文件。
如果使用聚合關(guān)系就已經(jīng)足夠,就不要使用繼承。
要避免使用內(nèi)聯(lián)或者復(fù)雜的調(diào)整方法,除非通過性能分析證明這確實(shí)是必要的。
正確使用名字空間。如果將一個類放入名字空間,那么同時(shí)要保證將這個類的所有輔助函數(shù)和運(yùn)算符函數(shù)也放入相同的名字空間。否則,你將在代碼中發(fā)現(xiàn)奇怪的結(jié)果。
存區(qū)域.jpg)
要理解這五種不同類型的內(nèi)存,了解他們?yōu)槭裁词遣煌模约八麄兏髯缘男袨橛质窃趺礃樱簵#ㄗ詣幼兞浚⒆杂纱鎯Γ╪ew/delete)、堆(malloc/free)、全局(靜態(tài)變量、全局變量、文件作用域變量等)、常量數(shù)據(jù)(字符串常量等)。
優(yōu)先使用自由存儲(new/delete),避免使用堆(malloc/free)。
對于“堆”和“自由存儲”進(jìn)行區(qū)分,這一點(diǎn)很重要,因?yàn)樵贑++標(biāo)準(zhǔn)中有意避開了這兩種類型的內(nèi)存是不是相關(guān)的這個問題。例如,當(dāng)通過::operator delete()函數(shù)來釋放內(nèi)存時(shí),在C++標(biāo)準(zhǔn)的18.4.1.1中,最后一項(xiàng)是這樣的:
“ 在C++標(biāo)準(zhǔn)中并沒有規(guī)定,在哪些情況下,在通過operator delete回收的存儲空間中,有一部分或者全部的控件可以再隨后調(diào)用operator new或者calloc,malloc以及realloc等函數(shù)時(shí)被重新分配,這些函數(shù)的聲明時(shí)在<cstdlib>中。”
而且,在C++標(biāo)準(zhǔn)中也沒有規(guī)定,new/delete是否需要通過malloc/free來實(shí)現(xiàn)。不過,在C++標(biāo)準(zhǔn)20.4.6節(jié)的第3段和第4段中規(guī)定了,malloc/free一定不能使用new/delete來實(shí)現(xiàn):“calloc、malloc和realloc函數(shù)不會通過調(diào)用::operator new()來分配存儲空間。函數(shù)free()不會通過調(diào)用::operator delete() 來釋放內(nèi)存。”
如果在類中定義了new和delete中的任意一個運(yùn)算符函數(shù),那么一定要同時(shí)定義另外一個。
通常應(yīng)該顯式地將函數(shù)operator new ()和operator delete()聲明為靜態(tài)函數(shù)。他們永遠(yuǎn)都不能使非靜態(tài)成員函數(shù)。
永遠(yuǎn)都不要通過多態(tài)的方式處理數(shù)組。
優(yōu)先選擇使用vector或者deque,而不是數(shù)組。
在編寫拷貝賦值運(yùn)算符函數(shù)時(shí),永遠(yuǎn)都不要指望能夠通過對自我賦值進(jìn)行檢測來保證函數(shù)的正確性;應(yīng)該在拷貝賦值運(yùn)算符函數(shù)中使用“創(chuàng)建臨時(shí)對象并進(jìn)行交換”的慣用法,這種方法不僅是異常安全的,而且在處理自我賦值時(shí)也是安全的。
可以將自我賦值檢測作為一種優(yōu)化手段,以避免不必要的工作,這是正確地做法。
不僅要避免編寫類型轉(zhuǎn)換運(yùn)算符函數(shù),而且還要避免編寫隱式的構(gòu)造函數(shù)。
盡量編寫異常安全的代碼。在編寫代碼時(shí)應(yīng)該始終遵循:即使在出現(xiàn)異常時(shí),資源仍然能夠被正確地釋放,并且數(shù)據(jù)也總是處于一致的狀態(tài)。
避免使用語言中那些不常用的特性,而應(yīng)該使用最簡單并且有效的技術(shù)。
拷貝初始化過程絕不是賦值過程,因此在初始化中永遠(yuǎn)都不會調(diào)用函數(shù)T::operator=()。是的,我知道在初始化語句中有一個“=”符合,但不要被它迷惑。它只是從C語言中沿用過來的一種語法,并不代表賦值運(yùn)算。
如果可能的話,優(yōu)先使用“T t(u);”這種形式,而不是“T t=u;”的形式。通常,能能夠時(shí)候后者的地方,都可以使用前者,并且使用前者還有更多的好處——例如,可以帶多個參數(shù)。
在函數(shù)聲明中,如果參數(shù)是以傳值方式來傳遞的,則不要使用const。而如果在這個函數(shù)的定義中,參數(shù)是不能被修改的,那么應(yīng)該使用const。
對于不是內(nèi)置類型的返回值來說,當(dāng)使用返回值的方式而不是返回引用的方式時(shí),應(yīng)該優(yōu)先選擇返回const值。
const 和mutable都是你的朋友
優(yōu)先使用新形式的類型轉(zhuǎn)換。
不要通過類型轉(zhuǎn)換去掉常量屬性,而應(yīng)該使用mutable。
避免使用向下的類型轉(zhuǎn)換。
優(yōu)先通過引用方式來傳遞對象參數(shù),而不是傳值方式,并且在所有可能的地方都使用const。
避免使用內(nèi)聯(lián),除非從性能的分析上來看確實(shí)有必要這么做。
避免使用全局變量或者靜態(tài)變量。如果必須使用,那么一定要特別注意這些變量的初始化順序。
在構(gòu)造函數(shù)的初始化列表中,應(yīng)該把 基類按照他們在類定義中出現(xiàn)的先后順序進(jìn)行排列。
在編寫代碼時(shí),永遠(yuǎn)都不應(yīng)該依賴函數(shù)參數(shù)的求值順序
絕對不要對無效的迭代器執(zhí)行解引用(dereference)操作
用于不要將異常安全性放在事后考慮。異常安全性會影響到類的設(shè)計(jì)。它永遠(yuǎn)都不會“只是一個實(shí)現(xiàn)細(xì)節(jié)”。
在傳遞對象參數(shù)時(shí),選擇const&方式而不是傳值方式。
對于程序運(yùn)行中不會改變的值,應(yīng)該預(yù)先計(jì)算并保存起來備用,而不是重復(fù)地創(chuàng)建對象,這是沒有必要的。
通常,為了保持一致性,應(yīng)該使用前置遞增來實(shí)現(xiàn)后置遞增,否則,當(dāng)其他用戶在使用你的類時(shí),可能會得到奇怪結(jié)果。
優(yōu)先選擇使用前置遞增。只有在需要初始值時(shí),才使用后置遞增。
在進(jìn)行隱式類型轉(zhuǎn)換時(shí),要注意在轉(zhuǎn)換過程中創(chuàng)建的 臨時(shí)對象。要避免這個問題,一個好辦法就是盡可能地通過顯式的方式來構(gòu)造對象,并避免編寫類型轉(zhuǎn)換運(yùn)算符。
記住對象的生存期。永遠(yuǎn),永遠(yuǎn),永遠(yuǎn)都不要返回指向局部對象的指針或引用;它們沒有任何用處,因?yàn)橹髡{(diào)代碼無法跟蹤它們的有效性,但卻可能會試圖這么做。
盡可能地重用代碼——尤其是標(biāo)準(zhǔn)庫中的代碼——而不是自己去編寫代碼,這樣更快、更容易,也更安全。
如果在函數(shù)中不打算處理所拋出的異常,那么應(yīng)該將異常轉(zhuǎn)發(fā)給能夠進(jìn)行處理的上層調(diào)用者。
在編寫代碼時(shí)應(yīng)該始終遵循:即使在出現(xiàn)異常時(shí),資源仍然能夠被正確地釋放,并且數(shù)據(jù)也總是處于一致的狀態(tài)。
遵循標(biāo)準(zhǔn)的異常安全規(guī)則:永遠(yuǎn)不要在析構(gòu)函數(shù)、重載運(yùn)算符函數(shù)operator delete()或者operator delete[]()中拋出異常; 在編寫每個析構(gòu)函數(shù)和內(nèi)存釋放函數(shù)時(shí),要假設(shè)存在著“throw()”這樣的異常規(guī)范。
遵循標(biāo)準(zhǔn)的異常安全性規(guī)則:在每個函數(shù)中,要將所有可能會拋出異常的代碼單獨(dú)放在一起,并且對這些代碼進(jìn)行安全處理。然后,當(dāng)你確認(rèn)這些代碼執(zhí)行的工作都已經(jīng)成功地完成時(shí),才可以使用不會拋出異常的操作來修改程序的狀態(tài)。
永遠(yuǎn)都不要到最后才實(shí)現(xiàn)異常安全性。異常安全性會對類的設(shè)計(jì)產(chǎn)生影響。它永遠(yuǎn)都不會“只是一個實(shí)現(xiàn)細(xì)節(jié)”。
優(yōu)先考慮實(shí)現(xiàn)內(nèi)聚。要努力使每段代碼——每個模塊、每個類、每個函數(shù)——都只有單一的,并且是明確定義的功能。
“異常不安全”總是與“拙劣的設(shè)計(jì)”結(jié)伴的。如果程序的設(shè)計(jì)邏輯清晰,那么即使有一段代碼不是異常安全的,一般來說也不會有太大問題,并且可以很簡單地進(jìn)行修正。但如果有一段代碼由于設(shè)計(jì)問題而不能被編寫成異常安全的,我們通常都會認(rèn)為這個設(shè)計(jì)時(shí)拙劣的。下面是兩個拙劣設(shè)計(jì)的示例。
示例1:如果在一個函數(shù)中需要實(shí)現(xiàn)兩個不同的功能,那么這個函數(shù)很難被編寫成異常安全的。
示例2:如果在拷貝賦值運(yùn)算符函數(shù)中必須對自我賦值進(jìn)行檢測,那么這個函數(shù)也可能不是完全異常安全的
遵循標(biāo)準(zhǔn)的異常安全性規(guī)則:以“獲得資源也就意味著初始化”這種模式來分離資源的所有權(quán)和資源的管理權(quán)。
在進(jìn)行設(shè)計(jì)中,要始終牢記重用性。
優(yōu)先采用“ a op=b;”這種寫法,而不是"a = a op b;"(這里的op表示某個運(yùn)算符)。這種寫法更為清晰,效率也高。
如果定義了某個運(yùn)算符(例如,operator+),那么通常還應(yīng)該同時(shí)定義與這個運(yùn)算符相對應(yīng)的賦值運(yùn)算符(例如,operator+=)。并且用后者來實(shí)現(xiàn)前者。而且,還應(yīng)該維護(hù)op和op=之間的自然關(guān)系。
在C++標(biāo)準(zhǔn)中規(guī)定:運(yùn)算符=,(),[]和->必須被定義為成員函數(shù),而在類中定義的new,new [],delete和delete[]等運(yùn)算符函數(shù)必須是靜態(tài)成員函數(shù)。對于其他的運(yùn)算符函數(shù):
如果運(yùn)算符函數(shù)是用于流I/O的opeator>>或者operator<<,或者如果運(yùn)算符函數(shù)需要對其左操作數(shù)進(jìn)行類型轉(zhuǎn)換,或者運(yùn)算符函數(shù)可以通過類的公有接口來實(shí)現(xiàn),那么將這個函數(shù)定義為非成員函數(shù)(在前兩種情況中,如果需要的話也可以被定義為友元函數(shù));如果運(yùn)算符函數(shù)需要實(shí)現(xiàn)虛函數(shù)的行為,那么增加一個虛函數(shù)來提供虛函數(shù)的行為,并用這個虛成員函數(shù)來實(shí)現(xiàn)運(yùn)算符函數(shù)否則將預(yù)算富函數(shù)定義為成員函數(shù)。
在函數(shù)opeator>>和operator<<中應(yīng)該始終返回對流對象的引用。
將基類的析構(gòu)函數(shù)定義為虛函數(shù)(除非你能保證,永遠(yuǎn)都不會有人通過指向基類的指針來刪除派生類的對象)。
如果在派生類中定義的函數(shù)與基類中的函數(shù)有相同的名字,并且你不想隱藏基類中函數(shù),那么應(yīng)通過using聲明語句將基類的這個函數(shù)引入到派生類的作用域中。
永遠(yuǎn)不要改變被覆蓋的基類函數(shù)中的默認(rèn)參數(shù)值。
除了對真正的Liskov IS-A和WORKS-LIKE-A關(guān)系進(jìn)行建模之外,永遠(yuǎn)都不要使用共有繼承。所有被覆蓋的成員函數(shù)不能超過實(shí)際需求的范圍,同時(shí)也不能小于這個范圍。
使用公有繼承的目的是重用代碼(編寫以多態(tài)的方式使用基類對象的代碼),而重用(基類中的)代碼并不一定要使用公有繼承。
對“is implemented in terms of”這種關(guān)系建模時(shí),應(yīng)該優(yōu)先選擇成員關(guān)系/包含的方式,而不是私有繼承的方式。只有非用繼承不可時(shí),才應(yīng)該使用私有繼承——也就是說,當(dāng)需要訪問保護(hù)成員或者需要覆蓋虛函數(shù)時(shí),才使用私有繼承。永遠(yuǎn)都不要只是為了代碼重用而使用共有繼承。
對于廣泛使用的類,應(yīng)該優(yōu)先使用編譯器防火墻這種慣用法(也叫做Pimpl慣用法)來隱藏實(shí)現(xiàn)細(xì)節(jié),通過一個不透明的指針(指向一個進(jìn)行了前置聲明但又沒有定義的類)來保存私有成員(包括狀態(tài)變量和成員函數(shù)),聲明這個指針時(shí)可采用“struct XxxxImpl* pImpl;XxxxImpl* pimpl_;”這樣的形式。例如:“class map{ private :struct MapImpl;MapImpl* pimpl_;}”
包含,也可以叫做“聚合”,“分層”,“HAS-A”或者“委托”。優(yōu)先選擇包含而不是繼承,對于IS-IMPLEMENTED-IN-TERMS-OF這種關(guān)系建模時(shí),應(yīng)該優(yōu)先考慮使用包含,而不是繼承。
2014年7月24日
2014年6月25日
1.確保目標(biāo)空間足夠大
2.了解各種與排序有關(guān)的選擇
如果需要對vector、string、deque或者數(shù)組中的元素執(zhí)行一次完全排序,那么可以使用sort或者stable_sort。
如果有一個vector、string、deque或者數(shù)組,并且只需要對等價(jià)性最前面的n個元素進(jìn)行排序,那么可以使用partial_sort。
如果有一個vector、string、deque或者數(shù)組,并且需要找到第n個位置上的元素,或者,需要找到等價(jià)性最前面的n個元素但又不必對這n個元素進(jìn)行排序,那么,nth_element正是你所需要的函數(shù)。
如果需要將一個標(biāo)準(zhǔn)序列容器中的元素按照是否滿足某個特定的條件區(qū)分開來,那么,partition和stable_partition可能正是你所需要的。
如果你的數(shù)據(jù)在一個list中,那么你仍然可以直接調(diào)用partition和stable_partition算法;可以用list::sort來替代sort和stable_sort算法。但是,如果你需要獲得partial_sort或nth_element算法的效果,那么,正如前面我所提到的那樣,你可以有一些簡潔的途徑來完成這項(xiàng)任務(wù)。
3。如果確實(shí)需要刪除元素,則需要在remove這一類算法之后調(diào)用erase。
remove不是真正意義上的刪除,因?yàn)樗霾坏健?br />
4.對包含指針的容器使用remove這一類算法時(shí)要特別小心。會導(dǎo)致資源泄露。
5.了解哪些算法要求使用排序的區(qū)間作為參數(shù)。
6.通過mismatch或lexicographical_compare實(shí)現(xiàn)簡單地忽略大小寫的字符串比較
7.理解copy_if算法的正確實(shí)現(xiàn)
8.使用accumlate或者for_each進(jìn)行區(qū)間統(tǒng)計(jì)。
2014年6月13日
1.iterator 優(yōu)先于const_iterator、reverse_iterator及const_reverse_iterator
2.使用distance和advace將容器的const_iterator轉(zhuǎn)換成iterator
3.正確理解由reverse_iterator的base()成員函數(shù)所產(chǎn)生的iterator的用法。
4.對于逐個字符的輸入請考慮使用istreambuf_iterator
2014年5月16日
1.理解相等(equality)和等價(jià)(equivalence)的區(qū)別
相等的概念是基于operator==的。等價(jià)關(guān)系是以“在已排序的區(qū)間中對象值得相對順序”為基礎(chǔ)的。如果從每個標(biāo)準(zhǔn)關(guān)聯(lián)容器的排列順序來考慮等價(jià)關(guān)系,那么著將是有意義的。標(biāo)準(zhǔn)關(guān)聯(lián)容器室基于等價(jià)而不是相等的。標(biāo)準(zhǔn)關(guān)聯(lián)容器總是保持排列順序的,所以每個容器必須有一個比較函數(shù)(默認(rèn)less)來決定保持怎樣的順序。等價(jià)是按照比較函數(shù)子。因此,標(biāo)準(zhǔn)關(guān)聯(lián)容器的使用者要為所使用的每個容器指定一個比較函數(shù)(用來決定如何排序)。如果該關(guān)聯(lián)容器使用相等來決定兩個對象是否有相同的值,那么每個關(guān)聯(lián)容器除了用于排序的比較函數(shù)外,還需要另一個比較函數(shù)來決定兩個值是否相等(默認(rèn)情況下,該比較函數(shù)應(yīng)該是equal_to,但有趣的是equal_to從來沒有被用做STL的默認(rèn)比較函數(shù)。當(dāng)STL中需要相等判斷時(shí),一般的慣例是直接調(diào)用operator==。比如,非成員函數(shù)find算法就是這么做的)
2.為包含指針的關(guān)聯(lián)容器指定比較類型
why?第一條已經(jīng)說明關(guān)聯(lián)容器是要排序。每當(dāng)你要創(chuàng)建包含指針的關(guān)聯(lián)容器時(shí),一定要記住,容器將會按照指針的值進(jìn)行排序。一般是不是你希望的,所以你幾乎要創(chuàng)建自己的函數(shù)子類作為該容器的比較類型。
3.總是讓比較函數(shù)在等值情況下返回false
比較函數(shù)的返回值表明的是按照該函數(shù)定義的排列順序,一個值是否在另一個之前。相等的值從來不會有前后順序關(guān)系,所以,對于相等的值,比較函數(shù)應(yīng)該始終返回false。
4.切勿直接修改set或multiset中的鍵。
5。考慮用排序的vector替代關(guān)聯(lián)容器
在排序的vector中存儲數(shù)據(jù)可能比在標(biāo)準(zhǔn)關(guān)聯(lián)容器中存儲同樣的數(shù)據(jù)要耗費(fèi)更少的內(nèi)存,而考慮到頁面錯誤的因素,通過二分搜索法來查找一個排序的vector可能比查找一個標(biāo)準(zhǔn)關(guān)聯(lián)容器要更快一些。
6.當(dāng)效率至關(guān)重要時(shí),請?jiān)趍ap::operator[]與map::insert之間謹(jǐn)慎做出選擇
map::operator[]的設(shè)計(jì)目的是為了提供“添加和更新”的功能。添加一個新元素最好選后者insert。
7.熟悉非標(biāo)準(zhǔn)的散列容器。