C++ 我想這樣用(五)
什麼是面向過程我想如果你還不知道,那你絕對不是C程序員!其實我個人感覺面向過程、模塊式的C編程風格是最棒的范式,因為平時我們遇到的大多數問題,都可以用這種范式解決,而且設計相當的簡潔明快,絕不拖泥帶水,相反不停的折騰面向對象模型真的會讓你離問題本質越來越遠。不管你已經對面向過程范式多麼熟悉了,C++環境下究竟還是有些不同的:
1.關於引用:
引用是現代編程語言中常有的一個概念,自然也是C++引入的新語言特性,是C++常用的一個重要內容。然而我不能不吐槽一句,難道就不能用個其他的操作符嗎?於習慣使用C進行開發的朋友們,在看到c++中出現的&符號,可能會犯迷糊,因為在C語言中這個符號表示了取地址符,但是在C++中它卻用來表示引用,於是乎產生了一大批混亂的寫法:
#include <stdio.h>
int main(void)
{
int a = 0; // &a = 0x0012ff60
int *p = &*(int*)0x0012ff60;
printf("The value is: %d\n", *p);
return 0;
}
我真的不想貼出來,每每看到都想吐、、 總之這是一個很奇葩的現象,在一門已經有了指針這個究極武器的語言中再引入引用,真的是很混亂。我的建議是:
a)如果你是C++程序員,用好你的引用吧,別來玩弄指針了;
b)相反你是C程序員,那請自愛,遠離引用!!至少C with Class裡是禁止使用的。
2.new和delete的使用注意
差不多到目前為止,new和delete操作符是我接受的唯一的C++裡面的東西,既然為了面向對象不得不接受他們,那麼就一定要用好他們。
new用法:
1. 開辟單變量地址空間
1)int *a = new int; //開辟一個存放數組的存儲空間,返回一個指向該存儲空間的地址.int *a = new int 即為將一個int類型的地址賦值給整型指針a.
2)int *a = new int(5); //作用同上,但是同時將整數賦值為5
2. 開辟數組空間
int *a = new int[100]; //開辟一個大小為100的整型數組空間
delete用法:
1. delete a; //釋放單個int的空間
2. delete [] a; //釋放int數組空間
一定要注意delete 和delete[] 的區別,C++告訴我們在回收用 new 分配的單個對象的內存空間的時候用 delete,回收用 new[] 分配的一組對象的內存空間的時候用 delete[]。 關於 new[] 和 delete[],其中又分為兩種情況:(1) 為基本數據類型分配和回收空間;(2) 為自定義類型分配和回收空間。
請看下面的程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<span style="color: #993300;">#include <iostream>;
using namespace std;
class T {
public:
T() { cout << "constructor" << endl; }
~T() { cout << "destructor" << endl; }
};
int main()
{
const int NUM = 3;
T* p1 = new T[NUM];
cout << hex << p1 << endl;
// delete[] p1;
delete p1;
T* p2 = new T[NUM];
cout << p2 << endl;
delete[] p2;
}
</span>
大家可以自己運行這個程序,看一看 delete p1 和 delete[] p1 的不同結果,我就不在這裡貼運行結果了。從運行結果中我們可以看出,delete p1 在回收空間的過程中,只有 p1[0] 這個對象調用了析構函數,其它對象如 p1[1]、p1[2] 等都沒有調用自身的析構函數,這就是問題的症結所在。如果用 delete[],則在回收空間之前所有對象都會首先調用自己的析構函數。基本類型的對象沒有析構函數,所以回收基本類型組成的數組空間用 delete 和 delete[] 都是應該可以的;但是對於類對象數組,只能用 delete[]。對於 new 的單個對象,只能用 delete 不能用 delete[] 回收空間。
所以一個簡單的使用原則就是:new 和 delete、new[] 和 delete[] 對應使用。
3.函數參數默認值
沒錯,很多C程序員早就知道C++可以給函數參數分配默認值,如果調用時不指定,就用默認值。我覺得這個特性真的很不錯,可以為設計API提供極大方便,然而C裡面是沒有的,為了我的強迫症,我還是放棄了這個特性,但放棄不代表不用了,其實用C我們也可以模擬實現這一特性,就是用最犀利的Macro!!
具體的做法如下:
復制代碼
1 #include<stdio.h>
2
3
4
5 enum{
6
7 plain=0,italic=1,bold=2
8
9 };
10
11
12
13 void printString(const char* message, int size, int style) {
14
15 printf("%s %d %d\n",message,size,style);
16
17 }
18
19
20
21 #define PRINT_STRING_1_ARGS(message) printString(message, 18, italic)
22
23 #define PRINT_STRING_2_ARGS(message, size) printString(message, size, italic)
24
25 #define PRINT_STRING_3_ARGS(message, size, style) printString(message, size, style)
26
27
28
29 #define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
30
31 #define PRINT_STRING_MACRO_CHOOSER(...) \
32
33 GET_4TH_ARG(__VA_ARGS__, PRINT_STRING_3_ARGS, \
34
35 PRINT_STRING_2_ARGS, PRINT_STRING_1_ARGS, )
36
37
38
39 #define PRINT_STRING(...) PRINT_STRING_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
40
41
42
43 int main(int argc, char * const argv[]) {
44
45 PRINT_STRING("Hello, World!");
46
47 PRINT_STRING("Hello, World!", 12);
48
49 PRINT_STRING("Hello, World!", 12, bold);
50
51 return 0;
52
53 }
復制代碼
好吧,確實很復雜,其實可以適當簡化的,不過如果你真的是想設計庫,上面的完整寫法真的是超贊的哦,而且可移植性很高呢,當然這不是我想出來的,原鏈接在這裡:
http://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros
4.inline函數
以前的C是沒有inline這個東西的,對於一些要頻繁使用的小函數,我們常常把他們寫成宏函數,這樣可以降低函數展開的開銷。但是C++是有的,於是很多的編譯器也為C加上了這個拓展,比如gcc,可是C99出現了,給C加上了這一特性。原本一切都好了,然而杯具的是C99的inline和許多編譯器以前加的那個用法是不完全相同的,如果你習慣了gcc風格的inline,請一定要注意啊,我在網絡上找到了這個,看看吧:
http://blog.163.com/zhaojie_ding/blog/static/172972895200811545032240
5.重載函數
現實生活中的一個詞可能有多種含義,比如,洗衣服、洗頭、洗車,都有一個洗字,但是他們的操作方式是不一樣的。函數也一樣,有時候它們的操作不完全一樣但是名字一樣,這就是重載函數。
重載函數就是,兩個以上的函數取相同的函數名,但是函數形參的個數或者類型不同,編譯器會根據實參與形參的類型和個數進行最佳匹配,自動確定調用哪一個函數。為什麼要有重載函數呢?因為如果沒有重載函數,那麼對不同類型的數據進行類似的操作也要定義不同名稱的函數,比如加法函數,就必須對整數加法和浮點數加法分別定義不同的函數名:
int nadd(int a, int b);
float fadd(float a, float b);
這樣調用需要記住的函數名太多,而且功能類似,很不方便。這是C++的一個語法特性,不評論好與壞,我在C with Class是不用的。
6.模版函數
有時候我們使用重載函數還不能達到最優的效果,比如,兩個求絕對值的函數:
int abs(int x)
{
return x<0 ? -x:x;
}
double abs(double x)
{
return x<0 ? -x:x;
}
大家觀察下這兩個函數,這兩個函數只是返回值類型和參數類型不同,功能完全一樣,如果能有辦法寫一段通用的代碼適用於多種不同的數據類型,就是不用像上面那樣寫兩個函數而只是一段代碼就能實現兩個函數的功能,那代碼的復用性不是更高了嗎?開發效率也會提高的。這就要函數模板來實現了。函數模板的定義形式是:
template <typename 標識符>
函數定義
沒錯,模版函數已經設計到模版了,而且最重要的是,這裡面已經不是OOP的思想了,而是更高層次的一種范式,即:泛型編程。顯然這離C with Class太遙遠。