上一節學習了C++的函式:c++:-2,本節學習C++的陣列、指標和字串
陣列
定義和初始化
定義
例如:int a[10];
表示a為整型陣列,有10個元素:a[0]...a[9]
例如: int a[5][3];
表示a為整型二維陣列,其中第一維有5個下標(04),第二維有3個下標(02),陣列的元素個數為15,可以用於存放5行3列的整型資料表格。
使用
- 必須先宣告,後使用。
- 只能逐個引用陣列元素,而不能一次引用整個陣列
例如:a[0]=a[5]+a[7]-a[2*3]
例如:b[1][2]=a[2][3]/2
儲存
(1)一維陣列的儲存
陣列元素在記憶體中順次存放,它們的地址是連續的。元素間實體地址上的相鄰,對應著邏輯次序上的相鄰。
(2)二維陣列的儲存
按行存放
例如: float a[3][4];
可以理解為:
其中陣列a的儲存順序為:
初始化
(1)一維陣列的初始化
- 在定義陣列時給出陣列元素的初始值。
- 列出全部元素的初始值
例如:static int a[10]={0,1,2,3,4,5,6,7,8,9}; - 可以只給一部分元素賦初值
例如:static int a[10]={0,1,2,3,4}; - 在對全部陣列元素賦初值時,可以不指定陣列長度
例如:static int a[]={0,1,2,3,4,5,6,7,8,9}
(2)二維陣列的初始化
- 將所有初值寫在一個{}內,按順序初始化
例如:static int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12 - 分行列出二維陣列元素的初值
例如:static int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; - 可以只對部分元素初始化
例如:static int a[3][4]={{1},{0,6},{0,0,11}}; - 列出全部初始值時,第1維下標個數可以省略
例如:static int a[][4]={1,2,3,4,5,6,7,8,9,10,11,12};
或:static int a[][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; - 注意:
如果不作任何初始化,內部auto型陣列中會存在垃圾資料,static陣列中的資料預設初始化為0;
如果只對部分元素初始化,剩下的未顯式初始化的元素,將自動被初始化為零;
傳參
- 陣列元素作實參,與單個變數一樣。
- 陣列名作引數,形、實引數都應是陣列名(實質上是地址),型別要一樣,傳送的是陣列首地址。
- 對形引數組的改變會直接影響到實引數組。
物件陣列
定義和訪問
(1)定義物件陣列
類名 陣列名[元素個數];
(2)訪問物件陣列元素
通過下標訪問:陣列名[下標].成員名
初始化
- 陣列中每一個元素物件被建立時,系統都會呼叫類建構函式初始化該物件。
- 通過初始化列表賦值。
例:Point a[2]={Point(1,2),Point(3,4)}; - 如果沒有為陣列元素指定顯式初始值,陣列元素便使用預設值初始化(呼叫預設建構函式)。
- 元素所屬的類不宣告建構函式,則採用預設建構函式。
- 各元素物件的初值要求為相同的值時,可以宣告具有預設形參值的建構函式。
- 各元素物件的初值要求為不同的值時,需要宣告帶形參的建構函式。
- 當陣列中每一個物件被刪除時,系統都要呼叫一次解構函式。
基於範圍的for迴圈
原for迴圈
#include "iostream"
using namespace std;
int main() {
int array[3] = {1, 2, 3};
int *p;
for (p = array; p < array + sizeof(array) / sizeof(int); ++p) {
*p += 2;
std::cout << *p << std::endl;
}
}
新for迴圈
#include "iostream"
using namespace std;
int main() {
int array[3] = {1, 2, 3};
for(int & e : array)
{
e += 2;
std::cout<<e<<std::endl;
}
return 0;
}
指標
(1)記憶體空間的訪問方式
- 通過變數名訪問
- 通過地址訪問
(2)指標的概念
- 指標:記憶體地址,用於間接訪問記憶體單元
- 指標變數:用於存放地址的變數
定義
(1)例:
static int i;
static int* ptr = &i;
(2)例:
*ptr = 3;
(3)與地址相關的運算——“*”和“&”
- 指標運算子:*
- 地址運算子:&
初始化
(1)語法形式:儲存型別 資料型別 *指標名=初始地址;
例:int *pa = &a;
(2)注意事項
- 用變數地址作為初值時,該變數必須在指標初始化之前已宣告過,且變數型別應與指標型別一致。
- 可以用一個已有合法值的指標去初始化另一個指標變數。
- 不要用一個內部非靜態變數去初始化 static 指標。
賦值
(1)語法形式:指標名=地址
注意:“地址”中存放的資料型別與指標型別必須相符
(2)向指標變數賦的值必須是地址常量或變數,不能是普通整數。例如:
- 通過地址運算“&”求得已定義的變數和物件的起始地址
- 動態記憶體分配成功時返回的地址
(3)例外:整數0可以賦給指標,表示空指標。
(4)允許定義或宣告指向 void 型別的指標。該指標可以被賦予任何型別物件的地址。
例: void *general;
指標空值nullptr
(1)以往用0或者NULL去表達空指標的問題:
C/C++的NULL巨集是個被有很多潛在BUG的巨集。因為有的庫把其定義成整數0,有的定義成 (void*)0。在C的時代還好。但是在C++的時代,這就會引發很多問題。
(2)C++11使用nullptr
關鍵字,是表達更準確,型別安全的空指標
指向常量的指標
不能通過指向常量的指標改變所指物件的值,但指標本身可以改變,可以指向另外的物件。
例:
int a;
const int *p1 = &a; //p1是指向常量的指標
int b;
p1 = &b; //正確,p1本身的值可以改變
*p1 = 1; //編譯時出錯,不能通過p1改變所指的物件(常量)
指標型別的常量
若宣告指標常量,則指標本身的值不能被改變。
例:
int a;
int * const p2 = &a;
p2 = &b; //錯誤,p2是指標常量,不能改變
運算
算術運算
- 指標p加上或減去n
其意義是指標當前指向位置的前方或後方第n個資料的起始位置。 - 指標的++、--運算
意義是指向下一個或前一個完整資料的起始。 - 運算的結果值取決於指標指向的資料型別,總是指向一個完整資料的起始位置。
- 當指標指向連續儲存的同型別資料時,指標與整數的加減運和自增自減算才有意義。
(1)指標與整數相加的意義
關係運算
- 指向相同型別資料的指標之間可以進行各種關係運算。
- 指向不同資料型別的指標,以及指標與一般整數變數之間的關係運算是無意義的。
- 指標可以和零之間進行等於或不等於的關係運算。
例如:p==0或p!=0
指標與陣列
用指標訪問陣列
陣列是一組連續儲存的同型別資料,可以通過指標的算術運算,使指標依次指向陣列的各個元素,進而可以遍歷陣列。
(1)定義與賦值:
例:int a[10], pa;
pa=&a[0]; 或 pa=a;
經過上述定義及賦值後:
pa就是a[0],(pa+1)就是a[1],... ,(pa+i)就是a[i]。a[i], *(pa+i), *(a+i), pa[i]都是等效的。
(2)注意:
不能寫 a++,因為a是陣列首地址、是常量。
(3)舉例:
設有一個int型陣列a,有10個元素。用三種方法輸出各元素:
方法1:使用陣列名和下標
#include "iostream"
using namespace std;
int main() {
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
for (int i = 0; i < 10; i++)
cout << a[i] << " ";
cout << endl;
return 0;
}
方法2:使用陣列名和指標運算
#include "iostream"
using namespace std;
int main() {
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
for (int i = 0; i < 10; i++)
cout << *(a+i) << " ";
cout << endl;
return 0;
}
方法3:使用指標變數
#include "iostream"
using namespace std;
int main() {
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
for (int *p = a; p < (a + 10); p++)
cout << *p << " ";
cout << endl;
return 0;
}
指標與函式
以指標作為函式引數
為什麼需要用指標做引數?
- 需要資料雙向傳遞時(引用也可以達到此效果)
- 用指標作為函式的引數,可以使被調函式通過形參指標存取主調函式中實參指標指向的資料,實現資料的雙向傳遞
- 需要傳遞一組資料,只傳首地址執行效率比較高
- 實參是陣列名時形參可以是指標
指標型別的函式
(1)不要將非靜態區域性地址用作函式的返回值
(2)例子
#include "iostream"
using namespace std;
int *search(int *a,int num)
{
for(int i=0;i<10;i++)
{
if(a[i]==0)
return &a[i];
}
}
int main()
{
int arr[10];
int *search(int *a,int num);
for(int i=0;i<10;i++){
cin >> arr[i];
}
int *zeroptr= search(arr,10);
return 0;
}
指向函式的指標
(1)定義形式
儲存型別 資料型別 (*函式指標名)();
(2)含義
函式指標指向的是程式程式碼儲存區。
(3)用途
實現函式回撥
- 通過函式指標呼叫的函式
- 例如將函式的指標作為引數傳遞給一個函式,使得在處理相似事件的時候可以靈活的使用不同的方法。
呼叫者不關心誰是被呼叫者 - 需知道存在一個具有特定原型和限制條件的被呼叫函式。
物件指標
(1)定義
類名 *物件指標名;
(2)例
Point a(5,10);
Piont *ptr;
ptr=&a;
(3)通過指標訪問物件成員
物件指標名->成員名
例:ptr->getx() 相當於** (*ptr).getx();**
(4)舉例
#include <iostream>
using namespace std;
class Point {
public:
Point(int x = 0, int y = 0) : x(x), y(y) { }
int getX() const { return x; }
int getY() const { return y; }
private:
int x, y;
};
int main() {
Point a(4, 5);
Point *p1 = &a; //定義物件指標,用a的地址初始化
cout << p1->getX() << endl;//用指標訪問物件成員
cout << a.getX() << endl; //用物件名訪問物件成員
return 0;
}
輸出:
4
4
this指標
- 指向當前物件自己
- 隱含於類的每一個非靜態成員函式中
- 指出成員函式所操作的物件
當通過一個物件呼叫成員函式時,系統先將該物件的地址賦給this指標,然後呼叫成員函式,成員函式對物件的資料成員進行操作時,就隱含使用了this指標。 - 例如:Point類的getX函式中的語句:return x;
相當於: return this->x; - 例
class Fred; //前向引用宣告
class Barney {
Fred *x;
};
class Fred {
Barney y;
};
動態記憶體分配
分配與釋放
(1)動態申請記憶體操作符 new
new 型別名T(初始化引數列表)
功能:在程式執行期間,申請用於存放T型別物件的記憶體空間,並依初值列表賦以初值。
結果值:成功:T型別的指標,指向新分配的記憶體;失敗:丟擲異常。
(2)釋放記憶體操作符delete
delete 指標p
功能:釋放指標p所指向的記憶體。p必須是new操作的返回值。
(3)舉例
#include <iostream>
using namespace std;
class Point {
public:
Point() : x(0), y(0) {
cout<<"Default Constructor called."<<endl;
}
Point(int x, int y) : x(x), y(y) {
cout<< "Constructor called."<<endl;
}
~Point() { cout<<"Destructor called."<<endl; }
int getX() const { return x; }
int getY() const { return y; }
void move(int newX, int newY) {
x = newX;
y = newY;
}
private:
int x, y;
};
int main() {
cout << "Step one: " << endl;
Point *ptr1 = new Point; //呼叫預設建構函式
delete ptr1; //刪除物件,自動呼叫解構函式
cout << "Step two: " << endl;
ptr1 = new Point(1,2);
delete ptr1;
return 0;
}
輸出:
Step one:
Default Constructor called.
Destructor called.
Step two:
Constructor called.
Destructor called.
動態陣列
動態多維陣列
智慧指標
- 顯式管理記憶體在是能上有優勢,但容易出錯。
- C++11提供智慧指標的資料型別,對垃圾回收技術提供了一些支援,實現一定程度的記憶體管理
- 三類:
unique_ptr :不允許多個指標共享資源,可以用標準庫中的move函式轉移指標
shared_ptr :多個指標共享資源
weak_ptr :可複製shared_ptr,但其構造或者釋放對資源不產生影響
vector物件
(1)為什麼需要vector?
封裝任何型別的動態陣列,自動建立和刪除。
陣列下標越界檢查。
(2)vector物件的定義
vector<元素型別> 陣列物件名(陣列長度);
(3)例:
vector<int> arr(5)//建立大小為5的int陣列
vector物件的使用
(1)對陣列元素的引用
與普通陣列具有相同形式:vector物件名 [ 下標表示式 ]
(2)vector陣列物件名不表示陣列首地址
獲得陣列長度用size
函式:陣列物件名.size()
(3)舉例:
#include <iostream>
#include <vector>
using namespace std;
//計算陣列arr中元素的平均值
double average(const vector<double> &arr) //vector的引用
{
double sum = 0;
for (unsigned i = 0; i<arr.size(); i++)
sum += arr[i];
return sum / arr.size();
}
int main() {
unsigned n;
cout << "n = ";
cin >> n;
vector<double> arr(n); //建立陣列物件
cout << "Please input " << n << " real numbers:" << endl;
for (unsigned i = 0; i < n; i++)
cin >> arr[i];
cout << "Average = " << average(arr) << endl;
return 0;
}
輸出:
n = 5
Please input 5 real numbers:
1
2
3
4
6
Average = 3.2
深拷貝
淺層複製
實現物件間資料元素的一一對應複製。
舉例:
#include <iostream>
#include <cassert>
using namespace std;
class Point {
//類的宣告同例6-16
//……
};
class ArrayOfPoints {
//類的宣告同例6-18
//……
};
int main() {
int count;
cout << "Please enter the count of points: ";
cin >> count;
ArrayOfPoints pointsArray1(count); //建立物件陣列
pointsArray1.element(0).move(5,10);
pointsArray1.element(1).move(15,20);
ArrayOfPoints pointsArray2(pointsArray1); //建立副本
cout << "Copy of pointsArray1:" << endl;
cout << "Point_0 of array2: " << pointsArray2.element(0).getX() << ", "
<< pointsArray2.element(0).getY() << endl;
cout << "Point_1 of array2: " << pointsArray2.element(1).getX() << ", "
<< pointsArray2.element(1).getY() << endl;
pointsArray1.element(0).move(25, 30);
pointsArray1.element(1).move(35, 40);
cout<<"After the moving of pointsArray1:"<<endl;
cout << "Point_0 of array2: " << pointsArray2.element(0).getX() << ", "
<< pointsArray2.element(0).getY() << endl;
cout << "Point_1 of array2: " << pointsArray2.element(1).getX() << ", "
<< pointsArray2.element(1).getY() << endl;
return 0;
}
執行結果如下:
Please enter the number of points:2
Default Constructor called.
Default Constructor called.
Copy of pointsArray1:
Point_0 of array2: 5, 10
Point_1 of array2: 15, 20
After the moving of pointsArray1:
Point_0 of array2: 25, 30
Point_1 of array2: 35, 40
Deleting...
Destructor called.
Destructor called.
Deleting...
接下來程式出現執行錯誤。
深層複製
當被複制的物件資料成員是指標型別時,不是複製該指標成員本身,而是將指標所指物件進行復制。
當返回的物件含有動態建立的空間時,需要用深層複製
#include <iostream>
#include <cassert>
using namespace std;
class Point { //類的宣告同例6-16
};
class ArrayOfPoints {
public:
ArrayOfPoints(const ArrayOfPoints& pointsArray);
//其他成員同例6-18
};
ArrayOfPoints::ArrayOfPoints(const ArrayOfPoints& v) {
size = v.size;
points = new Point[size];//重新建立空間
for (int i = 0; i < size; i++)
points[i] = v.points[i];
}
int main() {
//同例6-20
}
程式的執行結果如下:
Please enter the number of points:2
Default Constructor called.
Default Constructor called.
Default Constructor called.
Default Constructor called.
Copy of pointsArray1:
Point_0 of array2: 5, 10
Point_1 of array2: 15, 20
After the moving of pointsArray1:
Point_0 of array2: 5, 10
Point_1 of array2: 15, 20
Deleting...
Destructor called.
Destructor called.
Deleting...
Destructor called.
Destructor called.
移動建構函式
在現實中有很多這樣的例子,我們將錢從一個賬號轉移到另一個賬號,將手機SIM卡轉移到另一臺手機,將檔案從一個位置剪下到另一個位置……移動構造可以減少不必要的複製,帶來效能上的提升。
- C++11標準中提供了一種新的構造方法——移動構造。
- C++11之前,如果要將源物件的狀態轉移到目標物件只能通過複製。在某些情況下,我們沒有必要複製物件——只需要移動它們。
- C++11引入移動語義:源物件資源的控制權全部交給目標物件
問題
當臨時物件在被複制後,就不再被利用了。我們完全可以把臨時物件的資源直接移動,這樣就避免了多餘的複製操作。
移動構造
什麼時候該觸發移動構造?
有可被利用的臨時物件
移動建構函式:class_name ( class_name && )
字串
字元陣列
- 字串常量
例:"program" - 各字元連續、順序存放,每個字元佔一個位元組,以‘\0’結尾,相當於一個隱含建立的字元常量陣列
- “program”出現在表示式中,表示這一char陣列的首地址
- 首地址可以賦給char常量指標:
const char *STRING1 = "program";
缺點
- 執行連線、拷貝、比較等操作,都需要顯式呼叫庫函式,很麻煩
- 當字串長度很不確定時,需要用new動態建立字元陣列,最後要用delete釋放,很繁瑣
- 字串實際長度大於為它分配的空間時,會產生陣列下標越界的錯誤
string
使用字串類string表示字串
string實際上是對字元陣列操作的封裝
建構函式
(1)string(); //預設建構函式,建立一個長度為0的串
例:
string s1;
(2)string(const char *s); //用指標s所指向的字串常量初始化string物件
例:
string s2 = “abc”;
(3)string(const string& rhs); //複製建構函式
例:
string s3 = s2;
常用操作
- s + t 將串s和t連線成一個新串
- s = t 用t更新s
- s == t 判斷s與t是否相等
- s != t 判斷s與t是否不等
- s < t 判斷s是否小於t(按字典順序比較)
- s <= t 判斷s是否小於或等於t (按字典順序比較)
- s > t 判斷s是否大於t (按字典順序比較)
- s >= t 判斷s是否大於或等於t (按字典順序比較)
- s[i] 訪問串中下標為i的字元
例:
string s1 = "abc", s2 = "def";
string s3 = s1 + s2; //結果是"abcdef"
bool s4 = (s1 < s2); //結果是true
char s5 = s2[1]; //結果是'e'
getline
如何輸入整行字串?
用cin的>>操作符輸入字串,會以空格作為分隔符,空格後的內容會在下一回輸入時被讀取
- getline可以輸入整行字串(要包string標頭檔案),例如:getline(cin, s2);
- 輸入字串時,可以使用其它分隔符作為字串結束的標誌(例如逗號、分號),將分隔符作為getline的第3個引數即可,例如:getline(cin, s2, ',');
程式
陣列
存放陣列
求Fibonacci數列的前20項
#include <iostream>
using namespace std;
int main() {
int i;
int f[20] = {1,1}; //初始化第0、1個數
for (i = 2; i < 20; i++) //求第2~19個數
f[i] = f[i - 2] + f[i - 1];
for (i=0;i<20;i++) { //輸出,每行5個數
if (i % 5 == 0) cout << endl;
cout.width(12); //設定輸出寬度為12
cout << f[i];
}
return 0;
}
輸出:
1 1 2 3 5
8 13 21 34 55
89 144 233 377 610
987 1597 2584 4181 6765
統計正確率
迴圈從鍵盤讀入若干組選擇題答案,計算並輸出每組答案的正確率,直到輸入ctrl+z為止。每組連續輸入5個答案,每個答案可以是'a','b','c','d'。
#include <iostream>
using namespace std;
int main() {
const char key[ ] = {'a','c','b','a','d'};
const int NUM_QUES = 5;
char c;
int ques = 0, numCorrect = 0;
cout << "Enter the " << NUM_QUES << " question tests:" << endl;
while(cin.get(c)) //cin.get(c)得到的是輸出流,所以只需要一次輸入
{
if(c != '\n') {
if(c == key[ques]) {
numCorrect++; cout << " ";
} else
cout<<"*";
ques++;
} else {
cout << " Score " << static_cast<float>(numCorrect)/NUM_QUES*100 << "%";
ques = 0; numCorrect = 0; cout << endl;
}
}
return 0;
}
輸出:
Enter the 5 question tests:
abcda
**** Score 20%
abcad
** Score 60%
cabcd
** * Score 40%
ddaca
***** Score 0%
使用陣列名作為函式引數
主函式中初始化一個二維陣列,表示一個矩陣,矩陣,並將每個元素都輸出,然後呼叫子函式,分別計算每一行的元素之和,將和直接存放在每行的第一個元素中,返回主函式之後輸出各行元素的和。
#include <iostream>
using namespace std;
void rowSum(int a[][4], int nRow) {
for (int i = 0; i < nRow; i++) {
for(int j = 1; j < 4; j++)
a[i][0] += a[i][j];
}
}
int main() { //主函式
//定義並初始化陣列
int table[3][4] = {{1, 2, 3, 4}, {2, 3, 4, 5}, {3, 4, 5, 6}};
//輸出陣列元素
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++)
cout << table[i][j] << " ";
cout << endl;
}
rowSum(table, 3); //呼叫子函式,傳入的是陣列名,計算各行和
//輸出計算結果
for (int i = 0; i < 3; i++)
cout << "Sum of row " << i << " is " << table[i][0] << endl;
return 0;
}
輸出:
1 2 3 4
2 3 4 5
3 4 5 6
Sum of row 0 is 10
Sum of row 1 is 14
Sum of row 2 is 18
物件陣列應用
//Point.h
#ifndef _POINT_H
#define _POINT_H
class Point { //類的定義
public: //外部介面
Point();
Point(int x, int y);
~Point();
void move(int newX,int newY);
int getX() const { return x; }
int getY() const { return y; }
static void showCount(); //靜態函式成員
private: //私有資料成員
int x, y;
};
#endif //_POINT_H
//Point.cpp
#include <iostream>
#include "Point.h"
using namespace std;
Point::Point() : x(0), y(0) {
cout << "Default Constructor called." << endl;
}
Point::Point(int x, int y) : x(x), y(y) {
cout << "Constructor called." << endl;
}
Point::~Point() {
cout << "Destructor called." << endl;
}
void Point::move(int newX,int newY) {
cout << "Moving the point to (" << newX << ", " << newY << ")" << endl;
x = newX;
y = newY;
}
#include "Point.h"
#include <iostream>
using namespace std;
int main() {
cout << "Entering main..." << endl;
Point a[2];
for(int i = 0; i < 2; i++)
a[i].move(i + 10, i + 20);
cout << "Exiting main..." << endl;
return 0;
}
輸出:
Entering main...
Default Constructor called.
Default Constructor called.
Moving the point to (10, 20)
Moving the point to (11, 21)
Exiting main...
Destructor called.
Destructor called.
指標
void指標
#include <iostream>
using namespace std;
int main() {
//!void voidObject; 錯,不能宣告void型別的變數
void *pv; //對,可以宣告void型別的指標
int i = 5;
pv = &i; //void型別指標指向整型變數
int *pint = static_cast<int *>(pv); //void指標轉換為int指標
cout << "*pint = " << *pint << endl;
return 0;
}
輸出:
*pint = 5
指標表示矩陣
#include "iostream"
using namespace std;
int main()
{
int line1[]={1,0,1};
int line2[]={1,0,1};
int line3[]={1,0,1};
//定義整型指標陣列並初始化
int *pline[3]={line1,line2,line3};
cout <<"矩陣:"<< endl;
//輸出
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
cout <<pline[i][j]<<" ";
}
cout << endl;
}
return 0;
}
輸出:
矩陣:
1 0 1
1 0 1
1 0 1
指標做形參
讀入三個浮點數,將整數部分和小數部分分別輸出
#include "iostream"
using namespace std;
void splitFloat(float x, int *intPart, float *fracPart) {
*intPart = static_cast<int>(x); //取x的整數部分
*fracPart = x - *intPart; //取x的小數部分
}
int main() {
cout << "Enter 3 float point numbers:" << endl;
for(int i = 0; i < 3; i++) {
float x, f;
int n;
cin >> x;
splitFloat(x, &n, &f); //變數地址作為實參
cout << "Integer Part = " << n <<endl<< "Fraction Part = " << f << endl;
}
return 0;
}
指向常量的指標做形參
#include "iostream"
using namespace std;
const int N = 6;
void print(const int *p, int n);
int main() {
int array[N];
for (int i = 0; i < N; i++)
cin>>array[i];
print(array, N);
return 0;
}
void print(const int *p, int n) {
cout << "{ " << *p;
for (int i = 1; i < n; i++)
cout << ", " << *(p+i);
cout << " }" << endl;
}
輸出:
1
2
3
4
5
6
{ 1, 2, 3, 4, 5, 6 }
函式指標
編寫一個計算函式compute,對兩個整數進行各種計算。有一個形參為指向具體演算法函式的指標,根據不同的實參函式,用不同的演算法進行計算。編寫三個函式:求兩個整數的最大值、最小值、和。分別用這三個函式作為實參,測試compute函式
#include <iostream>
using namespace std;
int compute(int a, int b, int(*func)(int, int))
{ return func(a, b);}
int max(int a, int b) // 求最大值
{ return ((a > b) ? a: b);}
int min(int a, int b) // 求最小值
{ return ((a < b) ? a: b);}
int sum(int a, int b) // 求和
{ return a + b;}
int main()
{
int a, b, res;
cout << "請輸入整數a:"; cin >> a;
cout << "請輸入整數b:"; cin >> b;
res = compute(a, b, & max);
cout << "Max of " << a << " and " << b << " is " << res << endl;
res = compute(a, b, & min);
cout << "Min of " << a << " and " << b << " is " << res << endl;
res = compute(a, b, & sum);
cout << "Sum of " << a << " and " << b << " is " << res << endl;
}
輸出:
請輸入整數a:12
請輸入整數b:32
Max of 12 and 32 is 32
Min of 12 and 32 is 12
Sum of 12 and 32 is 44
動態陣列類
#include <iostream>
#include <cassert>
using namespace std;
class Point { //類的宣告同例6-16 … };
class ArrayOfPoints { //動態陣列類
public:
ArrayOfPoints(int size) : size(size) {
points = new Point[size];
}
~ArrayOfPoints() {
cout << "Deleting..." << endl;
delete[] points;
}
Point& element(int index) {
assert(index >= 0 && index < size);
return points[index];
}
private:
Point *points; //指向動態陣列首地址
int size; //陣列大小
};
int main() {
int count;
cout << "Please enter the count of points: ";
cin >> count;
ArrayOfPoints points(count); //建立陣列物件
points.element(0).move(5, 0); //訪問陣列元素的成員
points.element(1).move(15, 20); //訪問陣列元素的成員
return 0;
}
執行結果:
Please enter the number of points:2
Default Constructor called.
Default Constructor called.
Deleting...
Destructor called.
Destructor called.
vector陣列
#include <vector>
#include <iostream>
int main()
{
std::vector<int> v = {1,2,3};
for(auto i = v.begin(); i != v.end(); ++i)
std::cout << *i << std::endl;
for(auto e : v)//帶範圍的for迴圈
std::cout << e << std::endl;
}
移動構造
函式返回含有指標成員的物件
(1)先給出使用深層複製建構函式,返回時構造臨時物件,動態分配將臨時物件返回到主調函式,然後刪除臨時物件。
#include<iostream>
using namespace std;
class IntNum {
public:
IntNum(int x = 0) : xptr(new int(x)){ //建構函式
cout << "Calling constructor..." << endl;
}
//n是引數物件的引用;*n.xptr取值;xptr(new int(*n.xptr)是深拷貝
IntNum(const IntNum & n) : xptr(new int(*n.xptr)){//複製建構函式
cout << "Calling copy constructor..." << endl;
};
~IntNum(){ //解構函式
delete xptr;
cout << "Destructing..." << endl;
}
int getInt() { return *xptr; }
private:
int *xptr;
};
//返回值為IntNum類物件
IntNum getNum() {
IntNum a;//新建一個區域性物件,呼叫建構函式
return a; //返回一個物件,呼叫複製建構函式
}
int main() {
IntNum p=getNum();
cout<<p.getInt()<<endl;
return 0;
}
輸出:
Calling constructor...
Calling copy constructor...
Destructing...
0
Destructing...
(2)使用移動建構函式,將要返回的區域性物件轉移到主調函式,省去了構造和刪除臨時物件的過程。
#include<iostream>
using namespace std;
class IntNum {
public:
IntNum(int x = 0) : xptr(new int(x)){ //建構函式
cout << "Calling constructor..." << endl;
}
IntNum(const IntNum & n) : xptr(new int(*n.xptr)){//複製建構函式
cout << "Calling copy constructor..." << endl;
//注:•&&是右值引用;•函式返回的臨時變數是右值
}
IntNum(IntNum && n): xptr( n.xptr){ //移動建構函式
n.xptr = nullptr;
cout << "Calling move constructor..." << endl;
}
~IntNum(){ //解構函式
delete xptr;
cout << "Destructing..." << endl;
}
int getInt() { return *xptr; }
private:
int *xptr;
};
//返回值為IntNum類物件
IntNum getNum() {
IntNum a;
return a;
}
int main() {
cout << getNum().getInt() << endl; return 0;
}
輸出:
Calling constructor...
Calling move constructor...
Destructing...
0
Destructing...
上面兩個程式輸出是有問題的,待解決!
字串
string
#include <string>
#include <iostream>
using namespace std;
//根據value的值輸出true或false
//title為提示文字
inline void test(const char *title, bool value)
{
cout << title << " returns "
<< (value ? "true" : "false") << endl;
}
int main() {
string s1 = "DEF";
cout << "s1 is " << s1 << endl;
string s2;
cout << "Please enter s2: ";
cin >> s2;
cout << "length of s2: " << s2.length() << endl;
//比較運算子的測試
test("s1 <= \"ABC\"", s1 <= "ABC");
test("\"DEF\" <= s1", "DEF" <= s1);
//連線運算子的測試
s2 += s1;
cout << "s2 = s2 + s1: " << s2 << endl;
cout << "length of s2: " << s2.length() << endl;
return 0;
}
輸出:
s1 is DEF
Please enter s2: eqwe
length of s2: 4
s1 <= "ABC" returns false
"DEF" <= s1 returns true
s2 = s2 + s1: eqweDEF
length of s2: 7
getline
#include <iostream>
#include <string>
using namespace std;
int main() {
for (int i = 0; i < 2; i++){
string city, state;
getline(cin, city, ',');
getline(cin, state);
cout << "City:" << city << ",State:" << state << endl;
}
return 0;
}
輸出:
San Francisco,the United States
City:San Francisco,State:the United States
Beijing,China
City:Beijing,State:China
習題
(1)以下關於地址和指標的敘述中正確的是
- 可以取變數的地址賦值給同型別的指標變數(對)
- 可以取常量的地址賦值給同型別的指標變數
- 可以取一個指標變數的地址賦給本指標變數,這樣就使得指標變數指向自身
- 所有指標變數如果未賦初值,則自動賦空值NULL
解析:A選項正確。常量儲存在編譯檔案中,不能取地址。B選項錯誤。一個指標變數的地址只能賦給指向這種型別的指標變數,與其本身型別不同,不能賦值,C選項錯誤。未賦初值的指標變數自動賦任意地址值,D選項錯誤。
(2)要定義一個引用變數p使之引用類MyClass的一個物件,正確的定義語句是
- MyClass p=MyClass;
- MyClass p=new MyClass;
- MyClass &p=new MyClass;
- MyClass a, &p=a;(對),別名
(3)在C++的動態儲存分配,下列說法正確的是?
- new和delete是C++語言中專門用於動態記憶體分配和釋放的函式
- 動態分配的記憶體空間也可以被初始化(對)
- 當系統記憶體不夠時,會自動回收不再使用的記憶體單元,因此程式中不必用delete釋放記憶體空間
- 當動態分配記憶體失敗時,系統會立刻崩潰,因此一定要慎用new
分析:選項A,錯,new 與delete是用於動態平衡分配與釋放空間的運算子,不是函式;選項C,錯,要使用delete釋放空間,系統會統一管理,而不用delete釋放的空間,會造成記憶體洩漏,這種程式用的次數多,會造成記憶體耗盡;選項D,錯,不成功,會返回0。
(4)C++11中,&表示左值引用;&&表示右值引用——下列關於左值和右值的說法正確的是 - 表示式等號左邊的是左值,等號右邊的是右值
- 左值是指表示式結束後依然存在的持久物件(對)
- 右值是指表示式結束後依然存在的持久物件
- 可以對右值取地址
分析:左值和右值都是針對表示式而言的,左值是指表示式結束後依然存在的持久物件,右值指表示式結束時就不再存在的臨時物件——顯然右值不可以被取地址。