前言:距離上一次摸QT已經快10年了,時光匆匆,現在已經到6.9版本了
一、安裝QT
1.1、下載鏈接
https://mirrors.tuna.tsinghua.edu.cn/qt/official_releases/online_installers/
這是國內鏡像,比官網快很多了,官網那個可以用龜速來形如。
目前已采用在線下載方式,類似visual studio 2022,依稀記得,最開始接觸時,需要下載源代碼,然后自己適配,編譯。
二、計算器
先寫個計算器先熟悉下
-
功能包含:四則運算,含小數點,可進行浮點數計算,包含括號
-
界面設計:使用QT的UI工具進行布局
大致如圖所示,由于控件較多,也比較密集,所以采用網格布局(grid layout),然后為每個控件設置信號與對應的槽<=============>Windows下的win32或者MFC的事件與相關回調函數
當然除了在ui界面一個一個設置相關的槽,還可以直接通過代碼來進行設置
//連接信號與槽,對C按鈕使用
connect(ui->clear_Button, &QPushButton::clicked, this, [this](){express.clear();ui->display_LineEdit->setText(express);
});//連接信號與槽,對Del按鈕使用
connect(ui->del_Button, &QPushButton::clicked, this, [this](){if(!express.isEmpty()){express.chop(1);ui->display_LineEdit->setText(express);}
});
- 核心功能:實現計算器,采用逆波蘭式
-
- 先了解中綴表達式:形如
A+B
運算符在兩個操作數中間,所以就是中綴表達式,日常生活中使用的就是這種
- 先了解中綴表達式:形如
-
- 后綴表達式:運算符在操作數后面就是,如
A B +
- 后綴表達式:運算符在操作數后面就是,如
-
- 逆波蘭式的核心:就是將中綴表達式轉變為后綴表達式,然后對后綴表達式進行計算
-
- 一般要使用兩個棧,一個棧存放操作數,另外一個棧存放運算符。
-
- 計算終止條件,運算符棧為空,就彈出操作數棧的棧頂元素
- 計算終止條件,運算符棧為空,就彈出操作數棧的棧頂元素
-
//核心代碼如下
// 等號的槽函數
void Widget::on_equval_Button_clicked()
{if(express.isEmpty()){express += '0';ui->display_LineEdit->setText(express);return;}//逆波蘭式進行計算try{//替換掉表達式中的XQString str = express;str.replace('X', '*');//先將中綴表達式轉為后綴表達式QString post_str = infixToPostFix(str);//計算后綴表達式的值double result = calPostFix(post_str);QString temp = QString::number(result, 'g', 10); //將其轉為10位有效數字ui->display_LineEdit->setText(temp);}catch(...){express = "Error";ui->display_LineEdit->setText(express);}express.clear();
}
// 中綴轉后綴
QString Widget::infixToPostFix(const QString &str)
{QStack<QChar> ops;QString ans = "";auto priority = [](const QChar c)->int{ //需要進行比較運算符的優先級,優先計算優先級高的if(c == '+' || c== '-') return 1;if(c == '*' || c== '/') return 2;return 0;};int len = str.length();for(int i = 0; i < len; ++i){QChar c= str[i];//處理數字和小數點if(c == '.' || c.isDigit()){while(i < len && (str[i] == '.' || str[i].isDigit())){ans += str[i];++i;}i--; //再回退一格ans += ' '; //用空格分隔數字}else if(c == '('){ops.push(c);}else if(c == ')'){while(!ops.isEmpty() && ops.top() != '('){ans += ops.top();ops.pop();ans += ' ';}if(!ops.isEmpty() && ops.top() == '('){ops.pop();}}else{ //處理四則運算符while(!ops.isEmpty() && (priority(ops.top()) > priority(c))){ans += ops.top();ops.pop();ans += ' ';}ops.push(c);}}//將剩下的運算符全部彈出while(!ops.isEmpty()){ans += ops.top();ops.pop();ans += ' ';}return ans.trimmed();
}
// 計算后綴表達式的值
double Widget::calPostFix(const QString &str)
{QStack<double> nums;QString num_str;int len = str.length();auto isOperator = [](const QChar& c)->bool{ if(c == '+' || c == '-' || c == '*' || c == '/') return true;return false;};for(int i = 0; i < len; ++i){QChar c = str[i];//先處理數字和小數點if(c == '.' || c.isDigit()){num_str.clear();while(i < len && (str[i].isDigit() || str[i] == '.')){num_str += str[i];++i;}i--;bool flag = false;double num = num_str.toDouble(&flag);if(flag){nums.push(num);}}else if(isOperator(c)){//操作如果<2不允許計算if(nums.size() < 2){return 0;}double num_B = nums.top();nums.pop();double num_A = nums.top();nums.pop();double result = 0.0f;switch(c.unicode()){case '+': result = num_A + num_B; break;case '-': result = num_A - num_B; break;case '*': result = num_A * num_B; break;case '/': {if(num_B == 0){return 0;}result = num_A / num_B;}break;}nums.push(result);}}if(nums.isEmpty()){return 0;}return nums.top();
}
之前逆波蘭式說是兩個棧,但這邊優化了下,在中綴轉后綴以及計算后綴時,都分別只使用了一個棧和一個字符串,用字符串模擬棧操作,減少點代碼
三、小結:
3.1. 從代碼中可以看到,對于堆上申請的內存,比如ui
,我沒有進行手動釋放內存,難道不怕造成內存泄漏嗎?
QT擁有自動刪除機制:- 當QT父對象被刪除時,它會自動刪除所有子對象- 只有當對象沒有父對象時,才需要手動delete, 不過大部分對象都會被QT接管,所以不必過分關注,使用`QObject::deleteLater`來進行手動刪除<br>
3.2. 沒使用C++原生的STL容器,string, stack
,就連基礎類型char
都沒使用,使用的是QT這邊二次封裝的QString,QStack,QChar
,這對于不經常寫QT的人來說,確實一下子有點懵,常用的STL算法在QT這邊要變形,比如原生的判斷字符是否是數字,isdigit(c)
,在QT這邊沒有,而是c.isdigit()
, switch
表達式不能直接使用QChar
,要這樣使用switch(c.unicode())
。
Code