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。

指針與取指針變量的地址
假設(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ù)組

創(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;
}

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);
代碼示例



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é)果

錯(cuò)誤分析

修改后
#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;
}

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;
}




