第六章 3指針

1 指針數(shù)組

指針數(shù)組的每個(gè)元素都是地址
int *p[2];由p[0] 和p[1]兩個(gè)指針組成

指針數(shù)組和二維數(shù)組區(qū)別

  • 當(dāng)每行的元素個(gè)數(shù)是一樣的時(shí)候,兩者是一樣的
  • 當(dāng)每行的元素個(gè)數(shù)不同,只有指針數(shù)組才能實(shí)現(xiàn)
  • 二維數(shù)組每行的列數(shù)是一樣的,指針數(shù)組每行的列數(shù)沒(méi)有要求
int line1[]={1,0,0};
int line2[]={1,0};
int *pline[2]={line1,line2};
  • 元素訪問(wèn)
    指針數(shù)組的地址不是連續(xù)的,每行內(nèi)元素的地址連續(xù),但行與行不連續(xù),在使用指針訪問(wèn)時(shí),要特別注意


    image.png

2 指針作為函數(shù)參數(shù)

為什么需要指針做參數(shù)?

  • 需要數(shù)據(jù)的雙向傳遞時(shí)(引用也可以達(dá)到此效果)。
    雙向傳遞:將主調(diào)函數(shù)中已經(jīng)定義好的變量地址放在指針中傳給被調(diào)函數(shù),在被調(diào)用的函數(shù)體中,可以通過(guò)被傳過(guò)來(lái)的指針直接對(duì)主調(diào)函數(shù)中的變量進(jìn)行操作
  • 需要傳遞一組數(shù)組,只傳遞首地址效率比較高。

3 指向常量的指針做形參

const int N=6;
void print(const int *p,int n);

指向常量的指針做形參:雙向傳遞變成了單向,通過(guò)指針可以讀取指向的對(duì)象,但不能修改對(duì)象的值。

最小的授權(quán)原則

不要過(guò)多的授權(quán)


4 指針類型的函數(shù)

指針類型的函數(shù):函數(shù)的返回值是指針

指針類型的函數(shù)定義形式

存儲(chǔ)類型 數(shù)據(jù)類型 *函數(shù)名{
//函數(shù)體語(yǔ)句
}

注意事項(xiàng)

  • 不要將非靜態(tài)的局部地址用作函數(shù)的返回值。
    在函數(shù)內(nèi)部聲明一個(gè)局部變量,函數(shù)返回這個(gè)局部變量的地址返回給主函數(shù),就是非法地址
    image.png
  • 在子函數(shù)中通過(guò)動(dòng)態(tài)內(nèi)存分配new操作取得的內(nèi)存地址返回給主函數(shù)是合法有效的,但是內(nèi)存分配和釋放不在同一級(jí)別,要注意不能忘記釋放,避免內(nèi)存泄漏。
    image.png

5 new在局部中申請(qǐng)的內(nèi)存空間,在離開(kāi)局部后不會(huì)被釋放,即離開(kāi)包圍函數(shù)體的大括號(hào)不會(huì)被釋放,要對(duì)應(yīng)用delete函數(shù)釋放?。?!


6 指向函數(shù)的指針/函數(shù)指針

  • 定義形式
    存儲(chǔ)類型 數(shù)據(jù)類型 (*函數(shù)指針名)();
    指針在初始化時(shí)要聲明指向的類型,在函數(shù)指針中, 初始化時(shí)要聲明返回值和形參類型
  • 含義
    函數(shù)指針指向的是程序代碼存儲(chǔ)區(qū)

為什么需要指向函數(shù)的指針?函數(shù)指針的用途

函數(shù)指針的經(jīng)典用途 ——實(shí)現(xiàn)函數(shù)回調(diào)

  • 通過(guò)函數(shù)指針調(diào)用的函數(shù)
    例如將函數(shù)的指針作為參數(shù)傳遞給一個(gè)函數(shù),使得在處理相似事件的時(shí)候可以靈活使用不同的方法
  • 調(diào)用者不關(guān)心誰(shuí)是被調(diào)用者
    需知道存在一個(gè)具有特定原型和限制條件的被調(diào)函數(shù)(知道知道這個(gè)函數(shù)的參數(shù)表和返回值類型)
    函數(shù)原型則特指包括說(shuō)明參數(shù)類型的函數(shù)聲明。

