音視頻學習 (二) C++ 語言入門

前言

上一篇文章咱們學習了 C 語言基礎,那麼按照我們的學習計劃該學習 C++ 語言基礎了,若是沒有 C/C++ 基礎了能夠按照個人文章序列跟着敲一篇,不會沒什麼可怕的,可怕的是不會還不練習,光看是學不會的。前面幾篇學習語言基礎我知道很枯燥,可是沒有 C/C++ 語言基礎到時候學習 NDK 的內容你就更看不懂了,徹底就像是在看科幻片同樣。因此,咱們一塊兒加油,一塊兒學習,共同進步 。html

簡介

C++ 是一種中級語言,它是由 Bjarne Stroustrup 於 1979 年在貝爾實驗室開始設計開發的。C++ 進一步擴充和完善了 C 語言,是一種面向對象的程序設計語言。C++ 可運行於多種平臺上,如 Windows、MAC 操做系統以及 UNIX 的各類版本。ios

環境配置

若是您使用的是 Mac ,最快捷的獲取 GCC 的方法是從蘋果的網站上下載 Xcode 開發環境,並按照安裝說明進行安裝。一旦安裝上 Xcode,您就能使用 GNU 編譯器。c++

開發工具我這裏仍是使用的 CLion , 下一篇文章講解 JNI 技術就開始用 AndroidStudio 。程序員

C++ 語言入門

仍是以 」HelloWorld「 打開 C++ 的門。express

1. 基本語法

#include <iostream> 
// 空間命名
using namespace std;
int main(){//跟 C 同樣入口都是 main 函數
  test1();
  return 0;
}
void test1() {
    //1. 使用命名空間
// cout << "C++ 語言入門第一行代碼 Hello World!" << endl;
  	//就是一個對 C++ 打印的封裝
    println("", "C++ 語言入門第一行代碼 Hello World!");
    //未使用命名空間
// std::cout << "C++ 語言入門第一行代碼 Hello World!" << std::endl;
}
複製代碼

輸出:編程

C++ 語言入門第一行代碼 Hello World!
複製代碼

下面咱們就來解釋一下上面這段最簡單的程序的組成部分數組

  • C++ 語言定義了一些頭文件,這些頭文件包含了程序中必需的或有用的信息。上面這段程序中,包含了頭文件
  • 下一行是一個 // 的註釋。
  • 下一行 using namespace std; 告訴編譯器使用 std 命名空間。命名空間是 C++ 中一個相對新的概念。
  • 下一行 int main() 是主函數,程序從這裏開始執行。
  • 下一行調用 test1() 函數
  • 下一行 cout << "C++ 語言入門第一行代碼 Hello World!" << endl; 會在屏幕上顯示消息 "C++ 語言入門第一行代碼 Hello World!"。
  • 下一行 return 0; 終止 main( )函數,並向調用進程返回值 0。

中間忽略了一些註釋講解,我相信這仍是能看懂的。;安全

1. 使用 g++ 編譯bash

$ g++ *.cpp *.h
$ ./a.out
複製代碼

g++ 後面跟用到了的 cpp h 文件網絡

2. 使用 Clion 工具編譯

直接點擊 run,以下所示:

跟 C,Java 同樣。

2. C++ 關鍵字

下面表格描述了 C++ 中的關鍵字。這些關鍵字跟 C/Java 同樣都不能做爲常量名,變量名或其它標識符名稱。

關鍵字 說明 關鍵字 說明
asm 容許在 C++ 程序中嵌入彙編代碼 auto (自動,automatic)是存儲類型標識符,代表變量"自動"具備本地範圍,塊範圍的變量聲明(如for循環體內的變量聲明)默認爲auto存儲類型。
bool bool(布爾)類型,C++ 中的基本數據結構,其值可選爲 true(真)或者 false(假)。C++ 中的 bool 類型能夠和 int 混用,具體來講就是 0 表明 false,非 0 表明 true。bool 類型經常使用於條件判斷和函數返回值。 break break(中斷、跳出),用在switch語句或者循環語句中。程序遇到 break 後,即跳過該程序段,繼續後面的語句執行。
case 用於 switch 語句中,用於判斷不一樣的條件類型。 namespace namespace(命名空間)用於在邏輯上組織類,是一種比類大的結構。
catch catch 和 try 語句一塊兒用於異常處理。 new new(新建)用於新建一個對象。new 運算符老是返回一個指針。由 new 建立
char char(字符,character)類型,C++ 中的基本數據結構,其值通常爲 0~255 的 int。這 256 個字符對應着 256 個 ASCII 碼。char 類型的數據須要用單引號 ' 括起來。 operator operator(操做符)用於操做符重載。這是 C++ 中的一種特殊的函數。
class class(類)是 C++ 面向對象設計的基礎。使用 class 關鍵字聲明一個類。 private private(私有的),C++ 中的訪問控制符。被標明爲 private 的字段只能在本類以及友元中訪問。
const const(常量的,constant)所修飾的對象或變量不能被改變,修飾函數時,該函數不能改變在該函數外面聲明的變量也不能調用任何非const函數。在函數的聲明與定義時都要加上const,放在函數參數列表的最後一個括號後。在 C++ 中,用 const 聲明一個變量,意味着該變量就是一個帶類型的常量,能夠代替 #define,且比 #define 多一個類型信息,且它執行內連接,可放在頭文件中聲明;但在 C 中,其聲明則必須放在源文件(即 .C 文件)中,在 C 中 const 聲明一個變量,除了不能改變其值外,它還是一具變量。 protected protected(受保護的),C++ 中的訪問控制符。被標明爲 protected 的字段只能在本類以及其繼承類和友元中訪問。
const_cast 該運算符用來修改類型的 const 或 volatile 屬性。除了 const 或 volatile 修飾以外, type_id 和 expression 的類型是同樣的。常量指針被轉化成很是量指針,而且仍然指向原來的對象;常量引用被轉換成很是量引用,而且仍然指向原來的對象;常量對象被轉換成很是量對象。 public public(公有的),C++ 中的訪問控制符。被標明爲 public 的字段能夠在任何類
continue continue(繼續)關鍵字用於循環結構。它使程序跳過代碼段後部的部分,與 break 不一樣的是,continue 不是進入代碼段後的部分執行,而是從新開始新的循環。於是它是"繼續循環"之意,不是 break(跳出)。 register register(寄存器)聲明的變量稱着寄存器變量,在可能的狀況下會直接存放在機器的寄存器中;但對 32 位編譯器不起做用,當 global optimizations(全局優化)開的時候,它會作出選擇是否放在本身的寄存器中;不過其它與 register 關鍵字有關的其它符號都對32位編譯器有效。
default default(默認、缺省)用於 switch 語句。當 switch 全部的 case 都不知足時,將進入 default 執行。default 只能放在 switch 語句全部的 case 以後,而且是可選的。 reinterpret_cast type-id 必須是一個指針、引用、算術類型、函數指針或者成員指針。它能夠把一個指針轉換成一個整數,也能夠把一個整數轉換成一個指針(先把一個指針轉換成一個整數,在把該整數轉換成原類型的指針,還能夠獲得原先的指針值)。
delete delete(刪除)釋放程序動態申請的內存空間。delete 後面一般是一個指針或者數組 [],而且只能 delete 經過 new 關鍵字申請的指針,不然會發生段錯誤。 return return(返回)用於在函數中返回值。程序在執行到 return 語句後當即返回,return 後面的語句沒法執行到。
do do-while是一類循環結構。與while循環不一樣,do-while循環保證至少要進入循環體一次。 short short(短整型,short integer),C++ 中的基本數據結構,用於表示整數,精度小於 int。
double double(雙精度)類型,C++ 中的基本數據結構,以雙精度形式存儲一個浮點數。 signed signed(有符號),代表該類型是有符號數,和 unsigned 相反。數字類型(整型和浮點型)均可以用 signed 修飾。但默認就是 signed,因此通常不會顯式使用。
dynamic_cast dynamic_cast(動態轉換),容許在運行時刻進行類型轉換,從而使程序可以在一個類層次結構安全地轉換類型。dynamic_cast 提供了兩種轉換方式,把基類指針轉換成派生類指針,或者把指向基類的左值轉換成派生類的引用。 sizeof 因爲 C++ 每種類型的大小都是由編譯器自行決定的,爲了增長可移植性,能夠用 sizeof 運算符得到該數據類型佔用的字節數。
else else 緊跟在 if 後面,用於對 if 不成立的狀況的選擇。 static static(靜態的)靜態變量做用範圍在一個文件內,程序開始時分配空間,結束時釋放空間,默認初始化爲 0,使用時可改變其值。靜態變量或靜態函數,只有本文件內的代碼纔可訪問它,它的名字(變量名或函數名)在其它文件中不可見。所以也稱爲"文件做用域"。在 C++ 類的成員變量被聲明爲 static(稱爲靜態成員變量),意味着它被該類的全部實例所共享,也就是說當某個類的實例修改了該靜態成員變量,其修改值爲該類的其它全部實例所見;而類的靜態成員函數也只能訪問靜態成員(變量或函數)。類的靜態成員變量必須在聲明它的文件範圍內進行初始化才能使用,private 類型的也不例外。
enum enum(枚舉)類型,給出一系列固定的值,只能在這裏面進行選擇一個。 static_cast 該運算符把 expression 轉換爲 type-id 類型,但沒有運行時類型檢查來保證轉換的安全性。
explicit explicit(顯式的)的做用是"禁止單參數構造函數"被用於自動型別轉換,其中比較典型的例子就是容器類型。在這種類型的構造函數中你能夠將初始長度做爲參數傳遞給構造函數。 struct struct(結構)類型,相似於 class 關鍵字,與 C 語言兼容(class 關鍵字是不與 C 語言兼容的),能夠實現面向對象程序設計。
export 爲了訪問其餘編譯單元(如另外一代碼文件)中的變量或對象,對普通類型(包括基本數據類、結構和類),能夠利用關鍵字 extern,來使用這些變量或對象時;可是對模板類型,則必須在定義這些模板類對象和模板函數時,使用標準 C++ 新增長的關鍵字 export(導出)。 switch switch(轉換)相似於 if-else-if 語句,是一種多分枝語句。它提供了一種簡潔的書寫,而且可以生成效率更好的代碼。可是,switch 後面的判斷只能是int(char也能夠,但char本質上也是一種int類型)。switch 語句最後的 default 分支是可選的。
extern extern(外部的)聲明變量或函數爲外部連接,即該變量或函數名在其它文件中可見。被其修飾的變量(外部變量)是靜態分配空間的,即程序開始時分配,結束時釋放。用其聲明的變量或函數應該在別的文件或同一文件的其它地方定義(實現)。在文件內聲明一個變量或函數默認爲可被外部使用。在 C++ 中,還可用來指定使用另外一語言進行連接,這時須要與特定的轉換符一塊兒使用。目前僅支持 C 轉換標記,來支持 C 編譯器連接。 template template(模板),C++ 中泛型機制的實現。
false false(假的),C++ 的基本數據結構 bool 類型的值之一。等同於 int 的 0 值。 this this 返回調用者自己的指針。
float float(浮點數),C++ 中的基本數據結構,精度小於 double。 throw throw(拋出)用於實現 C++ 的異常處理機制,能夠經過 throw 關鍵字"拋出"一個異常。
for for 是 C++ 中的循環結構之一。 true true(真的),C++ 的基本數據結構 bool 類型的值之一。等同於 int 的非 0 值。
friend friend(友元)聲明友元關係。友元能夠訪問與其有 friend 關係的類中的 private/protected 成員,經過友元直接訪問類中的 private/protected 成員的主要目的是提升效率。友元包括友元函數和友元類。 try try(嘗試)用於實現 C++ 的異常處理機制。能夠在 try 中調用可能拋出異常的函數,而後在 try 後面的 catch 中捕獲並進行處理。
goto goto(轉到),用於無條件跳轉到某一標號處開始執行。 typedef 類型說明定義了一個數據類型的新名字而不是定義一種新的數據類型。定義名錶示這個類型的新名字。
if if(若是),C++ 中的條件語句之一,能夠根據後面的 bool 類型的值選擇進入一個分支執行。 typeid 指出指針或引用指向的對象的實際派生類型。
inline inline(內聯)函數的定義將在編譯時在調用處展開。inline 函數通常由短小的語句組成,能夠提升程序效率。 typename typename(類型名字)關鍵字告訴編譯器把一個特殊的名字解釋成一個類型。
int int(整型,integer),C++ 中的基本數據結構,用於表示整數,精度小於 long。 union union(聯合),相似於 enum。不一樣的是 enum 實質上是 int 類型的,而 union 能夠用於全部類型,而且其佔用空間是隨着實際類型大小變化的。
long long(長整型,long integer),C++ 中的基本數據結構,用於表示長整數。 unsigned unsigned(無符號),代表該類型是無符號數,和 signed 相反。
mutable mutable(易變的)是 C++ 中一個不經常使用的關鍵字。只能用於類的非靜態和很是量數據成員。因爲一個對象的狀態由該對象的非靜態數據成員決定,因此隨着數據成員的改變,對像的狀態也會隨之發生變化。若是一個類的成員函數被聲明爲 const 類型,表示該函數不會改變對象的狀態,也就是該函數不會修改類的非靜態數據成員。可是有些時候須要在該類函數中對類的數據成員進行賦值,這個時候就須要用到 mutable 關鍵字。 using 代表使用 namespace。
virtual virtual(虛的),C++ 中用來實現多態機制。 void void(空的),能夠做爲函數返回值,代表不返回任何數據;能夠做爲參數,代表沒有參數傳入(C++中不是必須的);能夠做爲指針使用。
volatile volatile(不穩定的)限定一個對象可被外部進程(操做系統、硬件或併發線程等)改變 wchar_t wchar_t 是寬字符類型,每一個 wchar_t 類型佔 2 個字節,16 位寬。漢字的表示就要用到 wchar_t。

4. 數據類型

基本的內置類型

C++ 爲程序員提供了種類豐富的內置數據類型和用戶自定義的數據類型。下表列出了七種基本的 C++ 數據類型:

類型 關鍵字
布爾型 bool
字符型 char
整型 int
浮點型 float
雙浮點型 double
無類型 void
寬字符型 wchar_t (wchar_t 實際上的空間是和 short int 同樣)

下表顯示了各類變量類型在內存中存儲值時須要佔用的內存,以及該類型的變量所能存儲的最大值和最小值。

注意: 不一樣系統會有所差別。

