前言
雖然做了幾年的C/C++開發,但是總體上感覺自己的基礎方面還是有些薄弱,很多細節問題沒有了解清楚,希望寫blog來加深自己的理解。
最近看這本書,覺得受益匪淺,所以決定寫一些文章,來介紹一下自己對這些條目的理解。
條款一:盡量使用const和inline,而不要使用define
宏常量
宏常量有兩個問題 ,一個是沒有類型,另一個是編譯報錯的時候比較難找,因為它以及把宏換成常量了。
所以使用const常量,可以避免上面的問題,如果是c語言寫的,你還是乖乖的用宏定義吧。
宏函數
因為以前本人主要做c語言開發,自然面試的時候都會問一下關於宏定義的試題。
一道考爛的題就是,用宏定義找出兩個數中最大的。
答案是
#define MAX(a,b) ((a)>(b)?(a):(b))
就是說所有變量,表達式以及 宏函數自身都要加括號,為什麼呢?
簡單說明一下
如果寫成
#define MAX(a,b) a>b?a:b
那麼我現在計算 MAX(2,3)*MAX(1,2) 就會被翻譯成
2>3?2:3*1>2?1:2
這個時候你就要查優先級表,?:的優先級小於*號,所以上面的實際結果是
2>3?2:(3>2?1:2)
2>3?2:1
最後的結果是1
而不是我們想要的6.
而且即使這樣做了,出現 ++ ,--等符號還是會有問題。 大家可以看書中講述的例子。
這個就是宏定義最大的問題。
所以使用內聯函數代替的話就沒有這個麻煩了,可以寫成
[cpp]
inline MAX(int a, int b)
{
return a:b?a>b
}
inline MAX(int a, int b)
{
return a:b?a>b
}
但是宏定義本身的特點是適應於多種情況,這個時候我們要使用模板來解決這個問題
[cpp]
#define MAX(a,b) ((a)>(b)? (a):(b))
template <typename T>
inline const T& max(T &a, T &b)
{
return a > b ? a :b;
}
#define MAX(a,b) ((a)>(b)? (a):(b))
template <typename T>
inline const T& max(T &a, T &b)
{
return a > b ? a :b;
}
這樣寫的好處,效率上和宏一樣高,而且比宏好維護,我們不用再去記憶宏定義的時候要所有的地方都加括號,也不用在調用宏函數的時候戰戰兢兢的,不用使用++ , --等符號,有的時候還要自己把它展開看一下。
下面有個完整的程序,分析這個問題
[cpp]
#include <iostream>
using namespace std;
#define MAX(a,b) ((a)>(b)? (a):(b))
template <typename T>
inline const T& max(T &a, T &b)
{
return a > b ? a :b;
}
int main()
{
int a = 4;
int b = 7;
cout<<MAX(++a,++b)<<endl;
a = 4;
b = 7;
cout<<max(++a, ++b)<<endl;
}
#include <iostream>
using namespace std;
#define MAX(a,b) ((a)>(b)? (a):(b))
template <typename T>
inline const T& max(T &a, T &b)
{
return a > b ? a :b;
}
int main()
{
int a = 4;
int b = 7;
cout<<MAX(++a,++b)<<endl;
a = 4;
b = 7;
cout<<max(++a, ++b)<<endl;
}
你可以看到,輸出的結果分別是9和8。
條款2:盡量用<iostream>而不用<stdio.h>
這個條款的說法,每個人有不同的看法。
書中的說法是,你可以不用記憶那些類型,%d表示十進制書中,%s, %c,%f等等輸出類型,
你也不用糾結scanf裡面要取地址。
對於類的輸出,你可以重寫<<或者>>來定義他們的輸入和輸出,但是用scanf和printf來打印可能很麻煩了。
但是也有人反對用<<和>>來進行輸入和輸出,他們的原因很簡單代碼的易讀性。
你讀scanf和 printf裡面可以看到他們會格式化輸出和輸出的東西,
例如你要求輸入格式是 input1,input2中間必須用逗號間隔,那麼iostream可能寫起來就比較麻煩了。
另外printf 你可以很明顯的讀出來這句log是什麼意思,但是用流來表示就不利於閱讀了,這個看應用場景。
[cpp]
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
int a,b;
float c,d;
cin>>a>>c;
cout<<a<<" "<<c<<endl;
printf("請按1,2這種格式輸入\n");
scanf("%d ,%f", &b, &d);
printf("%d, %f", b, d);
}
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
int a,b;
float c,d;
cin>>a>>c;
cout<<a<<" "<<c<<endl;
printf("請按1,2這種格式輸入\n");
scanf("%d ,%f", &b, &d);
printf("%d, %f", b, d);
}
條款3:盡量用new和delete而不用malloc和free
我們先看一個例子
[cpp]
int main()
{
int *p;
int *q;
p = new int[10];
q = (int*)malloc(sizeof(int)*10);
}
int main()
{
int *p;
int *q;
p = new int[10];
q = (int*)malloc(sizeof(int)*10);
}
編譯一下,你會發現編譯不通過,為什麼呢?因為new是操作符,就是類似於=, +,* 之類的符號,編譯器認識他們。
但是malloc其實是個函數,編譯器不認識他們,所以你要包含頭文件stdlib.h。
還有什麼區別呢?就是new 和delete的出現是為了類,他們會調用類的構造函數和析構函數。 但是malloc和free申請的時候並不會調用構造和析構函數,所以new和delete在c++中最好代替malloc和free.
另外一個注意的是,不要混用這四個。一定要對應。
為了簡單,用new和delete 可以實現所有的功能,所以c++中推薦用這個,如果是c語言,就乖乖的用malloc 和free吧。
條款4:盡量使用c++風格的注釋
c++的注釋是// 而c的注釋是/* */
這裡我個人覺得函數頭的注釋最好還是用/* */的段注釋。
如果是單行的注釋可以考慮用//
條款5:對應的new和delete要采用相同的形式
還有個問題,就是還有new [] 和delete[]他們是對應的,用new[]申請的要用delete[]釋放。
如果用new申請,而用delete[]釋放,就會導致釋放的多了,會引起不可以預知的問題。
同樣如果new[]申請,而用delete釋放,如果是基本類型,沒有問題delete和delete[]一樣,但是復雜類型就不一樣了。看下面的例子。
[cpp]
#include <stdlib.h>
#include <iostream>
using namespace std;
class A
{
public:
int a;
};
void testInt();
void testClass();
int main()
{
testInt();
testClass();
}
void testInt()
{
int *p;
int *q;
p = new int[10];
q = (int*)malloc(sizeof(int)*10);
p[1] = 10;
q[1] = 10;
delete []p;
free(q);
cout<<p[1]<<endl;
cout<<q[1]<<endl;
}
void testClass()
{
A *p = new A[10];
p[1].a = 10;
delete p;
cout<<p[1].a<<endl;
}
#include <stdlib.h>
#include <iostream>
using namespace std;
class A
{
public:
int a;
};
void testInt();
void testClass();
int main()
{
testInt();
testClass();
}
void testInt()
{
int *p;
int *q;
p = new int[10];
q = (int*)malloc(sizeof(int)*10);
p[1] = 10;
q[1] = 10;
delete []p;
free(q);
cout<<p[1]<<endl;
cout<<q[1]<<endl;
}
void testClass()
{
A *p = new A[10];
p[1].a = 10;
delete p;
cout<<p[1].a<<endl;
}
輸出的結果是
1629738516
0
10
可以看到類裡面沒有釋放完。
因為malloc和free都是指定大小的,你分配的時候的大小是知道的,free只用把對應的空間釋放掉就可以了。
而new的時候,要分兩種情況,new 一個對象,還是new[]分配對象數組。
所以 delete要明確的告訴是一個對象,還是對象數組。