函數(shù)指針舉例

#include<iostream>
using namespace std;
int compute(int a, int b, int (*func)(int,int) ){
    //獲得了func函數(shù)的地址,就可以訪問(wèn)這個(gè)函數(shù)
    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 add(int a,int b)
{
    return a+b;
}
int main(){
    int a,b,res;
     cin>>a>>b;
    res=compute(a,b,&max);
    //res=compute(a,b,max);不加&也可以,函數(shù)名本身就代表函數(shù)代碼的起始地址,加&只是更清晰
    return 0;
}

7數(shù)組名與數(shù)組名前加取地址符

對(duì)數(shù)組int a[10]來(lái)講,a和&a都表示地址,準(zhǔn)確來(lái)講它們不是地址,而是指針(地址基本上算是錯(cuò)誤的說(shuō)法)!

雖然,指針的內(nèi)容即指向的地址相同,但指針類型不同:
a是指向數(shù)組元素int的指針int,指針操作的基本單位為4Bytes;
而&a是指向數(shù)組a[10]的指針int(
)[10],指針操作的基本單位為40Bytes。

image.png

指針與取指針變量的地址

假設(shè)0x80000地址的值5,int p ,p這個(gè)變量在內(nèi)存中的地址為0x8abcd,
那么 p = 0x80000;
p = 5; &p =0x8abcd;
p是變量i的地址,相當(dāng)于一維指針
&p是變量p的地址,相當(dāng)于一個(gè)二維指針

對(duì)函數(shù)指針來(lái)說(shuō),用指針名和&指針名代表的地址都是一樣的


8 區(qū)分函數(shù)指針和指針函數(shù)

定義不同

指針函數(shù)本質(zhì)是一個(gè)函數(shù),其返回值為指針。
函數(shù)指針本質(zhì)是一個(gè)指針,其指向一個(gè)函數(shù)。

寫法不同

指針函數(shù):int* fun(int x,int y);
函數(shù)指針:int (*fun)(int x,int y);


9 對(duì)象指針

對(duì)象指針:指向?qū)ο蟮闹羔?/p>

  • 形式
    類型 *對(duì)象指針
    例:
 Point a(5,10);
    Point *ptr;
    ptr=&a;
  • 訪問(wèn)對(duì)象的元素
 對(duì)象指針名->成員名      ==(*對(duì)象指針名).成員名

對(duì)象指針與前向引用結(jié)合

class Fred;
class Barney {
    Fred x;//錯(cuò)誤,在給出Fred的具體細(xì)節(jié)之前不能使用Fred來(lái)聲明一個(gè)對(duì)象
};
class Fred {
    Barney y;
};
class Fred;
class Barney {
    Fred *x;//正確,只是聲明了一個(gè)指向Fred類的指針,不需要知道Fred類的具體細(xì)節(jié)
};
class Fred {
    Barney y;
};

10 this 指針

問(wèn)題描述:

當(dāng)前類中有多個(gè)對(duì)象,當(dāng)某個(gè)對(duì)象調(diào)用getX()函數(shù)時(shí),getX()要返回哪個(gè)對(duì)象的成員x給當(dāng)前對(duì)象?

解決:

this指針:指向當(dāng)前對(duì)象自己的指針

  • 隱含與類的每一個(gè)非靜態(tài)成員函數(shù)中
  • 指出成員函數(shù)所操作的對(duì)象
    當(dāng)通過(guò)一個(gè)對(duì)象調(diào)用成員函數(shù)時(shí),系統(tǒng)先將該對(duì)象的地址賦給this指針,然后調(diào)用成員函數(shù),成員函數(shù)對(duì)對(duì)象的數(shù)據(jù)成員進(jìn)行操作時(shí),就隱含使用了this指針
  • 例如 Point類的getX函數(shù)中的語(yǔ)句:
return x;
相當(dāng)于
return this->x;

11 動(dòng)態(tài)內(nèi)存分配

當(dāng)必須用動(dòng)態(tài)內(nèi)存分配的方式去申請(qǐng)內(nèi)存單元時(shí),那么動(dòng)態(tài)申請(qǐng)的單元沒(méi)有機(jī)會(huì)給它起變量名,返回一個(gè)首地址

動(dòng)態(tài)申請(qǐng)內(nèi)存操作符 new

new 類型名T(初始化參數(shù)列表)//給一個(gè)對(duì)象申請(qǐng)空間,并在()內(nèi)初始化,其中()可以省略
new 類型名T [數(shù)組個(gè)數(shù)]//申請(qǐng)一個(gè)容納多個(gè)對(duì)象的內(nèi)存空間

  • 功能:
    在程序執(zhí)行期間,申請(qǐng)用于存放T類型對(duì)象的內(nèi)存空間,并依初值列表賦以初值
  • 結(jié)果值:
    成功:返回T類型的指針,指向的是新分配內(nèi)存空間的起始地址
    失敗:拋出異常

釋放內(nèi)存操作符delete

delete 指針p

  • 功能:釋放指針p所指向的內(nèi)存。p必須是new操作的返回值
  • 注意,new和delete必須配套使用,delete只能釋放new申請(qǐng)的空間,不能釋放用其他方式(比如malloc)申請(qǐng)的空間
#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() { return x; }
    int getY() { 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;//沒(méi)有初始化,調(diào)用的是默認(rèn)構(gòu)造函數(shù)
    delete ptr1;//刪除對(duì)象,自動(dòng)調(diào)用析構(gòu)函數(shù),刪除的是指向的對(duì)象不是指針,指針在main函數(shù)結(jié)束后自動(dòng)被釋放
    cout << "Step two:" << endl;
    ptr1 = new Point(1,2);//有初始化,調(diào)用的是構(gòu)造函數(shù)
    delete ptr1;
    return 0;
}

結(jié)果

Step one:
Default Constructor called.
Destructor called.
Step two:
Constructor called.
Destructor called.

申請(qǐng)和釋放動(dòng)態(tài)數(shù)組

  • 分配: new 類型名T[數(shù)組長(zhǎng)度]
    • 數(shù)組長(zhǎng)度可以是任何整數(shù)類型表達(dá)式,在運(yùn)行時(shí)計(jì)算
  • 釋放:delete[] 數(shù)組名p
  • 釋放指針p所指向的數(shù)組,必須加方括號(hào)[],不加方括號(hào)僅僅會(huì)釋放數(shù)組首元素的地址,p必須使用new分配得到的數(shù)組首地址
#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() { return x; }
    int getY() { return y; }
    void move(int newX, int newY) {
        x = newX;
        y = newY;
    }
private:
    int x, y;
};
int main() {
    cout << "Step one:" << endl;
    Point* ptr = new Point[2];//創(chuàng)建對(duì)象數(shù)組,調(diào)用的都是默認(rèn)構(gòu)造函數(shù)
        ptr[0].move(5,10);//通過(guò)指針訪問(wèn)數(shù)組元素的成員
        ptr[1].move(5,20);
    cout << "Deleting   " << endl;
    delete[] ptr;
    return 0;
}

結(jié)果

Step one:
Default Constructor called.
Default Constructor called.
Deleting
Destructor called.
Destructor called.

int a[10]和int *p=new int[10]區(qū)別

  • 在函數(shù)中 創(chuàng)建int a[10],不能返回這個(gè)數(shù)組的首地址,因?yàn)樗且粋€(gè)局部變量,在函數(shù)執(zhí)行完后,內(nèi)存就會(huì)自動(dòng)被釋放
  • 在函數(shù)中 創(chuàng)建int *p=new int[10] 可以返回p,因?yàn)閚ew開(kāi)辟的空間只有delete才會(huì)釋放,系統(tǒng)不會(huì)自動(dòng)釋放