類型 範圍
char 1 個字節 -128 到 127 或者 0 到 255
unsigned char 1 個字節 0 到 255
signed char 1 個字節 -128 到 127
int 4 個字節 -2147483648 到 2147483647
unsigned int 4 個字節 0 到 4294967295
signed int 4 個字節 -2147483648 到 2147483647
short int 2 個字節 -32768 到 32767
unsigned short int 2 個字節 0 到 65,535
signed short int 2 個字節 -32768 到 32767
long int 8 個字節 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
signed long int 8 個字節 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
unsigned long int 8 個字節 0 到 18,446,744,073,709,551,615
float 4 個字節 精度型佔4個字節(32位)內存空間,+/- 3.4e +/- 38 (~7 個數字)
double 8 個字節 雙精度型佔8 個字節(64位)內存空間,+/- 1.7e +/- 308 (~15 個數字)
long double 16 個字節 長雙精度型 16 個字節(128位)內存空間,可提供18-19位有效數字。
wchar_t 2 或 4 個字節 1 個寬字符

從上表可得知,變量的大小會根據編譯器和所使用的電腦而有所不一樣。

下面實例會輸出您電腦上各類數據類型的大小。

void test2() {
    println("C++\t\t", "**************基本數據類型 Size ***********\n");

    println("bool\t\t", "所佔字節數:", sizeof(bool), "\t\t最大值:",
            (numeric_limits<bool>::max)(), "\t\t最小值:", (numeric_limits<bool>::min)());
    println("char\t\t", "所佔字節數:", sizeof(char), "\t\t最大值:",
            (numeric_limits<char>::max)(), "\t\t最小值:", (numeric_limits<char>::min)());
    println("unsigned char\t\t", "所佔字節數:", sizeof(unsigned char), "\t\t最大值:",
            (numeric_limits<unsigned char>::max)(), "\t\t最小值:", (numeric_limits<unsigned char>::min)());
    println("signed char\t\t", "所佔字節數:", sizeof(signed char), "\t\t最大值:",
            (numeric_limits<signed char>::max)(), "\t\t最小值:", (numeric_limits<signed char>::min)());
    println("int\t\t", "所佔字節數:", sizeof(int), "\t\t最大值:",
            (numeric_limits<int>::max)(), "\t\t最小值:", (numeric_limits<int>::min)());
    println("unsigned int\t\t", "所佔字節數:", sizeof(unsigned int), "\t\t最大值:",
            (numeric_limits<unsigned int>::max)(), "\t\t最小值:", (numeric_limits<unsigned int>::min)());
    println("signed int\t\t", "所佔字節數:", sizeof(signed int), "\t\t最大值:",
            (numeric_limits<signed int>::max)(), "\t\t最小值:", (numeric_limits<signed int>::min)());
    println("short int\t\t", "所佔字節數:", sizeof(short int), "\t\t最大值:",
            (numeric_limits<short int>::max)(), "\t\t最小值:", (numeric_limits<short int>::min)());
    println("unsigned short int\t\t", "所佔字節數:", sizeof(unsigned short int), "\t\t最大值:",
            (numeric_limits<unsigned short int>::max)(), "\t\t最小值:", (numeric_limits<unsigned short int>::min)());
    println("signed short int\t\t", "所佔字節數:", sizeof(signed short int), "\t\t最大值:",
            (numeric_limits<signed short int>::max)(), "\t\t最小值:", (numeric_limits<signed short int>::min)());
    println("long int\t\t", "所佔字節數:", sizeof(long int), "\t\t最大值:",
            (numeric_limits<long int>::max)(), "\t\t最小值:", (numeric_limits<long int>::min)());
    println("signed long int\t\t", "所佔字節數:", sizeof(signed long int), "\t\t最大值:",
            (numeric_limits<signed long int>::max)(), "\t\t最小值:", (numeric_limits<signed long int>::min)());
    println("unsigned long int\t\t", "所佔字節數:", sizeof(unsigned long int), "\t\t最大值:",
            (numeric_limits<unsigned long int>::max)(), "\t\t最小值:", (numeric_limits<unsigned long int>::min)());
    println("float\t\t", "所佔字節數:", sizeof(float), "\t\t最大值:",
            (numeric_limits<float>::max)(), "\t\t最小值:", (numeric_limits<float>::min)());
    println("double\t\t", "所佔字節數:", sizeof(double), "\t\t最大值:",
            (numeric_limits<double>::max)(), "\t\t最小值:", (numeric_limits<double>::min)());
    println("long double\t\t", "所佔字節數:", sizeof(long double), "\t\t最大值:",
            (numeric_limits<long double>::max)(), "\t\t最小值:", (numeric_limits<long double>::min)());
    println("wchar_t\t\t", "所佔字節數:", sizeof(wchar_t), "\t\t最大值:",
            (numeric_limits<wchar_t>::max)(), "\t\t最小值:", (numeric_limits<wchar_t>::min)());

}
複製代碼

輸出:

/**
 *   bool                 所佔字節數:1       最大值:1                 最小值:0
 *   char                 所佔字節數:1       最大值:127               最小值:-128
 *   unsigned char        所佔字節數:1       最大值:255               最小值:0
 *   signed char          所佔字節數:1       最大值:127               最小值:-128
 *   int                  所佔字節數:4       最大值:2147483647        最小值:-2147483648
 *   unsigned int         所佔字節數:4       最大值:-1            		最小值:0
 *   signed int           所佔字節數:4       最大值:2147483647    		最小值:-2147483648
 *   short int            所佔字節數:2       最大值:32767             最小值:-32768
 *   unsigned short int   所佔字節數:2       最大值:65535             最小值:0
 *   signed short int     所佔字節數:2       最大值:32767             最小值:-32768
 *   long int             所佔字節數:8       最大值:-1            		最小值:0
 *   signed long int      所佔字節數:8       最大值:-1            		最小值:0
 *   unsigned long int    所佔字節數:8       最大值:-1            		最小值:0
 *   float                所佔字節數:4       最大值:-2147483648       最小值:0
 *   double               所佔字節數:8       最大值:-2147483648       最小值:0
 *   long double          所佔字節數:16      最大值:-2147483648       最小值:0
 *   wchar_t              所佔字節數:4       最大值:2147483647    		最小值:-2147483648
*/
複製代碼

typedef 聲明

您可使用 typedef 爲一個已有的類型取一個新的名字。下面是使用 typedef 定義一個新類型的語法:

//格式: typedef type newname; 
//例子
typedef int feet;
複製代碼

如今,下面的聲明是徹底合法的,它建立了一個整型變量 distance:

feet distance;
複製代碼

枚舉類型

枚舉類型(enumeration)是C++中的一種派生數據類型,它是由用戶定義的若干枚舉常量的集合。

若是一個變量只有幾種可能的值,能夠定義爲枚舉(enumeration)類型。所謂"枚舉"是指將變量的值一一列舉出來,變量的值只能在列舉出來的值的範圍內。

建立枚舉,須要使用關鍵字 enum 跟 Java 語法差很少。枚舉類型的通常形式爲:

enum 枚舉名{ 
     標識符[=整型常數], 
     標識符[=整型常數], 
... 
    標識符[=整型常數]
} 枚舉變量;
複製代碼

若是枚舉沒有初始化, 即省掉"=整型常數"時, 則從第一個標識符開始。

例如,下面的代碼定義了一個顏色枚舉,變量 c 的類型爲 color。最後,c 被賦值爲 "blue"。

enum color { red, green, blue } c;
c = blue;
複製代碼

默認狀況下,第一個名稱的值爲 0,第二個名稱的值爲 1,第三個名稱的值爲 2,以此類推。可是,您也能夠給名稱賦予一個特殊的值,只須要添加一個初始值便可。例如,在下面的枚舉中,green 的值爲 5。

enum color { red, green=5, blue };
複製代碼

在這裏,blue 的值爲 6,由於默認狀況下,每一個名稱都會比它前面一個名稱大 1,但 red 的值依然爲 0。

5. 變量類型

變量其實只不過是程序可操做的存儲區的名稱。C++ 中每一個變量都有指定的類型,類型決定了變量存儲的大小和佈局,該範圍內的值均可以存儲在內存中,運算符可應用於變量上。

變量的名稱能夠由字母、數字和下劃線字符組成。它必須以字母或下劃線開頭。大寫字母和小寫字母是不一樣的,由於 C++ 是大小寫敏感的。

C++ 也容許定義各類其餘類型的變量,好比枚舉、指針、數組、引用、數據結構、類等等,這將會在後續的章節中進行講解。

下面咱們將講解如何定義、聲明和使用各類類型的變量。

定義:

變量定義就是告訴編譯器在何處建立變量的存儲,以及如何建立變量的存儲。變量定義指定一個數據類型,幷包含了該類型的一個或多個變量的列表,以下所示:

//格式:type variable_list;
//在這裏,type 必須是一個有效的 C++ 數據類型,能夠是 char、wchar_t、int、float、double、bool 或任何用戶自定義的對象,variable_list 能夠由一個或多個標識符名稱組成,多個標識符之間用逗號分隔。

//例子:
int    i, j, k;
char   c, ch;
float  f, salary;
double d;

複製代碼

變量聲明:

變量聲明向編譯器保證變量以給定的類型和名稱存在,這樣編譯器在不須要知道變量完整細節的狀況下也能繼續進一步的編譯。變量聲明只在編譯時有它的意義,在程序鏈接時編譯器須要實際的變量聲明。

當您使用多個文件且只在其中一個文件中定義變量時(定義變量的文件在程序鏈接時是可用的),變量聲明就顯得很是有用。您可使用 extern 關鍵字在任何地方聲明一個變量。雖然您能夠在 C++ 程序中屢次聲明一個變量,但變量只能在某個文件、函數或代碼塊中被定義一次。

例子:

//聲明變量
extern int a, b;
extern int c;
extern float f;

void test3() {
//變量定義
    int a, b;
    int c;
    float f;

//init
    a = 10;
    b = 20;
    c = a + b;
    println("聲明變量\t", "a + b=", c);

    f = 10 / 3;
    println("聲明變量\t", "10/3=", f);
}
複製代碼

輸出:

聲明變量 a + b=30 聲明變量 10/3=3

6. 變量做用域

做用域是程序的一個區域,通常來講有三個地方能夠定義變量:

  • 在函數或一個代碼塊內部聲明的變量,稱爲局部變量。
  • 在函數參數的定義中聲明的變量,稱爲形式參數。
  • 在全部函數外部聲明的變量,稱爲全局變量。

咱們將在後續的小節中學習什麼是函數和參數。本小節咱們先來說解什麼是局部變量和全局變量。

局部變量:

在函數或一個代碼塊內部聲明的變量,稱爲局部變量。它們只能被函數內部或者代碼塊內部的語句使用。下面的實例使用了局部變量:

void test4() {
    //局部變量聲明
    int i = 10, j = 20;
    int c;
    c = i * j;
    println("局部變量:", "i * j=", c);

}
複製代碼

輸出:

局部變量:i * j=200

全局變量:

在全部函數外部定義的變量(一般是在程序的頭部),稱爲全局變量。全局變量的值在程序的整個生命週期內都是有效的。

全局變量能夠被任何函數訪問。也就是說,全局變量一旦聲明,在整個程序中都是可用的。下面的實例使用了全局變量和局部變量:

int g, l = 20;//聲明全局變量

void test4() {
    //局部變量聲明
    int i = 10, j = 10;
    int c;
    c = i * j;
    println("局部變量:", "i * j=", c);

    //賦值給全局變量
    g = (int)i / j;
    println("全局變量:", "i / j=", g);

    println("全局變量:", "l =", l);
    l = 100;//從新賦值給全局變量 l
    println("全局變量:", "l =", l);

}
複製代碼

輸出:

局部變量:i * j=100 全局變量:i / j=1 全局變量:l =20 全局變量:l =100

初始化局部變量和全局變量:

當局部變量被定義時,系統不會對其初始化,您必須自行對其初始化。定義全局變量時,系統會自動初始化爲下列值:

數據類型 初始化默認值
int 0
char '\0'
float 0
double 0
pointer NULL

正確地初始化變量是一個良好的編程習慣,不然有時候程序可能會產生意想不到的結果。

7. 常量

常量是固定值,在程序執行期間不會改變。這些固定的值,又叫作字面量

整數常量:

整數常量能夠是十進制、八進制或十六進制的常量。前綴指定基數:0x 或 0X 表示十六進制,0 表示八進制,不帶前綴則默認表示十進制。

整數常量也能夠帶一個後綴,後綴是 U 和 L 的組合,U 表示無符號整數(unsigned),L 表示長整數(long)。後綴能夠是大寫,也能夠是小寫,U 和 L 的順序任意。

下面列舉幾個整數常量的實例:

212         // 合法的
215u        // 合法的
0xFeeL      // 合法的
078         // 非法的:8 不是八進制的數字
032UU       // 非法的:不能重複後綴
  
85         // 十進制
0213       // 八進制 
0x4b       // 十六進制 
30         // 整數 
30u        // 無符號整數 
30l        // 長整數 
30ul       // 無符號長整數
複製代碼

浮點常量:

浮點常量由整數部分、小數點、小數部分和指數部分組成。您可使用小數形式或者指數形式來表示浮點常量。

當使用小數形式表示時,必須包含整數部分、小數部分,或同時包含二者。當使用指數形式表示時, 必須包含小數點、指數,或同時包含二者。帶符號的指數是用 e 或 E 引入的。

下面列舉幾個浮點常量的實例:

3.14159       // 合法的 
314159E-5L    // 合法的 
510E          // 非法的:不完整的指數
210f          // 非法的:沒有小數或指數
.e55          // 非法的:缺乏整數或分數
複製代碼

布爾常量:

布爾常量共有兩個,它們都是標準的 C++ 關鍵字:

  • true 值表明真。
  • false 值表明假。

咱們不該把 true 的值當作 1,把 false 的值當作 0。

字符常量:

字符常量是括在單引號中。若是常量以 L(僅當大寫時)開頭,則表示它是一個寬字符常量(例如 L'x'),此時它必須存儲在 wchar_t 類型的變量中。不然,它就是一個窄字符常量(例如 'x'),此時它能夠存儲在 char 類型的簡單變量中。

字符常量能夠是一個普通的字符(例如 'x')、一個轉義序列(例如 '\t'),或一個通用的字符(例如 '\u02C0')。

在 C++ 中,有一些特定的字符,當它們前面有反斜槓時,它們就具備特殊的含義,被用來表示如換行符(\n)或製表符(\t)等。下表列出了一些這樣的轉義序列碼:

轉義序列 含義
\ \ 字符
' ' 字符
" " 字符
? ? 字符
\a 警報鈴聲
\b 退格鍵
\f 換頁符
\n 換行符
\r 回車
\t 水平製表符
\v 垂直製表符
\ooo 一到三位的八進制數
\xhh . . . 一個或多個數字的十六進制數
void test5() {
    //字符常量
    println("字符常量", "\tC\n+\n+\n");
}
複製代碼

輸出:

字符常量 C + +

字符串常量:

