文章目錄
- 實現hello world的兩種方式
- 通過圖形化的方式
- 通過純代碼的方式
- 1. 新老頭文件的說明
- 2.堆或棧上創建對象的選擇
- 3.QString的說明
- 內存泄漏問題
實現hello world的兩種方式
通過圖形化的方式
通過圖形化的方式,在界面上創建出一個控件,顯示出hello world,
雙擊widget.ui進入到Qt Designer
。label(標簽)
是界面上一個用來顯示內容的字符串控件
下面圖片在Qt Designer右上角處,通過樹形結構顯示出當前界面上有哪些控件
剛才往界面上拖拽了一個QLabel控件,可以通過下面圖片看到,此時的widget.ui文件的xml中就自動的生成了一段代碼,
隨后qmake就會在編譯項目的時候基于這個內容去生成一段C++代碼,通過這個C++代碼就會去構建出界面內容,這些都是Qt工具自動生成的,無需手動操作
最終程序運行結果,以及程序運行成功后
ui_widget.h文件內容的變動
通過純代碼的方式
通過純代碼的方式,通過編寫代碼,在界面上創建控件,顯示hello world。這里要特別注意:
;一般通過代碼去構建界面的時候,通常會把構建界面的代碼放到 Widget/MainWindow的構造函數中
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 構建界面的代碼QLabel* label = new QLabel(this);label->setText("hello world");
}
1. 新老頭文件的說明
若出現
unkonwn type name ' QLabel'
,這就沒有意味著沒有包含頭文件,因為Qt中每個類都有一個對應的頭文件,而當我們去widget.h中輸入頭文件時會出現以下兩個版本QLabel
和qlabel.h
,其中qlabel.h
是Qt早期時候用的頭文件,自1998年之后C++標準委員會成立了C++98標準,規定包含的頭文件統一使用#include<cstdio>
代替原有的#include<stdio.h>
,因此Qt的頭文件也隨之更新了
2.堆或棧上創建對象的選擇
這里得在堆上創建對象,我在棧上創建對象,發現程序上顯示不出 hello world ,這里我認為是棧和堆上對象的生命周期是不一樣的,且堆上對象的生命周期通常比棧上對象長的緣故(此時的label對象隨著構造函數的結束就銷毀了)
,這里的QLabel(this)
意思是給當前這個label對象指定一個父對象(widget w
),這里牽扯到對象樹,后面陳述
QLabel* label = new QLabel(this); // 這種是在堆上創建對象
QLabel label(this); // 這種是在棧上創建對象
3.QString的說明
label->setText(),意思是設置的控件中,要顯示的文本是啥
這里出現了一個QString
,咱記得C++中用的不是string
嗎?這個QString
又是咋回事呢?這里又牽扯到了Qt的歷史。
- Qt誕生于1991年,那個時候C++還沒有形成標準,那就更不用說C++有如今"標準庫"這樣的改變了,當時如何表示一個字符串有兩種方式,可以使用C風格字符串(
\0
結尾),也可以使用C++的string。 - 可是當年C++的string,C風格字符串 啥的都不太好用,那Qt 為了讓自己的開發能變的順暢,就自己發明了一套輪子,也就是搞了一系列的基礎類來支持Qt的開發(包括不限于:字符串 QString,動態數組 QVector,鏈表 QList,字典 QMap等等)。
- 那很多年之后,string,vector上述這些容器等內容,已經打磨的很好了,形成了C++標準,那這些已經引入的 Qt 自己包裝好的這些容器類也不可能刪了,也就只能和現有的標準庫中的容器類共存了。
- 因此,咱們開發 Qt代碼的時候,如果需要用到上述容器類可以使用標準庫的容器,也可以使用Qt自己搞的這一套,但是Qt原生的 api 中,涉及到的接口用的都是Qt 自己的這一套容器,后續的代碼中還會經常見到QString這樣的一些東西,但很少見到
std::string
- 不過QString和std::string 之間也能很方便的相互轉換。而且
QString
用起來要比std::string
好用很多,因為QString 內部已經對于字符編碼做了處理了,而不像std::string就啥都沒干
label->setText("hello world");
在QString中也提供了C風格字符串作為參數的構造函數,不顯示構造Qstring,
C風格字符串也會隱式構造成QString對象
。這里不需要去包含QString對應的頭文件,這是因為該頭文件已經被很多Qt內置的其他類給間接包含了,所以無需再去顯示包含QString頭文件
,這里通過代碼創建Qlabel,其字符串默認是放在左上角,想放到其它位置也是可以的(只不過我還沒學到哈)
內存泄漏問題
仔細一看上述的代碼會發現沒有delete label
,這將會造成內存泄漏,對于我們程序員來說,關注內存泄漏是要融入到DNA中的事情,這非常的重要!!!因為內存泄漏是個非常嚴重的事情(不僅僅是內存泄漏,包括文件描述符泄漏等同類問題都是非常嚴重的)
接下來看看我老師的自述(我認為是非常寶貴的經驗)
因為內存泄漏這種問題,不容易第一時間發現
。我在搜狗的時候~~我要上線一個程序(把程序部署到生產環境上 此時這個程序就可以被外面的用戶訪問到,那如果生產環境掛了,用戶就訪問不了了,非常嚴重的事故!!
)而對面的兄弟,想讓我給他帶一個版本到生產環境上去,這是因為上線是一個挺麻煩的事情(讓測試進行測試,測試通過,預約運維同學排期,還需要提電子流 讓領導層層審批)這種幫別人帶代碼上線,在當時是非常常見的,雖然操作是違規操作,但是還是會給人家帶。
具體的上線操作也是非常繁瑣的、當時我們有幾十臺服務器,就是要把程序部署到這幾十臺服務器上一般來說都是"灰度上線",先上線一臺機器,觀察一下,驗證一下,看看這個機器更新版本之后,有沒有問題,如果沒問題,再上線后續的機器,如果有問題,那就趕緊調查問題,后續的機器上線就暫停,只上線一臺機器,哪怕出現嚴重bug,影響面積不大,造成的后果/損失比較有限。
那天上午,我上線了一臺機器,中午吃飯了開始午休~~到了下午的時候,已經觀察幾個小時了,我檢查了一下這個上線的機器,發現沒啥問題,各個功能,各個指標都正常(這里監控程序會去監測
)給運維同學說可以上線后續機器了,這時候第一臺機器狂報警!這意味著出事了,我就趕緊去查看機器,發現這個機器上的程序出現了"文件資源泄露"問題(可能是打開了文件,沒有close)(每個進程=> pcb=>文件描述符表每次打開一個文件,都需要在文件描述符表中 申請一個表項,那文件描述符表長度是有上限的,而要花多長時間才能達到上限?不知道就看你的代碼泄露速度快不快了)我就找到這個兄弟一起排查,他發現自己的代碼中打開了文件沒關閉(這里可能關閉了文件,但因為啥原因跳過了關閉文件)那如果泄露速度再慢點,一直到凌晨,夜深人靜我們都在睡覺的時候突然搞出這么一手,那將面臨著所有的機器都可能會癱瘓,整個業務線就直接沒了。
先聲明,上述代碼在Qt中并不會產生內存泄漏的問題
,label對象會在合適的時候被析構給釋放掉(雖然并沒有手動寫析構,但確實能釋放掉),那之所以能釋放掉,主要是把這個對象掛在對象樹上。之前通過new的方式在堆上創建對象并且指定父節點,就是為了把這個對象的生命周期交給Qt對象樹來統一管理,這樣對象樹生命周期就是該對象的生命周期,那如果這個對象是按照棧上的變量創建的,那棧銷毀了,該對象不僅沒了嗎?這就可能存在一些提前釋放
的問題
前端開發(網頁開發)也涉及到類似的對象樹(DOM),本質上也是一個樹形結構(N叉樹),這樣通過樹形結構就能把界面上的各種元素都組織起來,而Qt也是類似的,搞了一個N叉樹,
最主要的目的就是為了能構造在合適的時機(窗口關閉/銷毀)把這些對象統一進行釋放,而且通過這個樹形結構把界面上要顯示的這些控件對象都組織起來了
,那如果某個對象提前銷毀了,此時就會導致對應的控件就在界面上不存在了,這也是為啥在棧上創建對象,運行起來的程序無法顯示出hello world