動(dòng)態(tài)創(chuàng)建多維數(shù)組(行不定,列確定)用數(shù)組指針,數(shù)組元素的地址是連續(xù)的

new 類型名T[第一維長(zhǎng)度][第二維長(zhǎng)度]

  • 如果內(nèi)存申請(qǐng)成功,new運(yùn)算返回一個(gè)指向新分配內(nèi)存首地址的指針(地址是連續(xù)的)。
  • 這種申請(qǐng)方式下,第二維長(zhǎng)度必須是一個(gè)常量,不能是變量
  • 例如
    char (*fp)[3];//數(shù)組指針,指向的是一個(gè)字符數(shù)組(整個(gè)數(shù)組 ),3為數(shù)組的長(zhǎng)度,當(dāng)fp+1時(shí)需要跨越3個(gè)字符數(shù)據(jù)的長(zhǎng)度
    fp=new char[2][3];


多維數(shù)組的行和列數(shù)都不確定 用指針數(shù)組,數(shù)組元素的地址不連續(xù)的

創(chuàng)建

int c=8;//列
int r=7;//行
int **p=new int*[r];//第一次動(dòng)態(tài)內(nèi)存分配,生成的是一個(gè)指針數(shù)組,數(shù)組的元素都是指針,指向的是一個(gè)一維數(shù)組的首地址
for(int i=0;i<r;i++){
    p[i]=new int[c];//第二次內(nèi)存分配,依次生成r個(gè)一位數(shù)組, p[i]指向的是第i行的首地址
//二維數(shù)組p的地址不是連續(xù)的,不能用p++來(lái)遍歷所有元素
}

釋放

for(int i=0;i<r;i++){
    delete[]  p[i];//加[]是因?yàn)閜[i]也是一個(gè)數(shù)組,p[i]指向的是數(shù)組的首地址
}
delete [] p;

創(chuàng)建三維數(shù)組

image.png

創(chuàng)建動(dòng)態(tài)多維數(shù)組總結(jié)

創(chuàng)建行不定 列確定和行不定 列也不確定的二維數(shù)組其實(shí)本質(zhì)上沒(méi)什么區(qū)別:申請(qǐng)空間時(shí),必須要確定申請(qǐng)空間的最小單元。

  • 行不定,列確定。
//最小單元是一個(gè)固定長(zhǎng)度一維數(shù)組
//每次申請(qǐng)一個(gè)確定的一維數(shù)組int[常量]大小的空間,根據(jù)需求動(dòng)態(tài)申請(qǐng)多個(gè)這樣的連續(xù)空間
int (*p)[常量]=new int[變量][常量]
  • 行不定,列不定。
//最小單元是一個(gè)指針
//每一次申請(qǐng)一個(gè)指針大小的空間,根據(jù)需求申請(qǐng)多個(gè)這樣連續(xù)的空間,即動(dòng)態(tài)指針數(shù)組
int **p=new int *[變量1]
//然后再給動(dòng)態(tài)指針數(shù)組的每個(gè)元素所指針的地址動(dòng)態(tài)申請(qǐng)空間
for(int i=0;i<變量1;i++){
    p[i]=new int[變量2];
}
//這樣數(shù)組p[變量1][變量2]就創(chuàng)建好了

12 將動(dòng)態(tài)數(shù)組封裝成類(難點(diǎn),多看看)

好處:

  • 更加簡(jiǎn)潔,便于管理
  • 可以在訪問(wèn)數(shù)組元素前檢查是否越界
#include<iostream>
#include <cassert>
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() { return x; }
    int getY() { return y; }
    void move(int newX, int newY) {
        x = newX;
        y = newY;
    }
private:
    int x, y;
};
class ArrayOfPoints {//動(dòng)態(tài)數(shù)組類
public:
    ArrayOfPoints(int size) :size(size) {
        points = new Point[size];
    }
    ~ArrayOfPoints() {
        delete[] points;
    }
    //返回?cái)?shù)組中下標(biāo)為index的元素,返回值類型是引用,之所以用引用是想在后邊改變返回值數(shù)組元素
    Point& element(int index) {
        assert(index >= 0 && index < size);//斷言,如果它的條件返回錯(cuò)誤,注意是錯(cuò)誤,則終止程序執(zhí)行
        return points[index];//返回?cái)?shù)組中下標(biāo)為index的元素
    }
private:
    Point* points;
    int size;
};
int main() {
    int count;
    cout << "Please enter the count of points:" << endl;
    cin >> count;
    ArrayOfPoints poinst(count);
    poinst.element(0).move(5, 0);
    poinst.element(1).move(15, 20); 
    return 0;
}
image.png

13 智能指針

C++11提供了一種智能指針,它提供了一種垃圾回收制度
智能指針有三種

  • unique_ptr:
    不允許多個(gè)指針共享資源,這個(gè)指針指向的空間只能由它自己所指向,不允許其它指針指向這個(gè)地址,可以用標(biāo)準(zhǔn)庫(kù)中的move函數(shù)轉(zhuǎn)移指針,把這個(gè)指針中的地址轉(zhuǎn)移到別的指針中,原指針失效
  • shared_ptr
    多個(gè)指針共享資源,多個(gè)share_ptr指針可以指向同一個(gè)內(nèi)存單元
  • weak_ptr
    可復(fù)制share_ptr指針?biāo)赶虻膯卧?,但其?gòu)造或釋放對(duì)資源不產(chǎn)生影響

14 vector類(是一個(gè)類)

vector對(duì)象是C++標(biāo)準(zhǔn)庫(kù)里面的一個(gè)類模板

為什么需要vector?

  • vector類封裝成任何類型的動(dòng)態(tài)數(shù)組,自動(dòng)創(chuàng)建和刪除,不需要像前面第12小節(jié)一樣要對(duì)不同的類型封裝成不同的類,前面來(lái)一個(gè)新的類型,就要寫一個(gè)新的類來(lái)封裝它
  • 數(shù)組下標(biāo)越界檢查
  • 需要注意的是vector運(yùn)行效率比較低,在刷一些算法題時(shí)最好不好用vector

vector對(duì)象的定義

vector<數(shù)據(jù)類型>數(shù)組對(duì)象名(數(shù)組長(zhǎng)度);

例如:

vector<int >arr(5);

vector對(duì)象的訪問(wèn)

  • 對(duì)數(shù)組元素的引用
對(duì)象數(shù)組名[下標(biāo)]
arr[2];
  • 獲得數(shù)組長(zhǎng)度(vector類中的成員函數(shù)size())
對(duì)象數(shù)組名.size()

vector類作為函數(shù)參數(shù)

  • 值傳遞,在函數(shù)中不能改變a的值,會(huì)發(fā)生拷貝構(gòu)造
//聲明:
void fun1(vector<int>vec);
//調(diào)用
fun1(vec);
  • 地址傳遞,在函數(shù)中可以改變a的值,不會(huì)發(fā)生拷貝構(gòu)造
//引用傳遞,不會(huì)發(fā)生拷貝構(gòu)造
void fun2(vector<int>&vec);
fun2(vec);
//指針傳遞
void fun2(vector<int>*vec);
fun2(&vec);

代碼示例

image.png

image.png

image.png

v.begin();是一個(gè)迭代器
auto是自動(dòng)類型


更多vector成員函數(shù)的介紹

更多vector類的成員函數(shù)介紹可以借鑒下面這篇博客
https://blog.csdn.net/weixin_41743247/article/details/90635931


15 深層復(fù)制與淺層復(fù)制

淺層復(fù)制

  • 實(shí)現(xiàn)對(duì)象間數(shù)據(jù)元素的一一對(duì)應(yīng)復(fù)制
  • 對(duì)于指針,是將指針中存放的地址復(fù)制過(guò)去
//淺層復(fù)制
int *i=new int(10);
int *j=i;//i和j指向的是同一塊內(nèi)存
  • 默認(rèn)復(fù)制構(gòu)造函數(shù)實(shí)現(xiàn)的是淺層復(fù)制

深層復(fù)制

  • 當(dāng)被復(fù)制的對(duì)象數(shù)據(jù)成員時(shí)指針類型時(shí)(不管是指針數(shù)組還是單一指針都要進(jìn)行深層復(fù)制),不是復(fù)制該指針成員本身,而是將該指針?biāo)笇?duì)象進(jìn)行復(fù)制
  • 深層復(fù)制會(huì)新開(kāi)辟一個(gè)空間,這個(gè)空間存放的就是要被復(fù)制的對(duì)象,再用指針指向這個(gè)空間
  • 深層復(fù)制就是給指針對(duì)象開(kāi)辟一個(gè)新的空間,用來(lái)存放這個(gè)指針對(duì)象的拷貝
//深層復(fù)制
int *i=new int(2);//動(dòng)態(tài)內(nèi)存分配  new 數(shù)據(jù)類型(初始化列表)
int *j=new int(*i);//j和i指向?qū)ο蟮闹迪嗤?,但指向的不是同一塊內(nèi)存

代碼示例

#include<iostream>
#include <cassert>
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() { return x; }
    int getY() { return y; }
    void move(int newX, int newY) {
        x = newX;
        y = newY;
    }
private:
    int x, y;
};
class ArrayOfPoints {//動(dòng)態(tài)數(shù)組類
public:
    ArrayOfPoints(int size) :size(size) {
        points = new Point[size];
    }
    ~ArrayOfPoints() {
        delete[] points;
    }
    //返回?cái)?shù)組中下標(biāo)為index的元素,返回值類型是引用,之所以用引用是想在后邊改變返回值數(shù)組元素
    Point& element(int index) {
        assert(index >= 0 && index < size);//斷言,如果它的條件返回錯(cuò)誤,注意是錯(cuò)誤,則終止程序執(zhí)行
        return points[index];//返回?cái)?shù)組中下標(biāo)為index的元素
    }
private:
    Point* points;
    int size;
};
int main() {
    int count;
    cout << "Please enter the count of points:" << endl;
    cin >> count;
    ArrayOfPoints poinst1(count);
    poinst1.element(0).move(5, 0);
    poinst1.element(1).move(15, 20);
    ArrayOfPoints poinst2(poinst1);//用默認(rèn)構(gòu)造函數(shù)創(chuàng)建副本
    cout << "copy of poinst1" << endl;
    cout << poinst2.element(0).getX() << "," << poinst2.element(0).getY() << endl;
    cout << poinst2.element(1).getX() << "," << poinst2.element(1).getY() << endl;
        return 0;
}

結(jié)果


image.png

錯(cuò)誤分析


image.png

修改后

#include<iostream>
#include <cassert>
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() { return x; }
    int getY() { return y; }
    void move(int newX, int newY) {
        x = newX;
        y = newY;
    }
private:
    int x, y;
};
class ArrayOfPoints {//動(dòng)態(tài)數(shù)組類
public:
    ArrayOfPoints(int size) :size(size) {
        points = new Point[size];
    }
    ~ArrayOfPoints() {
        delete[] points;
    }
    ArrayOfPoints(const ArrayOfPoints& v) {
        size = v.size;
        //復(fù)制時(shí),開(kāi)辟新的空間,將參數(shù)數(shù)組的值一一復(fù)制給當(dāng)前新數(shù)組
        points = new Point[size];
        for (int i = 0; i < size; i++) {
            points[i] = v.points[i];
        }
    }
    //返回?cái)?shù)組中下標(biāo)為index的元素,返回值類型是引用,之所以用引用是想在后邊改變返回值數(shù)組元素
    Point& element(int index) {
        assert(index >= 0 && index < size);//斷言,如果它的條件返回錯(cuò)誤,注意是錯(cuò)誤,則終止程序執(zhí)行
        return points[index];//返回?cái)?shù)組中下標(biāo)為index的元素
    }
private:
    Point* points;
    int size;
};
int main() {
    int count;
    cout << "Please enter the count of points:" << endl;
    cin >> count;
    ArrayOfPoints poinst1(count);
    poinst1.element(0).move(5, 0);
    poinst1.element(1).move(15, 20);
    ArrayOfPoints poinst2(poinst1);//用默認(rèn)構(gòu)造函數(shù)創(chuàng)建副本
    cout << "copy of poinst1" << endl;
    cout << poinst2.element(0).getX() << "," << poinst2.element(0).getY() << endl;
    cout << poinst2.element(1).getX() << "," << poinst2.element(1).getY() << endl;
    return 0;
}
image.png