字符串字面值或常量是括在雙引號 "" 中的。一個字符串包含相似於字符常量的字符:普通的字符、轉義序列和通用的字符。

您可使用空格作分隔符,把一個很長的字符串常量進行分行。

下面的實例顯示了一些字符串常量。下面這三種形式所顯示的字符串是相同的。

"hello, World"

"hello, \

World"

"hello, " "W" "orld"
複製代碼

定義常量:

在 C++ 中,有兩種簡單的定義常量的方式:

  • 使用 #define 預處理器。
  • 使用 const 關鍵字。
//定義常量 const,#define
#define NAME "DevYK"
#define Blog "https://www.devyk.top"

const string _NAME = "DevYK", _Blog = "https://www.devyk.top";

void test5() {
    //打印定義的常量
    println("#define 定義常量:", NAME "\t" Blog);
    //const 定義的常量
    const int length = 10, width = 20, height = 30;
    cout << "const 定義的常量\t" << _NAME << "\t" + _Blog << endl;
    println("const 定義常量\t", "求 width * height * length = ", length * width * height);
}
複製代碼

輸出:

define 定義常量:DevYK www.devyk.top const 定義的常量 DevYK www.devyk.top const 定義常量 求 width * height * length = 6000

8. 修飾符類型

C++ 容許在 char、int 和 double 數據類型前放置修飾符。修飾符用於改變基本類型的含義,因此它更能知足各類情境的需求。

下面列出了數據類型修飾符:

  • signed
  • unsigned
  • long
  • short

修飾符 signed、unsigned、long 和 short 可應用於整型,signedunsigned 可應用於字符型,long 可應用於雙精度型。

修飾符 signedunsigned 也能夠做爲 longshort 修飾符的前綴。例如:unsigned long int

C++ 容許使用速記符號來聲明無符號短整數無符號長整數。您能夠不寫 int,只寫單詞 unsigned、shortunsigned、long,int 是隱含的。例如,下面的兩個語句都聲明瞭無符號整型變量。

unsigned x;
unsigned int y;
複製代碼

爲了理解 C++ 解釋有符號整數和無符號整數修飾符之間的差異,咱們來運行一下下面這個短程序:

void test6() {
    //演示有符號和無符號整數之間的差異
    short int i;
    int k;
    short unsigned int j;
    j = 50000;
    i = j;
    k = j;
    cout << i << " " << j << " " << k << endl;

}
複製代碼

輸出:

-15536 50000 50000

上述結果中,無符號短整數 50,000 的位模式被解釋爲有符號短整數 -15,536。

9. 存儲類

存儲類定義 C++ 程序中變量/函數的範圍(可見性)和生命週期。這些說明符放置在它們所修飾的類型以前。下面列出 C++ 程序中可用的存儲類:

  • auto
  • register
  • static
  • extern
  • mutable
  • thread_local (C++11)

從 C++ 17 開始,auto 關鍵字再也不是 C++ 存儲類說明符,且 register 關鍵字被棄用。

auto 存儲類:

自 C++ 11 以來,auto 關鍵字用於兩種狀況:聲明變量時根據初始化表達式自動推斷該變量的類型、聲明函數時函數返回值的佔位符。

C++98標準中auto關鍵字用於自動變量的聲明,但因爲使用極少且多餘,在C++11中已刪除這一用法。

根據初始化表達式自動推斷被聲明的變量的類型,如:

auto f=3.14;      //double
auto s("hello");  //const char*
auto z = new auto(9); // int*
auto x1 = 5, x2 = 5.0, x3='r';//錯誤,必須是初始化爲同一類型
複製代碼

register 存儲類:

register 存儲類用於定義存儲在寄存器中而不是 RAM 中的局部變量。這意味着變量的最大尺寸等於寄存器的大小(一般是一個詞),且不能對它應用一元的 '&' 運算符(由於它沒有內存位置)。

{
   register int  miles;
}
複製代碼

寄存器只用於須要快速訪問的變量,好比計數器。還應注意的是,定義 'register' 並不意味着變量將被存儲在寄存器中,它意味着變量可能存儲在寄存器中,這取決於硬件和實現的限制。

static 存儲類:

static 存儲類指示編譯器在程序的生命週期內保持局部變量的存在,而不須要在每次它進入和離開做用域時進行建立和銷燬。所以,使用 static 修飾局部變量能夠在函數調用之間保持局部變量的值。

static 修飾符也能夠應用於全局變量。當 static 修飾全局變量時,會使變量的做用域限制在聲明它的文件內。

在 C++ 中,當 static 用在類數據成員上時,會致使僅有一個該成員的副本被類的全部對象共享。

//函數聲明
void test7_fun(void);

//聲明全局變量
static int valueCount = 5;


void test7() {
    //1.
    while(valueCount--){
        test7_fun();
    }
}

void test7_fun() {
    //使用 static 修飾局部變量能夠在函數調用之間保持局部變量的值。
    static int value = 5;
    value++;
    cout << "變量 value 爲:" << value << endl;
    cout << "變量 valueCount 爲:" << valueCount << endl;
}
複製代碼

輸出:

變量 value 爲:6 變量 valueCount 爲:4 變量 value 爲:7 變量 valueCount 爲:3 變量 value 爲:8 變量 valueCount 爲:2 變量 value 爲:9 變量 valueCount 爲:1 變量 value 爲:10 變量 valueCount 爲:0

extern 存儲類:

extern 存儲類用於提供一個全局變量的引用,全局變量對全部的程序文件都是可見的。當您使用 'extern' 時,對於沒法初始化的變量,會把變量名指向一個以前定義過的存儲位置。

當您有多個文件且定義了一個能夠在其餘文件中使用的全局變量或函數時,能夠在其餘文件中使用 extern 來獲得已定義的變量或函數的引用。能夠這麼理解,extern 是用來在另外一個文件中聲明一個全局變量或函數。

extern 修飾符一般用於當有兩個或多個文件共享相同的全局變量或函數的時候,以下所示:

第一個文件: main.cpp

//extern 存儲值
extern void value_extern();

void test7() {
    value_extern();
}
複製代碼

第二個文件:main_1.cpp

#include <iostream>

using namespace std;
void value_extern(void){
    cout << "value_extern 執行了" <<endl;
}
複製代碼

輸出:

value_extern 執行了

10. 運算符

運算符是一種告訴編譯器執行特定的數學或邏輯操做的符號。C++ 內置了豐富的運算符,並提供瞭如下類型的運算符:

  • 算術運算符
  • 關係運算符
  • 邏輯運算符
  • 位運算符
  • 賦值運算符
  • 雜項運算符

本小節將逐一介紹算術運算符、關係運算符、邏輯運算符、位運算符、賦值運算符和其餘運算符。

算術運算符:

下表顯示了 C++ 支持的算術運算符。

假設變量 A 的值爲 10,變量 B 的值爲 20,則:

運算符 描述 實例
+ 把兩個操做數相加 A + B 將獲得 30
- 從第一個操做數中減去第二個操做數 A - B 將獲得 -10
* 把兩個操做數相乘 A * B 將獲得 200
/ 分子除以分母 B / A 將獲得 2
% 取模運算符,整除後的餘數 B % A 將獲得 0
++ 自增運算符,整數值增長 1 A++ 將獲得 11
-- 自減運算符,整數值減小 1 A-- 將獲得 9
void test8() {
    //算術運算符
    int a = 21;
    int b = 10;
    int c;

    c = a + b;
    cout << "Line 1 - c 的值是 " << c << endl;
    c = a - b;
    cout << "Line 2 - c 的值是 " << c << endl;
    c = a * b;
    cout << "Line 3 - c 的值是 " << c << endl;
    c = a / b;
    cout << "Line 4 - c 的值是 " << c << endl;
    c = a % b;
    cout << "Line 5 - c 的值是 " << c << endl;

    int d = 10;   // 測試自增、自減
    c = d++; //先賦值,再自增
    cout << "Line 6 - c 的值是 " << c << " " << d << endl;

    d = 12;    // 從新賦值
    c = d--; //先賦值,再自減
    cout << "Line 7 - c 的值是 " << c << " " << d << endl;

}
複製代碼

輸出:

Line 1 - c 的值是 31 Line 2 - c 的值是 11 Line 3 - c 的值是 210 Line 4 - c 的值是 2 Line 5 - c 的值是 1 Line 6 - c 的值是 10 11 Line 7 - c 的值是 12 11

關係運算符:

下表顯示了 C++ 支持的關係運算符。

假設變量 A 的值爲 10,變量 B 的值爲 20,則:

運算符 描述 實例
== 檢查兩個操做數的值是否相等,若是相等則條件爲真。 (A == B) 不爲真。
!= 檢查兩個操做數的值是否相等,若是不相等則條件爲真。 (A != B) 爲真。
> 檢查左操做數的值是否大於右操做數的值,若是是則條件爲真。 (A > B) 不爲真。
< 檢查左操做數的值是否小於右操做數的值,若是是則條件爲真。 (A < B) 爲真。
>= 檢查左操做數的值是否大於或等於右操做數的值,若是是則條件爲真。 (A >= B) 不爲真。
<= 檢查左操做數的值是否小於或等於右操做數的值,若是是則條件爲真。 (A <= B) 爲
void test8() {   
    //關係運算符
    if (a == b) {
        cout << "Line 1 - a 等於 b" << endl;
    } else {
        cout << "Line 1 - a 不等於 b" << endl;
    }
    if (a < b) {
        cout << "Line 2 - a 小於 b" << endl;
    } else {
        cout << "Line 2 - a 不小於 b" << endl;
    }
    if (a > b) {
        cout << "Line 3 - a 大於 b" << endl;
    } else {
        cout << "Line 3 - a 不大於 b" << endl;
    }
    /* 改變 a 和 b 的值 */
    a = 5;
    b = 20;
    if (a <= b) {
        cout << "Line 4 - a 小於或等於 b" << endl;
    }
    if (b >= a) {
        cout << "Line 5 - b 大於或等於 a" << endl;
    }
}
複製代碼

輸出:

Line 1 - a 不等於 b Line 2 - a 不小於 b Line 3 - a 大於 b Line 4 - a 小於或等於 b Line 5 - b 大於或等於 a

邏輯運算符:

下表顯示了 C++ 支持的關係邏輯運算符。

假設變量 A 的值爲 1,變量 B 的值爲 0,則:

運算符 描述 實例
&& 稱爲邏輯與運算符。若是兩個操做數都非零,則條件爲真。 (A && B) 爲假。
|| 稱爲邏輯或運算符。若是兩個操做數中有任意一個非零,則條件爲真。 (A || B) 爲真。
! 稱爲邏輯非運算符。用來逆轉操做數的邏輯狀態。若是條件爲真則邏輯非運算符將使其爲假。 !(A && B) 爲真。
void test8() {
  
    //邏輯運算符

    if (a && b) {
        cout << "Line 1 - 條件爲真" << endl;
    }
    if (a || b) {
        cout << "Line 2 - 條件爲真" << endl;
    }
    /* 改變 a 和 b 的值 */
    a = 0;
    b = 10;
    if (a && b) {
        cout << "Line 3 - 條件爲真" << endl;
    } else {
        cout << "Line 4 - 條件不爲真" << endl;
    }
    if (!(a && b)) {
        cout << "Line 5 - 條件爲真" << endl;
    }  
}
複製代碼

輸出:

Line 1 - 條件爲真 Line 2 - 條件爲真 Line 4 - 條件不爲真 Line 5 - 條件爲真

位運算符:

位運算符做用於位,並逐位執行操做。&、 | 和 ^ 的真值表以下所示:

p q p & q p | q p ^ q
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1

假設若是 A = 60,且 B = 13,如今以二進制格式表示,它們以下所示:

A = 0011 1100

B = 0000 1101

-----------------

A&B = 0000 1100

A|B = 0011 1101

A^B = 0011 0001

~A = 1100 0011

下表顯示了 C++ 支持的位運算符。假設變量 A 的值爲 60,變量 B 的值爲 13,則:

運算符 描述 實例
& 若是同時存在於兩個操做數中,二進制 AND 運算符複製一位到結果中。 (A & B) 將獲得 12,即爲 0000 1100
| 若是存在於任一操做數中,二進制 OR 運算符複製一位到結果中。 (A | B) 將獲得 61,即爲 0011 1101
^ 若是存在於其中一個操做數中但不一樣時存在於兩個操做數中,二進制異或運算符複製一位到結果中。 (A ^ B) 將獲得 49,即爲 0011 0001
~ 二進制補碼運算符是一元運算符,具備"翻轉"位效果,即0變成1,1變成0。 (~A ) 將獲得 -61,即爲 1100 0011,一個有符號二進制數的補碼形式。
<< 二進制左移運算符。左操做數的值向左移動右操做數指定的位數。 A << 2 將獲得 240,即爲 1111 0000
>> 二進制右移運算符。左操做數的值向右移動右操做數指定的位數。 A >> 2 將獲得 15,即爲 0000 1111
void test8() {

    println("\n\n\n", "位運算符");
    //位運算符
    unsigned int aW = 60;      // 60 = 0011 1100
    unsigned int bW = 13;      // 13 = 0000 1101
    int cW = 0;

    cW = aW & bW;             // 12 = 0000 1100
    cout << "Line 1 - cW 的值是 " << cW << endl;

    cW = aW | bW;             // 61 = 0011 1101
    cout << "Line 2 - cW 的值是 " << cW << endl;

    cW = aW ^ bW;             // 49 = 0011 0001
    cout << "Line 3 - cW 的值是 " << cW << endl;

    cW = ~aW;                // -61 = 1100 0011
    cout << "Line 4 - cW 的值是 " << cW << endl;

    cW = aW << 2;            // 240 = 1111 0000
    cout << "Line 5 - cW 的值是 " << cW << endl;

    cW = aW >> 2;            // 15 = 0000 1111
    cout << "Line 6 - cW 的值是 " << cW << endl;
}
複製代碼

輸出:

Line 1 - cW 的值是 12 Line 2 - cW 的值是 61 Line 3 - cW 的值是 49 Line 4 - cW 的值是 -61 Line 5 - cW 的值是 240 Line 6 - cW 的值是 15

賦值運算符:

