C++的基本用法
C++是一種靜態類型的,編譯式的,通用的,大小寫敏感的,不規則的編程語言,支持過程化編程,面向對象編程和泛型編程的中級語言。
C++的編譯方法:
g++ runoob1.cpp runoob2.cpp -o runoob
生成的是一個可執行文件
1. typedef聲明
typedef爲一個已有的類型取一個新的名字:
typedef int feet;
這句話回告訴編譯器,feet是int的另一個名稱,所以可以用feet來聲明變量。
2. 枚舉類型:
如果一個變量只有幾種可能的值,定義爲枚舉類型:
enum color { red, green, blue } c;
變量c的類型爲color,只會有括號中的值
3. 變量聲明和定義:
可以使用extern關鍵字在任何地方聲明一個變量
#include <iostream>
using namespace std;
extern int a, b; // 變量聲明
int main()
{
int a, b; // 變量定義
}
變量賦值的真正含義:
int count;
count = 5;
程序會首先找到一塊能夠儲存整數的內存,將該內存單元標記爲count,並將5複製到該內存單元中,然後,就可以使用count來訪問該內存單元
4. bool值常量
true值爲真,false值爲假,不應該像c那樣看成是1和0
5. lambda函數表達式:
[capture](parameters) -> return_type{body}
比如 [] (int x, int y) -> int {int z = x + y; return z + x;}
lambda表達式內可以訪問當前作用域的變量,這是表達式的閉包行爲。
c++傳遞變量有傳遞值和傳遞變量的區別,使用最前面的[capture]來指定
[ ] 表示沒有定義任何變量,傳進來啥就用啥
[x, &y] 表示x以傳入值的方式,y以傳入引用的方式
[ = ] 表示任何外部變量都以傳值的方式加以引用
6. 生成隨機數:
srand ( (unsigned)time(NULL)); // 設置種子
j = rand(); // 生成實際的隨機數
7. 函數傳參三種方式:
傳值調用:
直接傳入值
指針調用:
void swap(int *x, int *y); // 函數需要的參數是指針類型
swap(&a, &b); // 調用函數,傳入的就是地址
引用調用:
void swap(int &x, int &y); // 函數直接使用傳入參數的地址
swap(a, b); // 調用函數,傳入的只是普通的變量
8. 傳遞數組給函數:
c++傳數組給一個函數,數組類型自動轉換爲指針類型,所以實際傳進去的都是地址
所以三種聲明函數方法,都是告訴編譯器將要接受一個整型指針:
void func(int *param);
void func(int param[10]);
void func(int param[]);
9. 函數返回數組:
c++不允許返回一個完整的數組,但是我們可以通過指定不帶索引的數組名來返回一個指向數組的指針:
int * getarray()
{
static int r[10]; // 由於不支持在函數外返回局部變量的地址,所以定義局部變量爲static
...
return r; // 返回一個指針
}
int main()
{
int * p; // 定義一個指針來接收地址
p = getarray();
}
10. c風格字符串:
char hello_array[] = "hello";
, c++中有大量的函數來操作以null結尾的字符串
strcpy(s1,s2) 複製s2到s1
strcat(s1,s2) 連接s2到s1末尾
strlen(s1) s1長度
strcmp(s1, s2) 相同返回0,s1<s2返回值小於0,反之
strchr(s1, ch) 返回一個指針,字符ch第一次出現的位置
strstr(s1, s2) 返回一個指針, 字符串s2第一次出現的位置
11. c++中的string類
string s1 = "hello";
string s2 = "world";
s2 = s1; // 複製
s3 = s2 + s1; // 拼接
len = s3.size(); // 長度
12. 數組指針
我們喜歡在程序中使用指針代替數組,因爲變量指針可以遞增,但是數組不能遞增,因爲數組是一個常量指針,所以:
int var[3] = {1,2,3};
*var = 2; // 正確
var++; // 錯誤,var是一個指向數組開頭的常量,不能作爲左值
int * ptr[3];
ptr聲明爲一個數組,ptr中的每一個元素都是一個指向int值的指針
指向指針的指針(多級間接尋址)
int ** ptr;
聲明瞭一個指向int類型指針的指針
所以取值需要兩個星號: ** ptr
13. C++引用
引用和指針很容易混淆,主要有三個區別:
- 不存在空引用,必須連接到一塊合法的內存
- 一旦引用被初始化爲一個對象,就不可以指向到另一個對象,指針可以在任何時候只想到另一個對象
- 引用必須在創建的時候被初始化,指針任何時間都可以初始化
其實變量名是變量在內存位置中的一個標籤,那麼可以吧引用當成是變量在內存位置中的第二個標籤。
int i = 10;
int& a = i; // 這裏的&讀作引用,所以打印a也是10
這裏就可以對照上面的函數傳參方式是引用調用理解
把引用作爲函數的返回值:
其實使用引用來替代指針,會是c++程序更容易維護與閱讀
int& func()
{
int q;3
return q; // 錯誤,超出作用域
static int i;
return i; // 正確,返回一個對靜態變量的引用
}
14. 結構裏面的typedef關鍵字
普通的用sstruct來聲明結構的方法就不用多說了
可以用typedef來更簡單的定義結構:
typrdef struct Books
{
....;
}Books;
Books book1, book2; // 可以不用struct關鍵字來定義結構變量了
15. 數組的替代品:vector和array模板類
vector:
#include <vector>
using namespace std;
vector<int> vi;
int n = 3;
vector<double> vd(n);
vi的初始長度爲0,vector對象在插入或者添加值的時候自動調整長度,可以使用vector包裏面的各種方法
創建方式: vector<type> vt(n_elem)
array:
vector類功能強大,但是付出的代價是效率稍低,至於array對象也位於名稱空間std裏面,與數組一樣,array對象的長度也是固定的,也使用棧也就是靜態內存分配,而不是自由存儲區,但是比原生數組更加方便。
#include <array>
using namespace std;
array<int, 5> ai;
array<double, 4> ad = {1.1, 1.2, 1.3, 1.4}
創建方式: vector<type, n_elem> vt
,這裏與vector不一樣的地方是這裏的n不能是變量
小結: 無論是數組還是array vector都可以使用標準數組表示法來訪問各元素,但是可以發現,array對象和數組都儲存在棧中,而vector對象儲存在自由存儲區或者堆中。可以將一個array對象的賦給另個array對象
C++的面向對象
1. 類和對象
class Box
{
public: // 類訪問修飾符,確定類成員的訪問屬性,還可以指定爲private,protected
double length;
double hight;
double getVolumn(void);
double getVolumn(void)
{
return ...;
}
};
Box box1; // 聲明類的對象就像聲明基本類型的變量一樣
類成員可以使用成員訪問運算符(.)來訪問,但是private和protected是不可以這樣訪問的
還可以在類的外部定義函數,使用範圍解析運算符(::),但是這個需要在類的內部進行成員函數聲明?
double Box::getVolumn(void)
{
return ...;
}
私有成員 private
私有成員的變量和函數在外部是不可訪問的,如果沒有使用任何訪問修飾符,那麼這個成員就默認爲私有成員:
修改私有成員的方法:聲明一個公有方法,然後該公有方法(不管是不是使用::在類外部定義的)就是類中的成員,可以訪問類中的私有成員,所以可以用這個公有方法來修改私有成員
保護成員 protected
與私有成員十分相似,但是在該類的子類中是可以訪問的
繼承方式
繼承方式有三種,public,protected,private,比如:
class B : public A
{
...
}
如果是protected方式繼承,那麼父類中成員的最高屬性爲protected,也就是父類中的public成員也變成了protected成員,其他成員屬性因爲低於或者等於protected,所以屬性不變。
2. 類的構造函數和析構函數
構造函數: 會在每次創建類的新對象時執行,構造函數的名稱與類的名稱完全相同,不會返回人和類型,用於在對象初始化的時候爲成員設置初始值
class testClass
{
...;
testClass(); // 這是構造函數,前面沒有類型是因爲它不會返回任何類型,也不會返回void
~testClass(); // 這是析構函數
}
testClass::testClass(void) // 構造函數也可以帶有參數
{
...;
}
使用初始化列表來初始化字段:
怎麼給對象裏面的字段給初始值呢?可以使用初始化列表來初始化字段
testClass::testClass(double len): length(len)
{
...
}
testClass test(1);
testClass類有length這個成員,那麼我們想給length這個成員賦予初始值,可以在創建對象的時候賦予,最後一行的時候傳入了參數1,那麼len=1,並且同時length=len=1。
析構函數
與構造函數區別只有,前面需要加波浪號~,然後用處是在刪除所創建對象時執行
testClass::~testClass(void)
{..}
3. 類的拷貝構造函數
這個特殊函數的作用是在創建對象的時候,使用同一類中之前創建的對象來初始化新的對象,所以作用之一就是複製對象把它作爲參數傳遞給函數,常見形式:
classname (const classname &obj){
}
函數參數裏面出現了&,也就是引用,所以obj是一個對象引用,用於初始化另一個對象,實際例子:
class Line
{
public:
int getLength(void); // 普通的成員函數
Line(int len); // 構造函數
Line(const Line &obj); // 拷貝構造函數
~Line(); // 析構函數
private:
int *ptr;
};
Line::Line(int len) // 構造函數
{
ptr = new int; // 爲指針分配內存
*ptr = len; // 內存裏面存初始化對象時傳入的值
}
Line::Line(const Line &obj) // 拷貝構造函數
{
ptr = new int; // 爲自己的指針分配內存
*ptr = *obj.ptr; // 拷貝值
}
Line::~Line(void) // 析構函數
{
delete ptr; // 釋放爲指針分配的內存
}
int main()
{
Line line1(30);
Line line2 = line1; // 這裏實際上調用了拷貝構造函數,用一個已有對象初始化了一個一樣的對象
return 0;
}
4. 友元函數
類的友元函數是定義在類外部,但有權限訪問類的所有成員,包括private和protected。友元函數不是類的成員函數。
- 要聲明函數爲一個類的友元,在類中定義函數原型前使用friend關鍵字
class Box{
friend void func(Box box);
};
void func(Box box){ // 它不是任何類的成員函數
...
}
- 如果聲明一個類中的所有成員函數都是另一個類的友元函數,需要在另一個類比如是ClassOne中加上聲明:
class ClassOne{
friend class ClassTwo; // 這樣ClassTwo中的所有函數都能使用ClassOne中的私有或者保護成員了。
}
5. 內聯函數
如果一個函數是內聯函數,那麼在編譯的時候,編譯器會把該函數的代碼副本放置在每個調用該函數的地方。所以如果內聯函數有任何修改,都需要重新編譯。內聯函數一般是1-5行的小函數:
inline int Max(int x, int y) // 內聯函數就是函數開頭使用inline說明符
{
return (x > y) ? x : y;
}
6. this指針和指向類的指針
C++中每一個對象都能使用this指針來訪問自己的地址,所有在成員函數內部,可以使用this指針來指向調用對象。友元函數沒有this指針,因爲友元函數不是類的成員函數
class Box{
double Volumn(){
return 1;
}
int compare(Box box){
return this->Volumn() > box.Volumn();
}
};
指向類的指針:
指向類的指針和指向結構的指針非常類似,也是用->符號來訪問成員的
int main(void)
{
Box box1(1, 2, 3);
Box *ptr;
ptr = &box1;
ptr->Volumn(); // 使用成員訪問符訪問成員
}
7. 類的靜態成員
當我們聲明類的成員爲靜態的時候,意味着無論創建多少個類的對象,靜態成員都只有一個副本,所以靜態成員在所有類的對象中都是共享的。
class Box{
public:
static int objcount;
Box(double a, double b, double c){
objcount++;
}
};
int Box::objcount = 0;
int main(void){
Box box1(1,2,3);
Box box2(1,2,3);
cout << Box::objcount;
return 0;
}
上面的輸出objcount就是2了,靜態函數只用使用類名加上::就可以訪問
8. 類的繼承
已有類爲基類,新建的繼承的類爲派生類
class new_class: access_specifier base_class
access_specifier是訪問修飾符,也是public,protected,private中的一個,如果不指定,默認是private。
派生類可以剛問基類中的所有非私有成員。一個派生類繼承了所有基類的方法,除了基類的構造函數,析構函數,拷貝構造函數,友元函數,重載運算符。
9. 重載運算符 重載函數
C++允許在同一個作用域中的某個函數和運算符指定多個定義,分別稱爲函數重載和運算重載符
同一個作用域中,可以聲明幾個功能類似的同名函數,這些同名函數的形式參數必須不同(個數,類型,順序)。編譯器會把傳入的參數類型和定義中的參數類型進行比較,決定選用最合適的定義,這個選擇的過程叫重載決策。
class printData{
public:
void print(int i){
cout << i;
}
void print(double f){
cout << f;
}
};
10. 多態
多態意味着調用成員函數的時候,會根據調用函數的對象的額類型來執行不同的函數。
比如有一個類:
class Shape{
public:
int area(){
out << ...
}
};
然後這個shape類有兩個派生類,裏面都重寫了area()函數,但是初始化爲對象實際調用函數的時候,但是會調用這個基類的函數,這就是所謂的靜態多態,函數調用在程序執行前就準備好了,也被稱之爲早綁定,該函數在程序編譯的時候就已經設置好了
然後我們需要在函數聲明前放置關鍵字virtual:
virtual int area(){...}
此時,編譯器看的是指針的內容,而不是它的類型。
虛函數: 虛函數就是在基類中使用virtual關鍵字定義的函數,它會告訴編譯器不要靜態鏈接該函數,所以會動態鏈接
純虛函數: 如果想要實現虛函數的功能,但是在基類中又不能給出該函數實際的意義,那麼可以用純虛函數:
virtual int area() = 0;
11. C++接口,抽象類
C++接口使用抽象類來實現的,如果一個類中至少有一個函數被聲明爲純虛函數,那麼這個類就是抽象類。
抽象類是爲了給其他類提供一個可以繼承的適當的基類,抽象類不能被用於實例化對象,只能作爲接口使用。
C++的目標之一就是將C語言轉變成OOP語言。
標準的C++程序:
#include <iostream>
int main()
{
using namespace std;
cout << "Come up and C++";
cout << endl;
cout << "You ..." << endl;
return 0;
}
第一行的include爲了實現輸入輸出,後面的using指令使得std命名空間的所有名稱可用,而不必使用std::前綴。
<<
符號將一個字符串插入到了輸出流中,endl
表示重啓一行
cout << variable;
可以直接打印出variable的值
cin >> variable;
將鍵盤輸入插入到輸出流中
字符串常量:使用雙引號,比如“s”代表的是字符s和\0組成的字符串
字符常量:使用單引號,比如’s’只是83的另一種寫法
使用cin輸入的時候,cin不能識別空格,所以我們需要使用cin.getline(name, 20)
來將輸入讀入到一個包含20個元素的name數組裏面去。
string類:
c++可以使用string類型的變量了,而不是繼續用數組來儲存字符串
string str2 = "panther";
類設計能夠自動處理string的大小,將自動調整string的長度
str3 = str1 + str2
可以使用+將兩個string對象合併起來
int len1 - str1.size()
使用對象的方法來計算字符串的長度
指針和自由存儲空間
計算機儲存數據必須跟蹤三個基本屬性:信息儲存在哪裏,儲存的值是多少,儲存的信息時什麼類型。
&和*都是沿用了C的風格
分配和釋放內存:
int * ps = new int; // 用new來分配一個int的內存
... // 使用內存
delete ps; // 釋放內存
delete是釋放ps指向的內存,但是不會刪除指針本身,可以將ps重新指向一個新分配的內存塊,所以要配對的使用new和delete,否則將會發生內存泄漏
動態數組:
- 使用new創建動態數組
int * psome = new int [10];
delete [] psome; // 加上[]是因爲要釋放整個數組,而不是指針指向的數組第一個元素
- 使用動態數組
如何訪問裏面的元素呢?可以使用psome[0],C和C++的數組和指針基本等價,所以只要把指針當做數組名就可以