c++:-3

PamShao發表於2022-05-05

上一節學習了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的儲存順序為:

\[a00,a01,a02,a03,a10,a11,a12,a13,a20,a21,a22,a23 \]

初始化

(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中,&表示左值引用;&&表示右值引用——下列關於左值和右值的說法正確的是
  • 表示式等號左邊的是左值,等號右邊的是右值
  • 左值是指表示式結束後依然存在的持久物件(對)
  • 右值是指表示式結束後依然存在的持久物件
  • 可以對右值取地址
    分析:左值和右值都是針對表示式而言的,左值是指表示式結束後依然存在的持久物件右值指表示式結束時就不再存在的臨時物件——顯然右值不可以被取地址。

相關文章