運算符 描述 實例
= 簡單的賦值運算符,把右邊操做數的值賦給左邊操做數 C = A + B 將把 A + B 的值賦給 C
+= 加且賦值運算符,把右邊操做數加上左邊操做數的結果賦值給左邊操做數 C += A 至關於 C = C + A
-= 減且賦值運算符,把左邊操做數減去右邊操做數的結果賦值給左邊操做數 C -= A 至關於 C = C - A
*= 乘且賦值運算符,把右邊操做數乘以左邊操做數的結果賦值給左邊操做數 C *= A 至關於 C = C * A
/= 除且賦值運算符,把左邊操做數除以右邊操做數的結果賦值給左邊操做數 C /= A 至關於 C = C / A
%= 求模且賦值運算符,求兩個操做數的模賦值給左邊操做數 C %= A 至關於 C = C % A
<<= 左移且賦值運算符 C <<= 2 等同於 C = C << 2
>>= 右移且賦值運算符 C >>= 2 等同於 C = C >> 2
&= 按位與且賦值運算符 C &= 2 等同於 C = C & 2
^= 按位異或且賦值運算符 C ^= 2 等同於 C = C ^ 2
|= 按位或且賦值運算符 C |= 2 等同於 C = C | 2
void test8() {
    println("\n\n\n", "賦值運算符");
    a = 50;
    //賦值運算符
    c = a;
    cout << "Line 1 - = 運算符實例,c 的值 = : " << c << endl;

    c += a;
    cout << "Line 2 - += 運算符實例,c 的值 = : " << c << endl;

    c -= a;
    cout << "Line 3 - -= 運算符實例,c 的值 = : " << c << endl;

    c *= a;
    cout << "Line 4 - *= 運算符實例,c 的值 = : " << c << endl;

    c /= a;
    cout << "Line 5 - /= 運算符實例,c 的值 = : " << c << endl;

    c = 200;
    c %= a;
    cout << "Line 6 - %= 運算符實例,c 的值 = : " << c << endl;

    c <<= 2;
    cout << "Line 7 - <<= 運算符實例,c 的值 = : " << c << endl;

    c >>= 2;
    cout << "Line 8 - >>= 運算符實例,c 的值 = : " << c << endl;

    c &= 2;
    cout << "Line 9 - &= 運算符實例,c 的值 = : " << c << endl;

    c ^= 2;
    cout << "Line 10 - ^= 運算符實例,c 的值 = : " << c << endl;

    c |= 2;
    cout << "Line 11 - |= 運算符實例,c 的值 = : " << c << endl;
}
複製代碼

輸出:

Line 1 - = 運算符實例,c 的值 = : 50 Line 2 - += 運算符實例,c 的值 = : 100 Line 3 - -= 運算符實例,c 的值 = : 50 Line 4 - *= 運算符實例,c 的值 = : 2500 Line 5 - /= 運算符實例,c 的值 = : 50 Line 6 - %= 運算符實例,c 的值 = : 0 Line 7 - <<= 運算符實例,c 的值 = : 0 Line 8 - >>= 運算符實例,c 的值 = : 0 Line 9 - &= 運算符實例,c 的值 = : 0 Line 10 - ^= 運算符實例,c 的值 = : 2 Line 11 - |= 運算符實例,c 的值 = : 2

其它:

運算符 描述
sizeof sizeof 運算符返回變量的大小。例如,sizeof(a) 將返回 4,其中 a 是整數。
Condition ? X : Y 條件運算符。若是 Condition 爲真 ? 則值爲 X : 不然值爲 Y。
, 逗號運算符會順序執行一系列運算。整個逗號表達式的值是以逗號分隔的列表中的最後一個表達式的值。
.(點)和 ->(箭頭) 成員運算符用於引用類、結構和共用體的成員。
Cast 強制轉換運算符把一種數據類型轉換爲另外一種數據類型。例如,int(2.2000) 將返回 2。
& 指針運算符 & 返回變量的地址。例如 &a; 將給出變量的實際地址。
* 指針運算符 * 指向一個變量。例如,*var; 將指向變量 var。

11. 循環

C++ 編程語言提供瞭如下幾種循環類型。點擊連接查看每一個類型的細節。

循環類型 描述
while 循環 當給定條件爲真時,重複語句或語句組。它會在執行循環主體以前測試條件。
for 循環 屢次執行一個語句序列,簡化管理循環變量的代碼。
do...while 循環 除了它是在循環主體結尾測試條件外,其餘與 while 語句相似。
嵌套循環 您能夠在 while、for 或 do..while 循環內使用一個或多個循環。

循環控制語句:

循環控制語句更改執行的正常序列。當執行離開一個範圍時,全部在該範圍中建立的自動對象都會被銷燬。

C++ 提供了下列的控制語句。點擊連接查看每一個語句的細節。

控制語句 描述
break 語句 終止 loopswitch 語句,程序流將繼續執行緊接着 loop 或 switch 的下一條語句。
continue 語句 引發循環跳過主體的剩餘部分,當即從新開始測試條件。
goto 語句 將控制轉移到被標記的語句。可是不建議在程序中使用 goto 語句。

死循環:

若是條件永遠不爲假,則循環將變成無限循環。for 循環在傳統意義上可用於實現無限循環。因爲構成循環的三個表達式中任何一個都不是必需的,您能夠將某些條件表達式留空來構成一個無限循環。

#include <iostream>
using namespace std;
 
int main () {
 	//也可使用 while (true){}
   for( ; ; )
   {
      printf("This loop will run forever.\n");
   }
 
   return 0;
}
複製代碼

當條件表達式不存在時,它被假設爲真。您也能夠設置一個初始值和增量表達式,可是通常狀況下,C++ 程序員偏向於使用 for(;;) 結構來表示一個無限循環。

**注意:**您能夠按 Ctrl + C 鍵終止一個無限循環。

例子:

void test9() {

    //for 循環
    for (int i = 0; i < 10; ++i) {
        if (i % 2 == 1) {
            cout << i << "==" << i % 2 << endl;
            //跳出當前循環,繼續下一次操做
            continue;
        }
        if (i == 8) {
            cout << "跳出循環" << endl;
            break;
        }
        cout << "遍歷中..." << "==" << i << endl;
    }

    for (int j = 0; j < 3; ++j) {
        if (j == 1) {
            cout << "j 跳出循環" << endl;
            return;
        }
        cout << "j 就遍歷中..." << "==" << j << endl;
    }


};
複製代碼

輸出:

遍歷中...==0 1==1 遍歷中...==2 3==1 遍歷中...==4 5==1 遍歷中...==6 7==1 跳出循環 j 就遍歷中...==0 j 跳出循環

12. 判斷

判斷語句:

C++ 編程語言提供瞭如下類型的判斷語句。點擊連接查看每一個語句的細節。

語句 描述
if 語句 一個 if 語句 由一個布爾表達式後跟一個或多個語句組成。
if...else 語句 一個 if 語句 後可跟一個可選的 else 語句,else 語句在布爾表達式爲假時執行。
嵌套 if 語句 您能夠在一個 ifelse if 語句內使用另外一個 ifelse if 語句。
switch 語句 一個 switch 語句容許測試一個變量等於多個值時的狀況。
嵌套 switch 語句 您能夠在一個 switch 語句內使用另外一個 switch 語句。

三元運算符:

咱們已經在前面的章節中講解了 條件運算符 ? :,能夠用來替代 if...else 語句。它的通常形式以下:

void test10() {
    int i = 10;
		//格式:Exp1 ? Exp2 : Exp3;
  	//其中,Exp一、Exp2 和 Exp3 是表達式。請注意,冒號的使用和位置。
		//? 表達式的值是由 Exp1 決定的。若是 Exp1 爲真,則計算 Exp2 的值,結果即爲整個 ? 表達式的 //值。若是 Exp1 爲假,則計算 Exp3 的值,結果即爲整個 ? 表達式的值。
    i = i == 10 ? i = 9 : i;
    cout << i << endl;
}
複製代碼

輸出:

9

13. 函數

函數是一組一塊兒執行一個任務的語句。每一個 C++ 程序都至少有一個函數,即主函數 main() ,全部簡單的程序均可以定義其餘額外的函數。

定義函數:

return_type function_name( parameter list ) {
   body of the function
}
複製代碼

在 C++ 中,函數由一個函數頭和一個函數主體組成。下面列出一個函數的全部組成部分:

  • **返回類型:**一個函數能夠返回一個值。return_type 是函數返回的值的數據類型。有些函數執行所需的操做而不返回值,在這種狀況下,return_type 是關鍵字 void
  • **函數名稱:**這是函數的實際名稱。函數名和參數列表一塊兒構成了函數簽名。
  • **參數:**參數就像是佔位符。當函數被調用時,您向參數傳遞一個值,這個值被稱爲實際參數。參數列表包括函數參數的類型、順序、數量。參數是可選的,也就是說,函數可能不包含參數。
  • **函數主體:**函數主體包含一組定義函數執行任務的語句。

例子:

// 函數返回兩個數中較大的那個數
int max(int num1, int num2) {
   // 局部變量聲明
   int result;
 
   if (num1 > num2)
      result = num1;
   else
      result = num2;
 
   return result; 
}
複製代碼

函數聲明:

//格式: return_type function_name( parameter list );
//例子:
int max(int num1, int num2);
int max(int, int);
複製代碼

當您在一個源文件中定義函數且在另外一個文件中調用函數時,函數聲明是必需的。在這種狀況下,您應該在調用函數的文件頂部聲明函數。

調用函數:

建立 C++ 函數時,會定義函數作什麼,而後經過調用函數來完成已定義的任務。

當程序調用函數時,程序控制權會轉移給被調用的函數。被調用的函數執行已定義的任務,當函數的返回語句被執行時,或到達函數的結束括號時,會把程序控制權交還給主程序。

調用函數時,傳遞所需參數,若是函數返回一個值,則能夠存儲返回值。例如:

#include <iostream>
using namespace std;
 
// 函數聲明
int max(int num1, int num2);
 
int main () {
   // 局部變量聲明
   int a = 100;
   int b = 200;
   int ret;
 
   // 調用函數來獲取最大值
   ret = max(a, b);
 
   cout << "Max value is : " << ret << endl;
 
   return 0;
}
 
// 函數返回兩個數中較大的那個數
int max(int num1, int num2) {
   // 局部變量聲明
   int result;
 
   if (num1 > num2)
      result = num1;
   else
      result = num2;
 
   return result; 
}
複製代碼

輸出:

Max value is : 200

函數參數:

若是函數要使用參數,則必須聲明接受參數值的變量。這些變量稱爲函數的形式參數

形式參數就像函數內的其餘局部變量,在進入函數時被建立,退出函數時被銷燬。

當調用函數時,有三種向函數傳遞參數的方式:

調用類型 描述
傳值調用 該方法把參數的實際值複製給函數的形式參數。在這種狀況下,修改函數內的形式參數對實際參數沒有影響。
指針調用 該方法把參數的地址複製給形式參數。在函數內,該地址用於訪問調用中要用到的實際參數。這意味着,修改形式參數會影響實際參數。
引用調用 該方法把參數的引用複製給形式參數。在函數內,該引用用於訪問調用中要用到的實際參數。這意味着,修改形式參數會影響實際參數。

默認狀況下,C++ 使用傳值調用來傳遞參數。通常來講,這意味着函數內的代碼不能改變用於調用函數的參數。以前提到的實例,調用 max() 函數時,使用了相同的方法。

參數的默認值:

當您定義一個函數,您能夠爲參數列表中後邊的每個參數指定默認值。當調用函數時,若是實際參數的值留空,則使用這個默認值。

這是經過在函數定義中使用賦值運算符來爲參數賦值的。調用函數時,若是未傳遞參數的值,則會使用默認值,若是指定了值,則會忽略默認值,使用傳遞的值。請看下面的實例:

#include <iostream>
using namespace std;
 
int sum(int a, int b=20) {
  int result;
 
  result = a + b;
  
  return (result);
}
 
int main () {
   // 局部變量聲明
   int a = 100;
   int b = 200;
   int result;
 
   // 調用函數來添加值
   result = sum(a, b);
   cout << "Total value is :" << result << endl;
 
   // 再次調用函數
   result = sum(a);
   cout << "Total value is :" << result << endl;
 
   return 0;
}
複製代碼

輸出:

Total value is :300 Total value is :120

14. 數學運算

在 C++ 中,除了能夠建立各類函數,還包含了各類有用的函數供您使用。這些函數寫在標準 C 和 C++ 庫中,叫作內置函數。您能夠在程序中引用這些函數。

C++ 內置了豐富的數學函數,可對各類數字進行運算。下表列出了 C++ 中一些有用的內置的數學函數。

爲了利用這些函數,您須要引用數學頭文件

序號 函數 & 描述
1 double cos(double); 該函數返回弧度角(double 型)的餘弦。
2 double sin(double); 該函數返回弧度角(double 型)的正弦。
3 double tan(double); 該函數返回弧度角(double 型)的正切。
4 double log(double); 該函數返回參數的天然對數。
5 double pow(double, double); 假設第一個參數爲 x,第二個參數爲 y,則該函數返回 x 的 y 次方。
6 double hypot(double, double); 該函數返回兩個參數的平方總和的平方根,也就是說,參數爲一個直角三角形的兩個直角邊,函數會返回斜邊的長度。
7 double sqrt(double); 該函數返回參數的平方根。
8 int abs(int); 該函數返回整數的絕對值。
9 double fabs(double); 該函數返回任意一個浮點數的絕對值。
10 double floor(double); 該函數返回一個小於或等於傳入參數的最大整數。

下面是一個關於數學運算的簡單實例:

void test11() {
    //數字定義
    short s = 10;
    int i = -1000;
    long l = 100000;
    float f = 250.41;
    double d = 200.45;

    //數學運算
    cout << "sin(d) :" << sin(d) << endl;
    cout << "abs(i) :" << abs(i) << endl;
    cout << "floor(d) :" << floor(d) << endl;
    cout << "sqrt(f) :" << sqrt(f) << endl;
    cout << "pow( d, 2) :" << pow(d, 2) << endl;

    //生成隨機數以前必須先調用 srand() 函數。
    int a;
    srand((unsigned) time(NULL));
    //生成 5 個隨機數
    for (int j = 0; j < 5; ++j) {
        a = rand();
        cout << "隨機數:" << a << endl;
    }
}
複製代碼

輸出:

sin(d) :-0.574448 abs(i) :1000 floor(d) :200 sqrt(f) :15.8243 pow( d, 2) :40180.2 隨機數:1753472662 隨機數:696942453 隨機數:1135996833 隨機數:1569150401 隨機數:1611604447

15. 數組

C++ 支持數組數據結構,它能夠存儲一個固定大小的相同類型元素的順序集合。

聲明數組:

在 C++ 中要聲明一個數組,須要指定元素的類型和元素的數量,以下所示:

//格式:
type arrayName [ arraySize ];//這叫作一維數組。arraySize 必須是一個大於零的整數常量,type 能夠是任意有效的 C++ 數據類型。
//例子:
double balance[10];
複製代碼

初始化數組:

在 C++ 中,您能夠逐個初始化數組,也可使用一個初始化語句,以下所示:

double d[5] = {1.0, 2.0, 3.0, 4.0, 5.0};
//修改數組
d[4] = 50.0;
複製代碼

上面的意思是先定義一個大小爲 5 的數組,而後在修改索引爲 4 的數值,如下是上面的圖形表示:

