C++與Java語法上的不同


最近學習算法和刷題基本都是用C++寫的程序,在這個過程中,發現C++和Java在語法上有很多相同點,但也有很多不同點,而這些不同點對於已經掌握Java的程序員來說,理解C++代碼可能會有些吃力甚至困難,在踩過了一個又一個坑后,我決定把這些不同記錄下來,可能涵蓋的不是很全,所以本文會持續更新。

零、目錄

1. new關鍵字
2. C++實例化對象的幾種方式
3. C++初始化表達式
4. C++構造函數后的冒號
5. C++中難以省略的分號;
6. C++中的::
7. C++中的*、&、->
8. C++函數前或者函數后的const
9. delete完成后需要將指針置為NULL

一、new關鍵字

// C++中:
MyClass* a = new MyClass();
delete a;
// Java中:
MyClass a = new MyClass();
  1. C++中new關鍵字實例化對象之后返回的是一個對象的指針,所以需要定義指針;而Java中定義對象時返回的其實也是一個指針(引用),但是在Java中不需要顯示的定義指針。
    不同點
  2. C++中通過new關鍵字實例化的對象在使用完畢后需要使用delete關鍵字手動釋放內存;而Java中由於垃圾回收機制的存在,我們無需手動釋放內存。

二、C++實例化對象的兩種方式

// 方式一
MyClass* a = new MyClass();
delete a;
// 方式二
MyClass b(params...); // 該方式將會調用有參數的構造函數
MyClass c; //該方式將會調用無參數的構造函數
  1. 方式一中new關鍵字做了三件事:首先在堆內存上獲得內存空間(類似malloc函數),然后調用類的構造函數,最后返回該內存空間的指針;因為是在堆內存的動態分配的內存空間,所以最后需要調用delete函數釋放這一部分內存空間。
  2. 方式二中直接在棧內存上分配了存儲空間,類似於基本類型的定義,所以無需手動釋放內存空間,其會在作用域生命周期結束時自動調用析構函數釋放內存;但是當對象信息量較大時,這種做法會占用很多內存,影響程序效率。

三、C++初始化表達式

int a(10); // 等效於 int a = 10;

與java不同,C++除了使用賦值語句初始化變量之外,還可以使用上述初始化表達式對變量進行初始化,其效果相同。

四、C++構造函數后的冒號

// 初始化表達式
class MyClass {
  public:
    const int a;
    int b;
    MyClass(int aVal, int bVal):a(aVal), b(bVal) {}
};
// 非初始化表達式,無法通過編譯,編譯時會報以下錯誤:
// 'const int MyClass::a' should be initialized
class MyClass {
  public:
    const int a;
    int b;
    MyClass(int aVal, int bVal) {
       a = aVal;
       b = bVal;
    }
};

這個語法其實就是上述第四點中的初始化表達式,但這里我們看到下面這種賦值方式在構造函數里面會報錯,這又是為什么呢?因為這里我們的冒號初始化和賦值語句干的事情不太一樣:在冒號初始化時,只有冒號后面有相應變量的初始化表達式,就會在為成員變量分配內存時進行初始化,所以常量a在分配內存時做了初始化操作,之后也沒有被改變值,所以不會報錯;而在使用賦值語句時,構造函數在執行到函數體時已經為a分配了內存但卻沒有對其進行初始化,因此就會報should be initialized編譯錯誤了。

這種神奇的寫法我第一次見的時候也是驚訝了一波。

五、C++中難以省略的分號;

在上面例子中我們看到了C++類的定義后面也要加分號;我們知道在Java中類定義的大括號后面都無需加分號。
之后查資料發現:
在C++中,除函數,及預編譯指令外,其它的語句或代碼段結尾都必須要加分號。其中預編譯指令是指,以#開頭的語句。常見的有,#include ,#define, #ifdef, #if, #elif, #else,#endif等。

六、C++中的::(作用域符)

C++的作用域符::用法有三,一一介紹如下

  1. 全局作用域符
int a = 3;
void test() {
  int a = ::a;
  cout << a << endl;
}

這里就是將全局變量a的值通過全局作用域符的方式訪問到,然后將其值賦給test函數中的局部變量a。
2. 類作用域符

class MyClass {
  public:
    void sayHello();
};
void MyClass::sayHello() {
  cout << "hello world!" << endl;
}

這里有點類似Java中定義接口和對接口進行實現;首先在類的定義中定義了一個函數sayHello,但是沒有具體函數體,然后在類的定義外,通過作用域符,在函數sayHello前加了一個MyClass::,相當於告訴編譯器,這個函數就是MyClass類的sayHello方法的具體實現。
3. 命名空間作用域符

std::cout << "hello world!" << endl;

這里std就是命名空間,這樣的用法相當於using namespace std;

七、C++中的*、&、->

class MyClass {
  public:
    int a;
    MyClass(int aVal):a(aVal){}
};
int main () {
  MyClass obj(5);
  MyClass* objPointer = new MyClass(3);
  // 常規操作
  cout << obj.a << endl;
  // 取指針操作
  cout << (*objPointer).a << endl;
  // ->操作
  cout << objPointer->a << endl;
  // 取地址操作
  cout << (&obj)->a << endl;
  // new出來的對象別忘了釋放內存哦
  delete objPointer;
  return 0;
}

在C++中:

  1. *表示訪問指針a指向的變量;
  2. &表示取得變量的地址;
  3. .表示訪問對象的的成員變量或者函數;(與Java相同)
  4. ->表示訪問指針指向的對象的成員變量或者函數;

指針內容實在太多,這里就不贅述了,有興趣的童鞋可以參考菜鳥教程

八、C++函數名前或者函數名后的const

  1. 函數名前的const
const int getVal() {
  const int a = 3;
  return a;
}
int main() {
  cout << getVal() << endl;
}

就是定義函數的返回類型,規定返回變量類型必須為const。
2. 函數名后的const(用於類的成員函數中)

class MyClass {
  public:
    int a;
    MyClass(int aVal):a(aVal){}
    void sayHello() const {
      cout << "a value is: " << a << endl;
    }
    // 編譯報錯:assignment of member 'MyClass::a' in read-only object
    void changeA() const {
      a = 5;
    }
};

函數名后面的const表示是該函數為常成員函數,該函數不能修改對象內的任何成員變量,只能發生讀操作,不能發生寫操作。這樣可以對成員變量起到保護作用。

九、delete完成后需要將指針置為NULL

MyClass* a = new MyClass();
delete a;
// 省略下面這句運行時會出現以下錯誤:
// pointer being freed was not allocated
a = NULL;
delete a;

對一個非空指針delete后,若沒有置為NULL,若再次delete的話,有可能出現問題。而C++標准規定:delete空指針是合法的,沒有副作用。所以每次delete之后要將指針的值置為NULL。這是一個不得不養成的好習慣,hah...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM