Java程序員學習一天半C++的感想
大學期間,學了一學期的C語言,當然包括學習數據結構時,用的也是C語言。當時剛剛接觸計算機,對于編程更是一無所知。上課學習學習,偶爾會照著 書上敲一下代碼。大二下學期,就丟掉了不用了。最近由于工作的需要,要使用Java Native Interface,所以就學習了1天半的C++,對C++有了一點點的了解,寫一下自己的理解。
一天半時間,也學不多少東西,我主要就搞明白了下面幾個問題:
1)指針
這么多年了,還記得在C語言時,最難以理解的,應該屬于指針了。還記得譚浩強的那本C語言書(書名是啥,真的忘了。不過作者譚浩強老師,絕大多 數的中國開發(fā)人員應該都知道的),前面大部分用的都是基本數據類型(也就是Java中的原生類型),后面一小部分突然講起了指針,當時立馬就 蒙 圈了。不過好在最后還是理解了指針,雖然后來又忘記了。
指針是什么?
指針是一個存放另外一個東西的地址的變量。指針是一個變量,把一個具有特殊作用的變量稱為指針。它的特殊作用就是:存放另外一個東西的內存地址。也 就是說指針變量的值代表了一個地址,這個地址是另外一個東西的。那另外一個東西是什么呢?就是我們說的對象(或者實例)。在C++中,還為這個對象起了一 個別名:引用。
總結一下就是:指針變量指向一個對象(或者引用)。
*、&的使用
在聲明語句中:
*表示聲明的是指針,&表示引用。
這里說的聲明語句,可以是變量聲明,也可以是函數聲明中。在函數聲明中,返回值、函數名、參數都可以聲明為指針。
在使用指針變量時,
(* 變量名)代表取對象。(& 引用)代表取指針。
函數指針、指針函數
- void personTest(Person * p){
- if(p!=NULL){
- p->setAddress("Bei Jing, Hai Dian"); // 采用指針的方式賦值
- (*p).setName("Fang JiNuo"); // 采用對象的方式賦值。
- (*p).setAge(23);
- printf("show info:\n%s\n", (*p).toString());
- delete p;
- p=NULL;
- }
- }
這個兩個詞八個字,不知道有多少人載了跟頭,其實很好理解了。中國人說話,多以敘述的方式為主。這個兩個詞都是省略句,不過省略的是助詞。
函數指針全名是:函數名是指針。
指針函數全名是:返回值是指針的函數。
這兩個中,指針函數很容易理解了:
char * func(char[] p);這個函數就是一個指針函數。
函數指針,函數名是指針。指針也是變量,所以就可以理解為:函數名是變量。
下面是一個函數指針變量的聲明:
typedef int (* func) (int x);
然后把這個變量作為另外一個函數的參數來使用:
- typedef int (*func)(int arg); // 定義一個函數指針
- /* 一個函數指針的實現
- * funcImpl就可以作為func的值進行賦值。
- */
- int funcImpl(int arg){
- return arg;
- }
- /*
- * 聲明一個函數,將函數指針作為函數call的參數
- */
- void call(func f){
- for(int i=0; i<10; i++){
- cout << f(i) << endl;
- }
- }
- // 進行測試
- int main(int argc, char* args[])
- {
- call(funcImpl);
- }
程序執(zhí)行結果是,打印出0到9。
這個函數指針與下面JavaScript的代碼有同樣的作用:
- function funcImpl(int num){
- return num;
- }
- (function call(f){
- if(f){
- if(f instance Function){
- for(int i=0; i<10; i++){
- alert(f(i));
- }
- }
- }
- })(funcImpl);
當然了與下面的Java代碼代碼也是一樣的:
- interface Callback{
- int doCall(int num);
- }
- static void call(Callback callback){
- if(callback==null) return;
- for(int i=0; i<10; i++){
- System.out.println(callback(i));
- }
- }
- public static void main(string[] args ){
- call(new Callback(){
- public int doCall(int num){
- return num;
- }
- });
- }
在C#中,它還有另外一個名字,delegate。
其實它們都是傳說中的鉤子函數callback。
2)頭文件、#include
在大學時,沒有寫過頭文件,也沒有看過頭文件。所以頭文件對我來說,一直是個謎。不過在學習了Java、C#后,就自然而然的會將#include 頭文件理解為import、using等。
那么頭文件中,會寫什么呢?
一般來說,會將聲明(類中的字段、方法的聲明)寫在.h文件中,將方法的實現,寫在cpp文件中。以此來達到接口與實現的分離。其他地方使用#include時,就只會看到.h文件中的聲明,看不到具體的實現。
另外要說的是#include的兩種方式。 例如#include <xxxx.h>、#include “xxxx.h”。這兩種方式,還是有區(qū)別的,<>方式是先從系統(tǒng)目錄下找.h文件,” ”則是先從用戶目錄下去找.h文件。有點類似于Java中ClassLoader了,默認采用委托加載,也可以使用子類優(yōu)先方式進行加載。
創(chuàng)建實例與回收
在C語言中,聲明一個變量,可以直接使用聲明的方式、也可以使用molloc的方式。
在C++中,又加入了一種new的方式,這種方式的寫法與Java中是一樣的。
創(chuàng)建 |
釋放 |
聲明(隱式):創(chuàng)建的是對象本身,而不是指針 |
隱式釋放,不需要通過寫代碼。因為聲明的對象在棧內,出棧后自動釋放 |
molloc(顯示):該方法用于分配內存,返回值是指針 |
使用free()進行釋放 |
new :分配內存,返回值是指針 |
使用delete 進行釋放 |
Molloc、new 分配內存后,返回值都是指針。且分配在內存中Heap區(qū),不會自動釋放,所以需要使用free、delete進行釋放。
另外在使用feee、delete后,最好是將指針的值設置為NULL, 因為free、delete只是釋放了對象占用的內存空間,而指針的值仍然是對象在被釋放前占用空間的首地址。
這與Java是不同的,Java能夠自動的進行回收。對象設置為null即可。Java中的回收機制是:采用分代回收算法對于不可達的對象進行回收。
- void fun(){
- Menu* m1=new Menu(); // 顯式聲明對象
- Menu m2; // 隱式聲明對象
- this->menulist->push_back(m1);
- this->menulist->push_back(&m2);
- }
- void showList(){
- list<Menu*>::iterator iter=this->menulist->begin();
- while(iter!=this->menulist->end()){
- Menu* menu=*iter;
- cout << m->toString() << endl;
- iter++;
- }
- }
這段代碼,在編譯時,是沒有問題的,也就是說從寫法來講,沒有錯誤的。但是在執(zhí)行showList()時就會出現空指針異常。原因如下:
在fun()中,創(chuàng)建的m1在heap中,不會自動的釋放,創(chuàng)建的m2,在stack中,會自動的釋放,當fun執(zhí)行完畢時m2對象實際已經不存在了。然后執(zhí)行showList()時,變量到m2對象時,肯定空的了,list中存儲的m2的指針,已經成為野指針了。
3)namespace
命名空間,在大多數語言中都有的。他們的作用都是為了區(qū)分。
//定義命名空間
namespace ns{
// your code
}
// 導入命名空間:
using namespace std;
// 使用命名空間:
std::xxxx
4)#define 、typedef
typeof 是為已有類型取別名。在編譯階段有效,由于是在編譯階段,因此typedef有類型檢查的功能。
#define是宏定義,發(fā)生在預處理階段,也就是編譯之前,它只進行簡單而機械的字符串替換,而不進行任何檢查。#define不只是可以為類型取別名,還可以定義常量、變量、編譯開關等。
5)操作符重載
學習C#時,知道可以對已有操作符進行重組,也就是賦予操作法新的功能。但是在C#中,我們很少這么做。Java中雖然沒有語言級別的支持,但是Java中字符串拼接使用的+,其實就可以看做是操作符的重載。
在了解到C++中有操作符重載后,哦,原來這一點,C#是借鑒C++的呀。另外C#中還保留了struct。說到struct,再提一點,struct完全可以理解為C語言中的類。
C++ 中則使用了大量的操作符重載。具體的怎么去定義操作符重載,用到的時候再說吧。
一天半時間,了解的東西真的不多,都是最基本的。雖然我也知道C++中的字符串拼接沒有Java、JavaScript那么隨意那么任性。但是這些不屬于難點,所以我認為不需要再提了。
最后,開一個玩笑,不懂JavaScript的Java程序員不是一個好的C++程序員。不懂Java的JavaScript程序員也不是一個好的C++程序員。