訪問數組元素:

數組元素能夠經過數組名稱加索引進行訪問。元素的索引是放在方括號內,跟在數組名稱的後邊。例如:

double temp = d[4];
複製代碼

上面的語句將把數組中第 4 個元素的值賦給 temp 變量。下面的實例使用了上述的三個概念,即,聲明數組、數組賦值、訪問數組:

void test12() {
    int n[100]; //包含 100 個整數
    for (int i = 0; i < 100; ++i) {
        n[i] = i;
    }
    //setw 能夠理解爲 輸出的間隔爲 10 個字符
    cout << "Element" << setw(10) << "value" << setw(10) << n[20] << endl;

    // 輸出數組中每一個元素的值
    for (int j = 0; j < 100; j++) {
        cout << "Element" << setw(10) << "value" << setw(10) << n[j] << endl;
    }
}
複製代碼

輸出:

Element value 20 Element value 0 Element value 1 Element value 2 Element value 3 Element value 4 Element value 5 Element value 6 Element value 7 Element value 8 Element value 9 Element value 10 Element value 11 Element value 12

...

其它:

在 C++ 中,數組是很是重要的,咱們須要瞭解更多有關數組的細節。下面列出了 C++ 程序員必須清楚的一些與數組相關的重要概念:

概念 描述
多維數組 C++ 支持多維數組。多維數組最簡單的形式是二維數組。
指向數組的指針 您能夠經過指定不帶索引的數組名稱來生成一個指向數組中第一個元素的指針。
傳遞數組給函數 您能夠經過指定不帶索引的數組名稱來給函數傳遞一個指向數組的指針。
從函數返回數組 C++ 容許從函數返回數組。

16. 字符串

C++ 提供瞭如下兩種類型的字符串表示形式:

  • C 風格字符串
  • C++ 引入的 string 類類型

C 風格字符串:

在上一篇文章中咱們學習了 C 中定義字符串,這裏咱們在複習下,看下面示例:

char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
//等同於以下定義
char greeting[] = "Hello";
複製代碼

如下是 C/C++ 中定義的字符串的內存表示:

其實,您不須要把 null 字符放在字符串常量的末尾。C++ 編譯器會在初始化數組時,自動把 '\0' 放在字符串的末尾。讓咱們嘗試輸出上面的字符串:

其實,您不須要把 null 字符放在字符串常量的末尾。C++ 編譯器會在初始化數組時,自動把 '\0' 放在字符串的末尾。

例子:

void test13() {
    char message[10] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '\0'};
    cout << message << endl;
}
複製代碼

輸出:

123456789

C++ 中有大量的函數用來操做以 null 結尾的字符串:supports a wide range of functions that manipulate null-terminated strings:

序號 函數 & 目的
1 strcpy(s1, s2); 複製字符串 s2 到字符串 s1。
2 strcat(s1, s2); 鏈接字符串 s2 到字符串 s1 的末尾。
3 strlen(s1); 返回字符串 s1 的長度。
4 strcmp(s1, s2); 若是 s1 和 s2 是相同的,則返回 0;若是 s1<s2 則返回值小於 0;若是 s1>s2 則返回值大於 0。
5 strchr(s1, ch); 返回一個指針,指向字符串 s1 中字符 ch 的第一次出現的位置。
6 strstr(s1, s2); 返回一個指針,指向字符串 s1 中字符串 s2 的第一次出現的位置。

下面的實例使用了上述的一些函數:

void test13() {
    char ctr1[20] = "網名:";
    char ctr2[50] = "我的Blog地址:";

    int ctr1Len, ctr2Len;

    strcat(ctr1, "DevYK");
    strcat(ctr2, "https://www.devyk.top");
    cout << "ctr1\t" << ctr1 << "\n" << "ctr2\t" << ctr2 << endl;
    ctr1Len = strlen(ctr1);
    ctr2Len = strlen(ctr2);
    cout << "ctr1Len" << setw(10) << ctr1Len << "\nctr2Len" << setw(10) << ctr2Len << endl;

}
複製代碼

輸出:

ctr1 網名:DevYK ctr2 我的Blog地址:www.devyk.top ctr1Len 12 ctr2Len 38

C++ 中的 String:

C++ 標準庫提供了 string 類類型,支持上述全部的操做,另外還增長了其餘更多的功能。咱們將學習 C++ 標準庫中的這個類,如今讓咱們先來看看下面這個實例:

如今您可能還沒法透徹地理解這個實例,由於到目前爲止咱們尚未討論類和對象。因此如今您能夠只是粗略地看下這個實例,等理解了面向對象的概念以後再回頭來理解這個實例。

#include <iostream>
#include <string>
int main(){
  test13();
  return 0;
}
void test13() {
    printf("\n\n\nstring 類型字符串");

    string meg[10] = {"1", "2", "3", "4", "5", "6", "7", "8", "9"};
    cout << meg << endl;
    //定義 string 變量
    string str1add, str2add;
    int str1Len, str2Len;

    //init
    string str1 = "網名:";
    string str2 = "我的Blog地址:";

    //追加到 str 末尾
    str1add = str1.append("DevYK");
    str2add = str2.append("https://www.devyk.top");

    cout << "str1\t" << str1 << "\n" << "str2\t" << str2 << endl;
    str1Len = str1add.length();
    str2Len = str2add.size();
    cout << "str1Len" << setw(10) << str1Len << "\nstr2Len" << setw(10) << str2Len << endl;


}
複製代碼

輸出:

string 類型字符串0x7ffee9b98550 str1 網名:DevYK str2 我的Blog地址:www.devyk.top str1Len 12 str2Len 38

17. 指針

學習 C++ 的指針既簡單又有趣。經過指針,能夠簡化一些 C++ 編程任務的執行,還有一些任務,如動態內存分配,沒有指針是沒法執行的。因此,想要成爲一名優秀的 C++ 程序員,學習指針是頗有必要的。

正如您所知道的,每個變量都有一個內存位置,每個內存位置都定義了可以使用連字號(&)運算符訪問的地址,它表示了在內存中的一個地址。請看下面的實例,它將輸出定義的變量地址:

void test14() {
    int var1;
    char var2[10];
    cout << "var1 變量的地址\t" << &var1 << "\nvar2 變量的地址\t" << &var2 << endl;
}
複製代碼

輸出:

var1 變量的地址 0x7ffeeef58698 var2 變量的地址 0x7ffeeef5869e

經過上面的實例,咱們瞭解了什麼是內存地址以及如何訪問它。接下來讓咱們看看什麼是指針。

指針是什麼?

指針是一個變量,其值爲另外一個變量的地址,即,內存位置的直接地址。就像其餘變量或常量同樣,您必須在使用指針存儲其餘變量地址以前,對其進行聲明。指針變量聲明的通常形式爲:

type *var-name;

複製代碼

在這裏,type 是指針的基類型,它必須是一個有效的 C++ 數據類型,var-name 是指針變量的名稱。用來聲明指針的星號 * 與乘法中使用的星號是相同的。可是,在這個語句中,星號是用來指定一個變量是指針。如下是有效的指針聲明:

int    *ip;    /* 一個整型的指針 */
double *dp;    /* 一個 double 型的指針 */
float  *fp;    /* 一個浮點型的指針 */
char   *ch;    /* 一個字符型的指針 */
複製代碼

全部指針的值的實際數據類型,無論是整型、浮點型、字符型,仍是其餘的數據類型,都是同樣的,都是一個表明內存地址的長的十六進制數。不一樣數據類型的指針之間惟一的不一樣是,指針所指向的變量或常量的數據類型不一樣。

指針的使用:

使用指針時會頻繁進行如下幾個操做:定義一個指針變量、把變量地址賦值給指針、訪問指針變量中可用地址的值。這些是經過使用一元運算符 ***** 來返回位於操做數所指定地址的變量的值。下面的實例涉及到了這些操做:

void test14() {
    int var = 20; //實際變量的聲明
    int *ip;
    //將 var 地址賦值給 ip 指針變量中
    ip = &var;

    cout << "指針變量中存儲的地址\t" << ip << endl;
    cout << "指針變量中的地址\t" << ip << endl;
    cout << "指針變量中地址對應的值\t" << *ip << endl;
}
複製代碼

輸出:

指針變量中存儲的地址 0x7ffeeef58694 指針變量中的地址 0x7ffeeef58694 指針變量中地址對應的值 20

18. 引用

引用變量是一個別名,也就是說,它是某個已存在變量的另外一個名字。一旦把引用初始化爲某個變量,就可使用該引用名稱或變量名稱來指向變量。

引用 VS 指針:

引用很容易與指針混淆,它們之間有三個主要的不一樣:

  • 不存在空引用。引用必須鏈接到一塊合法的內存。
  • 一旦引用被初始化爲一個對象,就不能被指向到另外一個對象。指針能夠在任什麼時候候指向到另外一個對象。
  • 引用必須在建立時被初始化。指針能夠在任什麼時候間被初始化。

建立引用:

int i = 17;
複製代碼

咱們能夠爲 i 聲明引用變量,以下所示:

int&  r = i;
double& s = d;
複製代碼

在這些聲明中,& 讀做引用。所以,第一個聲明能夠讀做 "r 是一個初始化爲 i 的整型引用",第二個聲明能夠讀做 "s 是一個初始化爲 d 的 double 型引用"。下面的實例使用了 int 和 double 引用:

void test15() {
    //聲明變量
    int a = 10, b = 20;
    //聲明引用變量
    int &i = a;
    int &j = b;

    cout << "a == " << a << "\t &i ==" << i << endl;

    cout << "b == " << b << "\t &j ==" << j << endl;

    a = 5, b = 6;
    cout << "a == " << a << "\t &i ==" << i << endl;

    cout << "b == " << b << "\t &j ==" << j << endl;

}
複製代碼

輸出:

a == 10 &i ==10 b == 20 &j ==20 a == 5 &i ==5 b == 6 &j ==6

引用一般用於函數參數列表和函數返回值。下面列出了 C++ 程序員必須清楚的兩個與 C++ 引用相關的重要概念:

概念 描述
把引用做爲參數 C++ 支持把引用做爲參數傳給函數,這比傳通常的參數更安全。
把引用做爲返回值 能夠從 C++ 函數中返回引用,就像返回其餘數據類型同樣。

19. 日期 & 時間

C++ 標準庫沒有提供所謂的日期類型。C++ 繼承了 C 語言用於日期和時間操做的結構和函數。爲了使用日期和時間相關的函數和結構,須要在 C++ 程序中引用 頭文件。

有四個與時間相關的類型:clock_t、time_t、size_ttm。類型 clock_t、size_t 和 time_t 可以把系統時間和日期表示爲某種整數。

結構類型 tm 把日期和時間以 C 結構的形式保存,tm 結構的定義以下:

struct tm {
  int tm_sec;   // 秒,正常範圍從 0 到 59,但容許至 61
  int tm_min;   // 分,範圍從 0 到 59
  int tm_hour;  // 小時,範圍從 0 到 23
  int tm_mday;  // 一月中的第幾天,範圍從 1 到 31
  int tm_mon;   // 月,範圍從 0 到 11
  int tm_year;  // 自 1900 年起的年數
  int tm_wday;  // 一週中的第幾天,範圍從 0 到 6,從星期日算起
  int tm_yday;  // 一年中的第幾天,範圍從 0 到 365,從 1 月 1 日算起
  int tm_isdst; // 夏令時
}
複製代碼

下面是 C/C++ 中關於日期和時間的重要函數。全部這些函數都是 C/C++ 標準庫的組成部分,您能夠在 C++ 標準庫中查看一下各個函數的細節。

序號 函數 & 描述
1 time_t time(time_t *time); 該函數返回系統的當前日曆時間,自 1970 年 1 月 1 日以來通過的秒數。若是系統沒有時間,則返回 .1。
2 char *ctime(const time_t *time); 該返回一個表示當地時間的字符串指針,字符串形式 day month year hours:minutes:seconds year\n\0
3 struct tm *localtime(const time_t *time); 該函數返回一個指向表示本地時間的 tm 結構的指針。
4 clock_t clock(void); 該函數返回程序執行起(通常爲程序的開頭),處理器時鐘所使用的時間。若是時間不可用,則返回 .1。
5 char * asctime ( const struct tm * time ); 該函數返回一個指向字符串的指針,字符串包含了 time 所指向結構中存儲的信息,返回形式爲:day month date hours:minutes:seconds year\n\0。
6 struct tm *gmtime(const time_t *time); 該函數返回一個指向 time 的指針,time 爲 tm 結構,用協調世界時(UTC)也被稱爲格林尼治標準時間(GMT)表示。
7 time_t mktime(struct tm *time); 該函數返回日曆時間,至關於 time 所指向結構中存儲的時間。
8 double difftime ( time_t time2, time_t time1 ); 該函數返回 time1 和 time2 之間相差的秒數。
9 size_t strftime(); 該函數可用於格式化日期和時間爲指定的格式。

當前日期:

void test16() {
    //獲取系統的時間
    time_t now = time(0);
    //把 now 轉爲字符串
    char *curTime = ctime(&now);

    //把 now 轉爲 tm 結構
    tm *gmtm = gmtime(&now);
    cout << "當前時間\t" << curTime << endl;

    curTime = asctime(gmtm);

    cout << "UTC 日期和時間\t" << curTime << endl;

}
複製代碼

輸出:

當前時間 Sun Jan 5 22:11:15 2020

UTC 日期和時間 Sun Jan 5 14:11:15 2020

使用結構 tm 格式化時間:

tm 結構在 C/C++ 中處理日期和時間相關的操做時,顯得尤其重要。tm 結構以 C 結構的形式保存日期和時間。大多數與時間相關的函數都使用了 tm 結構。下面的實例使用了 tm 結構和各類與日期和時間相關的函數。

在練習使用結構以前,須要對 C 結構有基本的瞭解,並懂得如何使用箭頭 -> 運算符來訪問結構成員。

void test16() {
    //基於當前系統的日期/時間
    cout << "1970 到目前通過的秒數:" << now << endl;

    tm *ltm = localtime(&now);
    //輸出 tm 結構的各個組成部分
    cout << "年: " << 1990 + ltm->tm_year << endl;
    cout << "月: " << 1 + ltm->tm_mon << endl;
    cout << "日: " << ltm->tm_mday << endl;
    cout << "時: " << ltm->tm_hour << endl;
    cout << "分: " << ltm->tm_min << endl;
    cout << "秒: " << ltm->tm_sec << endl;
}

複製代碼

輸出:

1970 到目前通過的秒數:1578233475 年: 2110 月: 1 日: 5 時: 22 分: 11 秒: 15

20. 輸入輸出

C++ 標準庫提供了一組豐富的輸入/輸出功能,咱們將在後續的章節進行介紹。本章將討論 C++ 編程中最基本和最多見的 I/O 操做。