16 移動(dòng)構(gòu)造

  • C++11標(biāo)準(zhǔn)提供了一種新的構(gòu)造方法——移動(dòng)構(gòu)造
  • C++11之前,如果想要將源對(duì)象的狀態(tài)轉(zhuǎn)移到目標(biāo)對(duì)象只能通過(guò)復(fù)制。在某些情況下,我們沒(méi)有必要復(fù)制對(duì)象——只需要移動(dòng)他們
  • C++11引入移動(dòng)語(yǔ)義:源對(duì)象資源的控制權(quán)全部交給目標(biāo)對(duì)象
    image.png

什么時(shí)候需要移動(dòng)構(gòu)造?

如果這個(gè)對(duì)象即將消亡,并且它的資源需要被再利用

移動(dòng)構(gòu)造函數(shù)

類名(類名&&)
(普通的復(fù)制構(gòu)造 類名(const 類名&))

代碼示例

#include<iostream>
using namespace std;
class IntNum {
public:
    IntNum(int x = 1) :xptr(new int(x)) {
        cout << "Calling constructor ..." << endl;
    }
    //在初始化列表中賦值別用等號(hào) 用()
//類里面只有數(shù)據(jù)成員會(huì)占用空間,成員函數(shù)不占用空間(成員函數(shù)放在了代碼空間)
    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 getNum() {
    //調(diào)用的是默認(rèn)構(gòu)造函數(shù)
    IntNum a;
    //局部變量a在函數(shù)運(yùn)行結(jié)束后就會(huì)消亡,return時(shí)會(huì)調(diào)用復(fù)制構(gòu)造函數(shù),產(chǎn)生一個(gè)臨時(shí)無(wú)名對(duì)象,這個(gè)臨時(shí)無(wú)名對(duì)象時(shí)局部變量a的一個(gè)備份
    return a;
}
int main() {
    cout << getNum().getInt() << endl;
    return 0;
}

結(jié)果

Calling constructor ...
Calling copy constructor ...
Destructing ...
1
Destructing ...

移動(dòng)構(gòu)造

#include<iostream>
using namespace std;
class IntNum {
public:
    IntNum(int x = 1) :xptr(new int(x)) {
        cout << "Calling constructor ..." << endl;
    }
    //(不能加const修飾,因?yàn)橐淖冏兞浚┮苿?dòng)構(gòu)造,右值引用,要消亡的局部變量的所占用的資源轉(zhuǎn)讓給臨時(shí)對(duì)象
    IntNum(IntNum&& n) :xptr(n.xptr) {
        n.xptr = nullptr;//現(xiàn)在xptr和n.ptr都指向同一個(gè)對(duì)象,可以讓本要消亡的對(duì)象指向空,把資源讓給需要的
        cout << "Calling move constructor ..." << endl;
    }
    ~IntNum() {
        delete xptr;
        cout << "Destructing ..." << endl;
    }
    int getInt() { return *xptr; }

private:
    int* xptr;
};
IntNum getNum() {
    //調(diào)用的是默認(rèn)構(gòu)造函數(shù)
    IntNum a;
    //局部變量a在函數(shù)運(yùn)行結(jié)束后就會(huì)消亡,return時(shí)會(huì)調(diào)用復(fù)制構(gòu)造函數(shù),產(chǎn)生一個(gè)臨時(shí)無(wú)名對(duì)象,這個(gè)臨時(shí)無(wú)名對(duì)象時(shí)局部變量a的一個(gè)備份
    return a;
}
int main() {
    cout << getNum().getInt() << endl;
    return 0;
}

結(jié)果

Calling constructor ...
Calling move constructor ...//只是用到了淺層復(fù)制構(gòu)造 效率比較高
Destructing ...
1
Destructing ...

17 左值、右值、臨時(shí)變量和無(wú)名對(duì)象

左值引用與右值引用

C++對(duì)于左值和右值沒(méi)有標(biāo)準(zhǔn)定義,但是有一個(gè)被廣泛認(rèn)同的說(shuō)法:

  • 可以取地址的,有名字的,非臨時(shí)的就是左值;
    格式 : 類型 & 引用名 = 左值表達(dá)式;
  • 不能取地址的,沒(méi)有名字的,臨時(shí)的就是右值;
    格式 : 類型 && 引用名 = 右值表達(dá)式;
  • 可見(jiàn)立即數(shù)(常量,例如 1 2,“s”),函數(shù)返回的值等都是右值;而非匿名

臨時(shí)變量

什么是臨時(shí)變量?
  • C++真正的臨時(shí)對(duì)象是不可見(jiàn)的匿名對(duì)象,不會(huì)出現(xiàn)在你的源碼中,但是程序在運(yùn)行時(shí)確實(shí)生成了這樣的對(duì)象。
什么時(shí)候會(huì)產(chǎn)生臨時(shí)變量?

(1)當(dāng)函數(shù)返回對(duì)象的時(shí)候
(2)為了使函數(shù)調(diào)用成功而進(jìn)行隱式類型轉(zhuǎn)換的時(shí)候

什么是無(wú)名對(duì)象?

無(wú)名對(duì)象,也成臨時(shí)對(duì)象(注意不是臨時(shí)變量)指的是直接由構(gòu)造函數(shù)產(chǎn)生,但是沒(méi)有被任何符號(hào)所引用的對(duì)象。例如:string("abc"),這句話產(chǎn)生的就是一個(gè)無(wú)名對(duì)象,這個(gè)對(duì)象產(chǎn)生以后,沒(méi)有什么辦法使用它。但是對(duì)于string str("abc")來(lái)說(shuō),則產(chǎn)生的是一個(gè)有名字的對(duì)象,他的名字就是 str。

臨時(shí)變量和臨時(shí)對(duì)象區(qū)別

臨時(shí)對(duì)象指代在不具名內(nèi)存中產(chǎn)生的匿名對(duì)象,特點(diǎn)為右值,結(jié)束其表達(dá)式無(wú)價(jià)值。臨時(shí)變量指代為某算法而引入的一些臨時(shí)性的中間變量,特點(diǎn)為離開(kāi)其作用域無(wú)價(jià)值。

//注意,由于tmp是局部變量,離開(kāi)函數(shù)時(shí)會(huì)進(jìn)行消亡,return時(shí)編譯器會(huì)對(duì)象進(jìn)行復(fù)制d構(gòu)造,產(chǎn)生一個(gè)臨時(shí)的無(wú)名對(duì)象。這里T不能是不完整類型。
template<typename T>
T function()
{
T tmp;
return tmp;
}
C++0x標(biāo)準(zhǔn)中引入新的右值引用類型,可以直接返回臨時(shí)對(duì)象而不進(jìn)行復(fù)制構(gòu)造:
std::vector<int>&& func()
{
std::vector<int> retv;
// fill retv
return retv;
}

理解的還是不太清楚,反正要記住的就是return 返回一個(gè)局部變量時(shí),會(huì)自動(dòng)拷貝一份局部變量,拷貝產(chǎn)生的這個(gè)變量就是臨時(shí)變量,return返回這個(gè)臨時(shí)變量

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容