C++ 的 I/O 發生在流中,流是字節序列。若是字節流是從設備(如鍵盤、磁盤驅動器、網絡鏈接等)流向內存,這叫作輸入操做。若是字節流是從內存流向設備(如顯示屏、打印機、磁盤驅動器、網絡鏈接等),這叫作輸出操做

I/O 庫頭文件

下列的頭文件在 C++ 編程中很重要。

頭文件 函數和描述
該文件定義了 cin、cout、cerrclog 對象,分別對應於標準輸入流、標準輸出流、非緩衝標準錯誤流和緩衝標準錯誤流。
該文件經過所謂的參數化的流操縱器(好比 setwsetprecision),來聲明對執行標準化 I/O 有用的服務。
該文件爲用戶控制的文件處理聲明服務。咱們將在文件和流的相關章節討論它的細節。

標準輸入流 cout:

預約義的對象 coutiostream 類的一個實例。cout 對象"鏈接"到標準輸出設備,一般是顯示屏。cout 是與流插入運算符 << 結合使用的。

標準輸入流 cin:

預約義的對象 ciniostream 類的一個實例。cin 對象附屬到標準輸入設備,一般是鍵盤。cin 是與流提取運算符 >> 結合使用的。

標準錯誤流 cerr:

預約義的對象 cerriostream 類的一個實例。cerr 對象附屬到標準錯誤設備,一般也是顯示屏,可是 cerr 對象是非緩衝的,且每一個流插入到 cerr 都會當即輸出。

cerr 也是與流插入運算符 << 結合使用的。

標準日誌流 clog:

預約義的對象 clogiostream 類的一個實例。clog 對象附屬到標準錯誤設備,一般也是顯示屏,可是 clog 對象是緩衝的。這意味着每一個流插入到 clog 都會先存儲在緩衝在,直到緩衝填滿或者緩衝區刷新時纔會輸出。

clog 也是與流插入運算符 << 結合使用的。

例子:

void test17() {
    //1. 標準輸出流 cout
    char str[] = "Hello World";
    cout << "值爲:" << str << endl;
    //2. 標準輸入流 cin
    char name[10];
    cout << "請輸入你的名稱: ";
    cin >> name;
    cout << "你的名稱是:" << name << endl;
// 請輸入你的名稱: DevYK
// 你的名稱是:DevYK

    //3. 標準錯誤流 cerr
    char errStr[] = "Read Error";
    cerr << "error message:" << errStr << endl; //輸出爲報紅的:error message:Read Error

    //4. 標準日誌流 clog
    char logStr[] = "out Log";
    clog << "Debug Log:" << logStr << endl; // 輸出爲報紅的 Debug Logout Log
}
複製代碼

輸出:

21. 數據結構

C/C++ 數組容許定義可存儲相同類型數據項的變量,可是結構是 C++ 中另外一種用戶自定義的可用的數據類型,它容許您存儲不一樣類型的數據項。

定義結構:

爲了定義結構,您必須使用 struct 語句。struct 語句定義了一個包含多個成員的新的數據類型,struct 語句的格式以下:

struct type_name {
member_type1 member_name1;
member_type2 member_name2;
member_type3 member_name3;
.
.
} object_names;
複製代碼

type_name 是結構體類型的名稱,member_type1 member_name1 是標準的變量定義,好比 int i; 或者 float f; 或者其餘有效的變量定義。在結構定義的末尾,最後一個分號以前,您能夠指定一個或多個結構變量,這是可選的。下面是聲明一個結構體類型 Movies,變量爲 movies

//定義結構
struct Movies {
    char title[10];
    char address[30];
}movies;
複製代碼

訪問結構成員:

爲了訪問結構的成員,咱們使用成員訪問運算符(.)。成員訪問運算符是結構變量名稱和咱們要訪問的結構成員之間的一個句號。

下面的實例演示告終構的用法:

//定義結構
struct Movies {
    char title[10];
    char address[30];
};

void test18() {
    //定義結構體變量
    Movies movieA, movieB;

    //movieA 詳述
    strcpy(movieA.title, "葉問4");
    strcpy(movieA.address, "china");

    //movieB 詳述
    strcpy(movieB.title, "戰狼2");
    strcpy(movieB.address, "china");

    //輸出電影信息
    cout << movieA.title << "\t" << movieA.address << endl;
    cout << movieB.title << "\t" << movieB.address << endl;

}

複製代碼

輸出:

葉問4 china 戰狼2 china

結構體做爲函數參數:

您能夠把結構做爲函數參數,傳參方式與其餘類型的變量或指針相似。您可使用上面實例中的方式來訪問結構變量:

//定義結構
struct Movies {
    char title[10];
    char address[30];
};

void movieInfo(struct Movies movie);

void test18() {
    //定義結構體變量
    Movies movieA, movieB;

    //movieA 詳述
    strcpy(movieA.title, "葉問4");
    strcpy(movieA.address, "china");

    //movieB 詳述
    strcpy(movieB.title, "戰狼2");
    strcpy(movieB.address, "china");

    //將結構體做爲函數參數傳遞
    movieInfo(movieA);
    movieInfo(movieB);


}

void movieInfo(struct Movies movie) {
    //輸出電影信息
    cout << movie.title << "\t" << movie.address << endl;
}

複製代碼

輸出:

葉問4 china 戰狼2 china

指針結構的指針:

您能夠定義指向結構的指針,方式與定義指向其餘類型變量的指針類似,以下所示:

struct Movies *struct_pointer;
複製代碼

如今,您能夠在上述定義的指針變量中存儲結構變量的地址。爲了查找結構變量的地址,請把 & 運算符放在結構名稱的前面,以下所示:

struct_pointer = &Movies;
複製代碼

爲了使用指向該結構的指針訪問結構的成員,您必須使用 -> 運算符,以下所示:

struct_pointer->title;
複製代碼

讓咱們使用結構指針來重寫上面的實例,這將有助於您理解結構指針的概念:

//定義結構
struct Movies {
    char title[10];
    char address[30];
}movies;


void movieInfo(struct Movies *movie);

void test18() {
    //定義結構體變量
    Movies movieA, movieB;

    //movieA 詳述
    strcpy(movieA.title, "葉問4");
    strcpy(movieA.address, "china");

    //movieB 詳述
    strcpy(movieB.title, "戰狼2");
    strcpy(movieB.address, "china");


    //將結構體做爲指針傳遞
    movieInfo(&movieA);
    movieInfo(&movieB);

}

void movieInfo(struct Movies *movie) {
    //輸出電影信息
    // 爲了使用指向該結構的指針訪問結構的成員,您必須使用 -> 運算符
    cout << movie->title << "\t" << movie->address << endl;
}

複製代碼

輸出:

葉問4 china 戰狼2 china

22. 類 & 對象

C++ 在 C 語言的基礎上增長了面向對象編程,C++ 支持面向對象程序設計。類是 C++ 的核心特性,一般被稱爲用戶定義的類型。

類用於指定對象的形式,它包含了數據表示法和用於處理數據的方法。類中的數據和方法稱爲類的成員。函數在一個類中被稱爲類的成員。

類定義:

定義一個類,本質上是定義一個數據類型的藍圖。這實際上並無定義任何數據,但它定義了類的名稱意味着什麼,也就是說,它定義了類的對象包括了什麼,以及能夠在這個對象上執行哪些操做。

類定義是以關鍵字 class 開頭,後跟類的名稱。類的主體是包含在一對花括號中。類定義後必須跟着一個分號或一個聲明列表。例如,咱們建立一個 *.h 文件 使用關鍵字 class 定義 Person 數據類型,以下所示:

//定義了一個 Person ,跟 Java class 差很少
class Person {
public: //公共的屬性
    Person(); //空參

    ~Person(); //Person 銷燬執行

    Person(char *name, char *gen, int age); //有參構造

  	//成員變量
    char *name;
    char *gen;
    int age;

  	//函數
    void setName(char *name);

    char *getName();

    void setGen(char *gen);

    char *getGen();

    void setAge(int age);

    int getAge();
};
複製代碼

關鍵字 public 肯定了類成員的訪問屬性。在類對象做用域內,公共成員在類的外部是可訪問的。您也能夠指定類的成員爲 privateprotected

定義 C++ 對象:

類提供了對象的藍圖,因此基本上,對象是根據類來建立的。聲明類的對象,就像聲明基本類型的變量同樣。下面的語句聲明瞭類 Person的兩個對象:

Person personTemp;//聲明 personTemp 類型爲 Person
複製代碼

訪問數據成員:

類的對象的公共數據成員可使用直接成員訪問運算符 (.) 來訪問。爲了更好地理解這些概念,讓咱們嘗試一下下面的實例:

//1. person.h 文件中定義 Person 對象
//定義了一個 Person ,跟 Java class 差很少
class Person {
public: //公共的屬性
    Person();

    ~Person();

    Person(char *name, char *gen, int age);

    char *name;
    char *gen;
    int age;

    void setName(char *name);

    char *getName();

    void setGen(char *gen);

    char *getGen();

    void setAge(int age);

    int getAge();

};

//2. person.cpp 

Person::Person(char *name, char *gen, int age) {
    this->name = name;
    this->age = age;
    this->gen = gen;
}

Person::~Person() {
    cout << "Person 銷燬" << endl;
}

Person::Person() {
    cout << "執行 Person 空參構造函數" << endl;
}

void Person::setAge(int age) {
    this->age = age;
}

void Person::setName(char *name) {
    this->name = name;
}

void Person::setGen(char *gen) {
    this->gen = gen;
}

char *Person::getName() {
    cout << "DevYK getName" << endl;
    return this->name;
}

char *Person::getGen() {
    return this->gen;
}

int Person::getAge() {
    return this->age;
}

//3. 測試
void test19() {
    //棧裏面定義的當該方法執行完就會回收掉 Person 對象
    Person personTemp;
    personTemp.setGen("男");
    personTemp.setName("小明");
    personTemp.setAge(18);
    cout << personTemp.getName() << "\t" << personTemp.getGen() << "\t" << personTemp.getAge() << endl;
    //初始化 Person 對象,在堆內存中,若是不主動清理 那麼就會形成 內存泄漏
    Person *person = new Person("DevYK", "男", 28);
    cout << person->getName() << "\t" << person->getGen() << "\t" << person->getAge() << endl;
    //釋放 person 堆內存
    delete person;
}
複製代碼

輸出:

執行 Person 空參構造函數 小明 男 18 DevYK 男 28 Person 銷燬 Person 銷燬

須要注意的是,私有的成員和受保護的成員不能使用直接成員訪問運算符 (.) 來直接訪問。咱們將在後續的教程中學習如何訪問私有成員和受保護的成員。

類 & 對象詳解:

到目前爲止,咱們已經對 C++ 的類和對象有了基本的瞭解。下面的列表中還列出了其餘一些 C++ 類和對象相關的概念,能夠點擊相應的連接進行學習。

概念 描述
類成員函數 類的成員函數是指那些把定義和原型寫在類定義內部的函數,就像類定義中的其餘變量同樣。
類訪問修飾符 類成員能夠被定義爲 public、private 或 protected。默認狀況下是定義爲 private。
構造函數 & 析構函數 類的構造函數是一種特殊的函數,在建立一個新的對象時調用。類的析構函數也是一種特殊的函數,在刪除所建立的對象時調用。
C++ 拷貝構造函數 拷貝構造函數,是一種特殊的構造函數,它在建立對象時,是使用同一類中以前建立的對象來初始化新建立的對象。
C++ 友元函數 友元函數能夠訪問類的 private 和 protected 成員。
C++ 內聯函數 經過內聯函數,編譯器試圖在調用函數的地方擴展函數體中的代碼。
C++ 中的 this 指針 每一個對象都有一個特殊的指針 this,它指向對象自己。
C++ 中指向類的指針 指向類的指針方式如同指向結構的指針。實際上,類能夠當作是一個帶有函數的結構。
C++ 類的靜態成員 類的數據成員和函數成員均可以被聲明爲靜態的。

23. 繼承

面向對象程序設計中最重要的一個概念是繼承。繼承容許咱們依據另外一個類來定義一個類,這使得建立和維護一個應用程序變得更容易。這樣作,也達到了重用代碼功能和提升執行效率的效果。

當建立一個類時,您不須要從新編寫新的數據成員和成員函數,只需指定新建的類繼承了一個已有的類的成員便可。這個已有的類稱爲基類,新建的類稱爲派生類

繼承表明了 is a 關係。例如,哺乳動物是動物,狗是哺乳動物,所以,狗是動物,等等。

基類 & 派生類:

一個類能夠派生自多個類,這意味着,它能夠從多個基類繼承數據和函數。定義一個派生類,咱們使用一個類派生列表來指定基類。類派生列表以一個或多個基類命名,形式以下:

class derived-class: access-specifier base-class 複製代碼

其中,訪問修飾符 access-specifier 是 public、protectedprivate 其中的一個,base-class 是以前定義過的某個類的名稱。若是未使用訪問修飾符 access-specifier,則默認爲 private。

//1. 定義基類
class SuperMan {
public:
    ~SuperMan();

    char *superName;

    void setSuperName(char *superName);

    char *getSuperName();
};

//2. 定義子類
class DevYK : public SuperMan {
public:
    DevYK();

    ~DevYK();

    char *getSupName();
};

//3. 測試
void test20() {
		//子類
    DevYK *devYkTemp = new DevYK();
    //父類set Name
    devYkTemp->setSuperName("superMan");
    //子類獲取
    cout << "單繼承:" << devYkMoreTemp->getPersonName();
}
複製代碼

指針類型的訪問須要以 「->」 形式來訪問

輸出:

執行 DevYK 空參構造函數

單繼承:superMan

訪問控制和繼承:

派生類能夠訪問基類中全部的非私有成員。所以基類成員若是不想被派生類的成員函數訪問,則應在基類中聲明爲 private。

咱們能夠根據訪問權限總結出不一樣的訪問類型,以下所示:

訪問 public protected private
同一個類 yes yes yes
派生類 yes yes no
外部的類 yes no no

一個派生類繼承了全部的基類方法,但下列狀況除外:

  • 基類的構造函數、析構函數和拷貝構造函數。
  • 基類的重載運算符。
  • 基類的友元函數。

多繼承:

多繼承即一個子類能夠有多個父類,它繼承了多個父類的特性。

C++ 類能夠從多個類繼承成員,語法以下:

class <派生類名>:<繼承方式1><基類名1>,<繼承方式2><基類名2>,…
{
<派生類類體>
};
複製代碼

其中,訪問修飾符繼承方式是 public、protectedprivate 其中的一個,用來修飾每一個基類,各個基類之間用逗號分隔,如上所示。如今讓咱們一塊兒看看下面的實例:

//1. 定義基類 Person
class Person {
public: //公共的屬性
    Person();

    ~Person();

    Person(char *name, char *gen, int age);

    char *name;
    char *gen;
    int age;

    void setName(char *name);

    char *getName();

    void setGen(char *gen);

    char *getGen();

    void setAge(int age);

    int getAge();



};

//2. 定義基類 SuperMan
class SuperMan {
public:
    ~SuperMan();

    char *superName;

    void setSuperName(char *superName);

    char *getSuperName();
};

//3. 派生類
/** * 繼承關係 * 多繼承 */
class DevYK : public Person, public SuperMan {
public:
    DevYK(char *name, int age) : Person(name, "nan", age) {};

    int length;

    DevYK();

    ~DevYK();

    char *getPersonName();

    char *getSupName();

    void setLength(int len);

    int getLength();

    char *getName();
};

//4. 測試
void test20() {
// //子類
    DevYK *devYkTemp = new DevYK();
    //父類set Name
    devYkTemp->setName("DevYK");
    //子類獲取
    cout << "單繼承 Person Name:" << devYkTemp->getPersonName() << endl;

    //多繼承
    DevYK *devYkMoreTemp = new DevYK();
    //給父類 Person set Name
    devYkMoreTemp->setName("DevYK");
    //給父類 Super set Name
    devYkMoreTemp->setSuperName("superMan");
    //子類獲取
    cout << "多繼承 Person Name:" << devYkMoreTemp->getPersonName()<< endl;
    cout << "多繼承 SuperMan Name:" << devYkMoreTemp->getSupName() << endl;
    //釋放內存
    delete devYkTemp, devYkMoreTemp;
}
複製代碼

輸出:

執行 Person 空參構造函數 執行 DevYK 空參構造函數 單繼承 Person Name:DevYK 執行 Person 空參構造函數 執行 DevYK 空參構造函數 多繼承 Person Name:DevYK 多繼承 SuperMan Name:superMan DevYK 銷燬 SuperMan 銷燬 Person 銷燬

24. 重載運算符和重載函數

C++ 容許在同一做用域中的某個函數運算符指定多個定義,分別稱爲函數重載運算符重載

重載聲明是指一個與以前已經在該做用域內聲明過的函數或方法具備相同名稱的聲明,可是它們的參數列表和定義(實現)不相同。

當您調用一個重載函數重載運算符時,編譯器經過把您所使用的參數類型與定義中的參數類型進行比較,決定選用最合適的定義。選擇最合適的重載函數或重載運算符的過程,稱爲重載決策

函數重載:

//1. 在 .h 文件中定義 DevYK 對象
class DevYK {
public:
    int length;

    DevYK();

    ~DevYK();

    void setLength(int len);

    int getLength();

};

//2. .cpp 實現

DevYK::DevYK() {
    cout << "執行 DevYK 空參構造函數" << endl;
}

DevYK::~DevYK() {
    cout << "DevYK 銷燬" << endl;
}

void DevYK::setLength(int len) {
    this->length = len;
}

int DevYK::getLength() {
    return this->length;
}

//3. 測試
void test21() {
    //1.運算符重載
    DevYK devYkA;
    DevYK devYkB;
    DevYK devYkC;

    //進行初始化賦值
    devYkA.setLength(500);
    devYkB.setLength(20);

    cout << "devYkA length : " << devYkA.getLength() << endl;
    cout << "devYkB length : " << devYkB.getLength() << endl;
};
複製代碼

輸出:

devYkA length : 500 devYkB length : 20

運算符重載:

您能夠重定義或重載大部分 C++ 內置的運算符。這樣,您就能使用自定義類型的運算符。

重載的運算符是帶有特殊名稱的函數,函數名是由關鍵字 operator 和其後要重載的運算符符號構成的。與其餘函數同樣,重載運算符有一個返回類型和一個參數列表。

DevYK operator+(const DevYK &b);
複製代碼

下面的實例使用成員函數演示了運算符重載的概念。在這裏,對象做爲參數進行傳遞,對象的屬性使用 this 運算符進行訪問,以下所示:

//1. 在 .h 文件中定義 DevYK 對象
class DevYK {
public:
    int length;

    DevYK();

    ~DevYK();

    void setLength(int len);

    int getLength();
  
    DevYK operator+(const DevYK &b);

};

//2. .cpp 實現

DevYK::DevYK() {
    cout << "執行 DevYK 空參構造函數" << endl;
}

DevYK::~DevYK() {
    cout << "DevYK 銷燬" << endl;
}

void DevYK::setLength(int len) {
    this->length = len;
}

int DevYK::getLength() {
    return this->length;
}

// 重載 + 運算符,用於把兩個 Box 對象相加
class DevYK DevYK::operator+(const class DevYK &b) {
    DevYK devYk;
    devYk.length = this->length + b.length;
    return devYk;
}

//3. 測試:
void test21() {
    //1.運算符重載
    DevYK devYkA;
    DevYK devYkB;
    DevYK devYkC;

    //進行初始化賦值
    devYkA.setLength(500);
    devYkB.setLength(20);

    cout << "devYkA length : " << devYkA.getLength() << endl;
    cout << "devYkB length : " << devYkB.getLength() << endl;

    devYkC = devYkA + devYkB;
    cout << "DevYK 運算符重載 = " << devYkC.getLength() << endl;
};
複製代碼

輸出:

devYkA length : 500 devYkB length : 20

DevYK 運算符重載 = 520

25. 多態

多態按字面的意思就是多種形態。當類之間存在層次結構,而且類之間是經過繼承關聯時,就會用到多態。

C++ 多態意味着調用成員函數時,會根據調用函數的對象的類型來執行不一樣的函數。

下面的實例中,基類 Shape 被派生爲兩個類,以下所示:

//1. 定義了一個 Person 基類,跟 Java class 差很少
class Person {
public: //公共的屬性
    Person();

    ~Person();

    Person(char *name, char *gen, int age);

    char *name;
    char *gen;
    int age;

    void setName(char *name);

    char *getName();

    void setGen(char *gen);

    char *getGen();

    void setAge(int age);

    int getAge();
     /** * 虛函數 是在基類中使用關鍵字 virtual 聲明的函數。 * 在派生類中從新定義基類中定義的虛函數時,會告訴編譯器不要靜態連接到該函數。 * @return */
    //virtual 轉移給子類實現
    virtual int test() {
        return 10;
    };
};

//2. 定義 DevYK 派生類
class DevYK : public Person {
public:
    DevYK(char *name, int age) : Person(name, "nan", age) {};

    int length;

    DevYK();

    ~DevYK();

    char *getPersonName();

    void setLength(int len);

    int getLength();

    DevYK operator+(const DevYK &b);

    char *getName();
  
    int test() {
        return -10;
    };
};

//3. .cpp 文件

#include "ClssSample.h"
#include <iostream>

using namespace std;


Person::Person(char *name, char *gen, int age) {
    this->name = name;
    this->age = age;
    this->gen = gen;
}

Person::~Person() {
    cout << "Person 銷燬" << endl;
}

Person::Person() {
    cout << "執行 Person 空參構造函數" << endl;
}

void Person::setAge(int age) {
    this->age = age;
}

void Person::setName(char *name) {
    this->name = name;
}

void Person::setGen(char *gen) {
    this->gen = gen;
}

char *Person::getName() {
    return this->name;
}

char *Person::getGen() {
    return this->gen;
}

int Person::getAge() {
    return this->age;
}


DevYK::DevYK() {
    cout << "執行 DevYK 空參構造函數" << endl;
}

DevYK::~DevYK() {
    cout << "DevYK 銷燬" << endl;
}

char *DevYK::getName() {
    cout << "DevYK getName" << endl;
    return this->name;
}

char *DevYK::getPersonName() {
    return this->name;
}


void DevYK::setLength(int len) {
    this->length = len;
}

int DevYK::getLength() {
    return this->length;
}

// 重載 + 運算符,用於把兩個 Box 對象相加
class DevYK DevYK::operator+(const class DevYK &b) {
    DevYK devYk;
    devYk.length = this->length + b.length;
    return devYk;
}

//3. 測試

void test22() {
    //父類
    Person *person;
    //定義子類
    DevYK devYk("DevYK", 27);
    if (person) {
        cout << "Person\t" << person->getName() << "\t" << person->getGen() << "\t" << person->getAge() << endl;
    }
    cout << "DevYK->\t" << devYk.getName() << "\t" << devYk.getGen() << "\t" << devYk.getAge() << endl;

    //把 devyk 的內存地址賦值給 person
    person = &devYk;

    if (person) {
        cout << "Person\t" << person->getName() << "\t" << person->getGen() << "\t" << person->getAge() << endl;
    }
    cout << "DevYK 內存地址->\t" << &devYk << " Person 內存地址\t" << person << endl;

    //獲取子類數據
    int test = person->test();
    cout << "test->\t" << test << endl;
}
複製代碼

輸出:

DevYK-> DevYK getName DevYK nan 27 Person DevYK nan 27 DevYK 內存地址-> 0x7ffee1d26628 Person 內存地址 0x7ffee1d26628 test-> -10 DevYK 銷燬 Person 銷燬

虛函數:

虛函數 是在基類中使用關鍵字 virtual 聲明的函數。在派生類中從新定義基類中定義的虛函數時,會告訴編譯器不要靜態連接到該函數。

咱們想要的是在程序中任意點能夠根據所調用的對象類型來選擇調用的函數,這種操做被稱爲動態連接,或後期綁定

純虛函數:

您可能想要在基類中定義虛函數,以便在派生類中從新定義該函數更好地適用於對象,可是您在基類中又不能對虛函數給出有意義的實現,這個時候就會用到純虛函數。

26. 數據封裝

全部的 C++ 程序都有如下兩個基本要素:

  • **程序語句(代碼):**這是程序中執行動做的部分,它們被稱爲函數。
  • **程序數據:**數據是程序的信息,會受到程序函數的影響。

封裝是面向對象編程中的把數據和操做數據的函數綁定在一塊兒的一個概念,這樣能避免受到外界的干擾和誤用,從而確保了安全。數據封裝引伸出了另外一個重要的 OOP 概念,即數據隱藏

數據封裝是一種把數據和操做數據的函數捆綁在一塊兒的機制,數據抽象是一種僅向用戶暴露接口而把具體的實現細節隱藏起來的機制。

C++ 經過建立來支持封裝和數據隱藏(public、protected、private)。咱們已經知道,類包含私有成員(private)、保護成員(protected)和公有成員(public)成員。默認狀況下,在類中定義的全部項目都是私有的。例如:

class Box {
   public:
      double getVolume(void) {
         return length * breadth * height;
      }
   private:
      double length;      // 長度
      double breadth;     // 寬度
      double height;      // 高度
};
複製代碼

變量 length、breadth 和 height 都是私有的(private)。這意味着它們只能被 Box 類中的其餘成員訪問,而不能被程序中其餘部分訪問。這是實現封裝的一種方式。

爲了使類中的成員變成公有的(即,程序中的其餘部分也能訪問),必須在這些成員前使用 public 關鍵字進行聲明。全部定義在 public 標識符後邊的變量或函數能夠被程序中全部其餘的函數訪問。

把一個類定義爲另外一個類的友元類,會暴露實現細節,從而下降了封裝性。理想的作法是儘量地對外隱藏每一個類的實現細節。

//1. .h
class Box {
   public:
      double getVolume(void) {
         return length * breadth * height;
      }
   private:
      double length;      // 長度
      double breadth;     // 寬度
      double height;      // 高度
};
//2. .cpp
double Box::getVolume() {
    return this->length * this->height * this->width;
}

void Box::initValue() {
    //默認
    int a = 10, b = 20, c = 30;
    length = a, width = b, height = c;
}
//3. cpp 測試
void test23() {
    Box box;
    box.initValue();
    cout << "數據私有封住:" << box.getVolume() << endl;
}
複製代碼

輸出:

數據私有封住:6000

27. 接口(抽象類)

接口描述了類的行爲和功能,而不須要完成類的特定實現。

C++ 接口是使用抽象類來實現的,抽象類與數據抽象互不混淆,數據抽象是一個把實現細節與相關的數據分離開的概念。

若是類中至少有一個函數被聲明爲純虛函數,則這個類就是抽象類。純虛函數是經過在聲明中使用 "= 0" 來指定的。

例子參考 25 小節的多態。

28. 文件和流

到目前爲止,咱們已經使用了 iostream 標準庫,它提供了 cincout 方法分別用於從標準輸入讀取流和向標準輸出寫入流。

本教程介紹如何從文件讀取流和向文件寫入流。這就須要用到 C++ 中另外一個標準庫 fstream,它定義了三個新的數據類型:

數據類型 描述
ofstream 該數據類型表示輸出文件流,用於建立文件並向文件寫入信息。
ifstream 該數據類型表示輸入文件流,用於從文件讀取信息。
fstream 該數據類型一般表示文件流,且同時具備 ofstream 和 ifstream 兩種功能,這意味着它能夠建立文件,向文件寫入信息,從文件讀取信息。

要在 C++ 中進行文件處理,必須在 C++ 源代碼文件中包含頭文件 和 。

在從文件讀取信息或者向文件寫入信息以前,必須先打開文件。ofstreamfstream 對象均可以用來打開文件進行寫操做,若是隻須要打開文件進行讀操做,則使用 ifstream 對象。

下面是 open() 函數的標準語法,open() 函數是 fstream、ifstream 和 ofstream 對象的一個成員。

void open(const char *filename, ios::openmode mode);
複製代碼

在這裏,open() 成員函數的第一參數指定要打開的文件的名稱和位置,第二個參數定義文件被打開的模式。

模式標誌 描述
ios::app 追加模式。全部寫入都追加到文件末尾。
ios::ate 文件打開後定位到文件末尾。
ios::in 打開文件用於讀取。
ios::out 打開文件用於寫入。
ios::trunc 若是該文件已經存在,其內容將在打開文件以前被截斷,即把文件長度設爲 0。

您能夠把以上兩種或兩種以上的模式結合使用。例如,若是您想要以寫入模式打開文件,並但願截斷文件,以防文件已存在,那麼您可使用下面的語法:

ofstream outfile;
outfile.open("file.dat", ios::out | ios::trunc );
複製代碼

相似地,您若是想要打開一個文件用於讀寫,可使用下面的語法:

ifstream  afile;
afile.open("file.dat", ios::out | ios::in );
複製代碼

關閉文件:

當 C++ 程序終止時,它會自動關閉刷新全部流,釋放全部分配的內存,並關閉全部打開的文件。但程序員應該養成一個好習慣,在程序終止前關閉全部打開的文件。

下面是 close() 函數的標準語法,close() 函數是 fstream、ifstream 和 ofstream 對象的一個成員。

void close();
複製代碼

寫入文件:

在 C++ 編程中,咱們使用流插入運算符( << )向文件寫入信息,就像使用該運算符輸出信息到屏幕上同樣。惟一不一樣的是,在這裏您使用的是 ofstreamfstream 對象,而不是 cout 對象。

讀取文件:

在 C++ 編程中,咱們使用流提取運算符( >> )從文件讀取信息,就像使用該運算符從鍵盤輸入信息同樣。惟一不一樣的是,在這裏您使用的是 ifstreamfstream 對象,而不是 cin 對象。

讀取 & 寫入實例:

void test24() {
    char data[100];
    //ofstream: 用於建立文件並向文件寫入信息。
    ofstream outfile;
    outfile.open("/Users/devyk/Data/ClionProjects/NDK_Sample/README.md");
    cout << "請輸入寫入文件的內容:" << endl;
    cin.getline(data, 100);
    //向文件開始寫入數據
    outfile << data << endl;

    cin.ignore();
    //關閉打開的文件
    outfile.close();


    cout << "\n\n" << endl;
    char readData[100];
    //開始讀文件
    ifstream readFile;
    readFile.open("/Users/devyk/Data/ClionProjects/NDK_Sample/README.md");
    readFile.getline(readData, 100);
    cout << "讀取成功\n" << readData << endl;
    readFile.close();


}
複製代碼

29. 異常處理

異常是程序在執行期間產生的問題。C++ 異常是指在程序運行時發生的特殊狀況,好比嘗試除以零的操做。

異常提供了一種轉移程序控制權的方式。C++ 異常處理涉及到三個關鍵字:try、catch、throw

  • throw: 當問題出現時,程序會拋出一個異常。這是經過使用 throw 關鍵字來完成的。
  • catch: 在您想要處理問題的地方,經過異常處理程序捕獲異常。catch 關鍵字用於捕獲異常。
  • try: try 塊中的代碼標識將被激活的特定異常。它後面一般跟着一個或多個 catch 塊。

若是有一個塊拋出一個異常,捕獲異常的方法會使用 trycatch 關鍵字。try 塊中放置可能拋出異常的代碼,try 塊中的代碼被稱爲保護代碼。使用 try/catch 語句的語法以下所示:

try
{
   // 保護代碼
}catch( ExceptionName e1 )
{
   // catch 塊
}catch( ExceptionName e2 )
{
   // catch 塊
}catch( ExceptionName eN )
{
   // catch 塊
}
複製代碼

若是 try 塊在不一樣的情境下會拋出不一樣的異常,這個時候能夠嘗試羅列多個 catch 語句,用於捕獲不一樣類型的異常。

拋出異常:

您可使用 throw 語句在代碼塊中的任何地方拋出異常。throw 語句的操做數能夠是任意的表達式,表達式的結果的類型決定了拋出的異常的類型。

如下是嘗試除以零時拋出異常的實例:

double division(int a, int b) {
   if( b == 0 )
   {
      throw "Division by zero condition!";
   }
   return (a/b);
}
複製代碼

捕獲異常:

catch 塊跟在 try 塊後面,用於捕獲異常。您能夠指定想要捕捉的異常類型,這是由 catch 關鍵字後的括號內的異常聲明決定的。

try
{
   // 保護代碼
}catch( ExceptionName e )
{
  // 處理 ExceptionName 異常的代碼
}
複製代碼

下面是一個實例,拋出一個除以零的異常,並在 catch 塊中捕獲該異常。

#include <iostream>
using namespace std;
 
double division(int a, int b) {
   if( b == 0 )
   {
      throw "Division by zero condition!";
   }
   return (a/b);
}
 
int main () {
   int x = 50;
   int y = 0;
   double z = 0;
 
   try {
     z = division(x, y);
     cout << z << endl;
   }catch (const char* msg) {
     cerr << msg << endl;
   }
 
   return 0;
}
複製代碼

因爲咱們拋出了一個類型爲 const char* 的異常,所以,當捕獲該異常時,咱們必須在 catch 塊中使用 const char*。當上面的代碼被編譯和執行時,它會產生下列結果:

輸出:

Division by zero condition!

自定義異常:

您能夠經過繼承和重載 exception 類來定義新的異常。下面的實例演示瞭如何使用 std::exception 類來實現本身的異常:

//自定義一個新的異常
class MyException : public std::exception{

public:
    const char* what() const throw(){
        return "C++ 操做失誤,請檢查代碼是否正確!";
    }
};

//測試:
void test25() {
    try {
        throw "空指針異常";
    } catch (const char *msg) {
        cout << "錯誤日誌:\t" << msg << endl;
    }

    //自定義異常
    try {
        throw MyException();
    } catch (MyException exception) {
        cout << "自定義異常:" << exception.what() << endl;
    } catch (exception &error) {
        cout << "其它異常:" << error.what() << endl;
    }
}
複製代碼

輸出:

錯誤日誌: 空指針異常 自定義異常:C++ 操做失誤,請檢查代碼是否正確!

30. 動態內存

瞭解動態內存在 C++ 中是如何工做的是成爲一名合格的 C++ 程序員必不可少的。C++ 程序中的內存分爲兩個部分:

  • **棧:**在函數內部聲明的全部變量都將佔用棧內存。
  • **堆:**這是程序中未使用的內存,在程序運行時可用於動態分配內存。

不少時候,您沒法提早預知須要多少內存來存儲某個定義變量中的特定信息,所需內存的大小須要在運行時才能肯定。

在 C++ 中,您可使用特殊的運算符爲給定類型的變量在運行時分配堆內的內存,這會返回所分配的空間地址。這種運算符即 new 運算符。

若是您再也不須要動態分配的內存空間,可使用 delete 運算符,刪除以前由 new 運算符分配的內存。

new 和 delete 運算符:

下面是使用 new 運算符來爲任意的數據類型動態分配內存的通用語法:

new data-type;
複製代碼

在這裏,data-type 能夠是包括數組在內的任意內置的數據類型,也能夠是包括類或結構在內的用戶自定義的任何數據類型。讓咱們先來看下內置的數據類型。例如,咱們能夠定義一個指向 double 類型的指針,而後請求內存,該內存在執行時被分配。咱們能夠按照下面的語句使用 new 運算符來完成這點:

double* pvalue  = NULL; // 初始化爲 null 的指針
pvalue  = new double;   // 爲變量請求內存
複製代碼

若是自由存儲區已被用完,可能沒法成功分配內存。因此建議檢查 new 運算符是否返回 NULL 指針,並採起如下適當的操做:

double* pvalue  = NULL;
if( !(pvalue  = new double ))
{
   cout << "Error: out of memory." <<endl;
   exit(1);
 
}
複製代碼

malloc() 函數在 C 語言中就出現了,在 C++ 中仍然存在,但建議儘可能不要使用 malloc() 函數。new 與 malloc() 函數相比,其主要的優勢是,new 不僅是分配了內存,它還建立了對象。

在任什麼時候候,當您以爲某個已經動態分配內存的變量再也不須要使用時,您可使用 delete 操做符釋放它所佔用的內存,以下所示:

delete pvalue;        // 釋放 pvalue 所指向的內存
複製代碼

下面的實例中使用了上面的概念,演示瞭如何使用 new 和 delete 運算符:

void test26() {
    //演示如何使用 new 和 delete 運算符:
    //初始化爲 null 的指針
    double *pvalue = NULL;
    //爲變量申請內存
    pvalue = new double;
    //在分配的地址存儲值
    *pvalue = 1314.520;
    //打印存儲的數字
    cout << "pvalue 的地址值爲:\t" << &pvalue << "\n在該地址存儲的值爲:\t" << *pvalue << endl;
    //釋放內存
    delete pvalue;

    //數組的動態內存分配
    //動態分配,數組長度爲 10
    int *array = new int[10];
    //釋放數組內存
    delete[] array;

    //對象的動態內存分配
    Box *testBox = new Box[10];
    delete[] testBox;

}
複製代碼

輸出:

pvalue 的地址值爲: 0x7ffee8b27658 在該地址存儲的值爲: 1314.52

pvalue 的地址值爲: 0x7ffee7b8c658 在該地址存儲的值爲: 1314.52 調用構造函數! 調用構造函數! 調用構造函數! 調用構造函數! 調用構造函數! 調用構造函數! 調用構造函數! 調用構造函數! 調用構造函數! 調用構造函數! 調用析構函數! 調用析構函數! 調用析構函數! 調用析構函數! 調用析構函數! 調用析構函數! 調用析構函數! 調用析構函數! 調用析構函數! 調用析構函數!

31. 命名空間

定義命名空間:

命名空間的定義使用關鍵字 namespace,後跟命名空間的名稱,以下所示:

namespace namespace_name {
   // 代碼聲明
}
複製代碼

爲了調用帶有命名空間的函數或變量,須要在前面加上命名空間的名稱,以下所示:

name::code;  // code 能夠是變量或函數
複製代碼

讓咱們來看看命名空間如何爲變量或函數等實體定義範圍:

//1. 定義命名空間
namespace test1_space {
    void func() {
        cout << "test1_space" << endl;
    }
}
namespace test2_space {
    void func2() {
        cout << "test2_space" << endl;
    }
}


void test27() {
    test1_space::func();
    test2_space::func2();
}
複製代碼

輸出:

test1_space test2_space

using 指令:

您可使用 using namespace 指令,這樣在使用命名空間時就能夠不用在前面加上命名空間的名稱。這個指令會告訴編譯器,後續的代碼將使用指定的命名空間中的名稱。

//1. 定義命名空間
namespace test1_space {
    void func() {
        cout << "test1_space" << endl;
    }
}
namespace test2_space {
    void func2() {
        cout << "test2_space" << endl;
    }
}
//2. 使用 using 指令
using namespace test1_space;
using namespace test2_space;

void test27() {
    //1.
    test1_space::func();
    test2_space::func2();
    //2.
    func();
    func2();
}
複製代碼

輸出:

test1_space test2_space

test1_space test2_space

32. 預處理器

預處理器是一些指令,指示編譯器在實際編譯以前所需完成的預處理。

全部的預處理器指令都是以井號(#)開頭,只有空格字符能夠出如今預處理指令以前。預處理指令不是 C++ 語句,因此它們不會以分號(;)結尾。

咱們已經看到,以前全部的實例中都有 #include 指令。這個宏用於把頭文件包含到源文件中。

C++ 還支持不少預處理指令,好比 #include、#define、#if、#else、#line 等,讓咱們一塊兒看看這些重要指令。

define 預處理:

define 預處理指令用於建立符號常量。該符號常量一般稱爲,指令的通常形式是:

#define macro-name replacement-text 
複製代碼

當這一行代碼出如今一個文件中時,在該文件中後續出現的全部宏都將會在程序編譯以前被替換爲 replacement-text。例如:

//1. 預處理器
#define MAX 100;
void test28() {
    cout << "經過預處理器定義了一個 MAX:" << MAX;
}
複製代碼

輸出:

經過預處理器定義了一個 MAX:100

參數宏:

您可使用 #define 來定義一個帶有參數的宏,以下所示:

//2. 參數宏
#define MAX_VALUE(a, b)(a>b?a:b);
void test28() {
    cout << "\n經過預處理器定義了一個 MAX_VALUE:" << MAX_VALUE(10, 20);
}
複製代碼

輸出:

經過預處理器定義了一個 MAX_VALUE:20

條件編譯:

有幾個指令能夠用來有選擇地對部分程序源代碼進行編譯。這個過程被稱爲條件編譯。

條件預處理器的結構與 if 選擇結構很像。請看下面這段預處理器的代碼:

//3. 條件編譯
#ifdef NULL
#define NULL 0
#endif
複製代碼

33. 多線程

多線程是多任務處理的一種特殊形式,多任務處理容許讓電腦同時運行兩個或兩個以上的程序。通常狀況下,兩種類型的多任務處理:基於進程和基於線程

  • 基於進程的多任務處理是程序的併發執行。
  • 基於線程的多任務處理是同一程序的片斷的併發執行。

多線程程序包含能夠同時運行的兩個或多個部分。這樣的程序中的每一個部分稱爲一個線程,每一個線程定義了一個單獨的執行路徑。

本教程假設您使用的是 Linux 操做系統,咱們要使用 POSIX 編寫多線程 C++ 程序。POSIX Threads 或 Pthreads 提供的 API 可在多種類 Unix POSIX 系統上可用,好比 FreeBSD、NetBSD、GNU/Linux、Mac OS X 和 Solaris。

建立線程:

下面的程序,咱們能夠用它來建立一個 POSIX 線程:

#include <pthread.h>
pthread_create (thread, attr, start_routine, arg) 
複製代碼

在這裏,pthread_create 建立一個新的線程,並讓它可執行。下面是關於參數的說明:

參數 描述
thread 指向線程標識符指針。
attr 一個不透明的屬性對象,能夠被用來設置線程屬性。您能夠指定線程屬性對象,也可使用默認值 NULL。
start_routine 線程運行函數起始地址,一旦線程被建立就會執行。
arg 運行函數的參數。它必須經過把引用做爲指針強制轉換爲 void 類型進行傳遞。若是沒有傳遞參數,則使用 NULL。

建立線程成功時,函數返回 0,若返回值不爲 0 則說明建立線程失敗。

終止線程:

使用下面的程序,咱們能夠用它來終止一個 POSIX 線程:

#include <pthread.h>
pthread_exit (status) 
複製代碼

在這裏,pthread_exit 用於顯式地退出一個線程。一般狀況下,pthread_exit() 函數是在線程完成工做後無需繼續存在時被調用。

若是 main() 是在它所建立的線程以前結束,並經過 pthread_exit() 退出,那麼其餘線程將繼續執行。不然,它們將在 main() 結束時自動被終止。

//定義一個線程數量宏
#define NUM_THREADS 5

//線程的運行函數
void *logD(void * args) {
   int threadID =  *((int*)args);
    cout << "線程執行了" << threadID << endl;
}

void test29() {
    //定義線程的 ID 變量,多個變量使用數組
    pthread_t tids[NUM_THREADS];

    for (int i = 0; i < NUM_THREADS; ++i) {
        //參數依次是:建立的線程 id,線程參數,調用的函數,傳入的參數函數
        int ptc = pthread_create(&tids[i],NULL,logD,(void*)&(i));
        if(0!=ptc){
            cout << "pthread_create error:error code :" << ptc << endl;
        }
    }


    //等線程執行完畢後在釋放線程
    pthread_exit(NULL);

}
複製代碼

輸出:

線程執行了線程執行了5 線程執行了5 5 線程執行了5 線程執行了5

總結

這裏差很少 C++ 基礎都講解完了,面向對象思想仍是很好理解的,下來你們必定要好好消化,下一個開始就開始使用 AS 學習 JNI 知識了。

參考

相關文章
相關標籤/搜索