邢臺(tái)網(wǎng)站優(yōu)化建設(shè)站長(zhǎng)查詢(xún)域名
1. 內(nèi)存分析診斷工具:valgrind;
2. 內(nèi)存管理的兩種方式:
? ? ? ? ①用戶(hù)管理:自己申請(qǐng)的,自己用,自己回收;效率高,但容易導(dǎo)致內(nèi)存泄漏;
? ? ? ? ②系統(tǒng)管理:系統(tǒng)自動(dòng)回收垃圾;安全性高,但消耗內(nèi)存資源;
3. 內(nèi)存分配的兩種方式:
(1)靜態(tài)分配(數(shù)組),存在于??臻g;
? ? ? ??①優(yōu)點(diǎn):物理地址連續(xù),方便遍歷;
? ? ? ? ②缺點(diǎn):分配與利用效率較低;
(2)動(dòng)態(tài)分配(malloc、new),存在于堆空間;
? ? ? ??①優(yōu)點(diǎn):使用效率較高;
? ? ? ? ②缺點(diǎn):物理地址不連續(xù);
4. 防止內(nèi)存泄漏的方法:
? ? ? ? ①養(yǎng)成良好的編碼規(guī)范;
? ? ? ? ②使用指針時(shí),初始化就置空,避免野指針;
? ? ? ? ③malloc后及時(shí)free;new后及時(shí)delete;
? ? ? ? ④利用內(nèi)存檢測(cè)工具,如valgrind進(jìn)行輔助處理;且可以利用g++選項(xiàng)工具同步分析;
5. 為什么c++沒(méi)有GC機(jī)制?
? ? ? ??①?zèng)]有共同的基類(lèi):c++從C語(yǔ)言演變而來(lái),可以直接操作指針,且類(lèi)型之間可以相互轉(zhuǎn)換,對(duì)于一個(gè)指針無(wú)法直到它實(shí)際指向類(lèi)型;
? ? ? ? ②系統(tǒng)開(kāi)銷(xiāo)比較大:垃圾回收帶來(lái)的系統(tǒng)開(kāi)銷(xiāo),需要占用更多的內(nèi)存,違反了c++的設(shè)計(jì)哲學(xué)“不為不必要的功能支付代價(jià)”,不符合高效特性;
? ? ? ? ③且c++中有析構(gòu)函數(shù)實(shí)現(xiàn)智能指針,通過(guò)引用計(jì)數(shù)來(lái)管理資源的釋放,對(duì)GC的需求不迫切;
6. 智能指針
? ? (1)智能指針的作用:幫助開(kāi)發(fā)者對(duì)動(dòng)態(tài)分配的對(duì)象進(jìn)行聲明周期管理,可以有效地防止內(nèi)存泄漏;
? ? (2)分類(lèi),可以分為三類(lèi)
? ? ? ??①u(mài)nique_ptr:獨(dú)占指針
? ? ? ? ②shared_ptr:共享指針
? ? ? ? ③weak_ptr:弱指針
? ? (3)shared_ptr:共享指針
? ? ? ??①共享:多個(gè)指針可以同時(shí)指向同一個(gè)對(duì)象,當(dāng)最后一個(gè)指針被銷(xiāo)毀或者指向其他對(duì)象時(shí),這個(gè)對(duì)象會(huì)被釋放;
? ? ? ? ②工作原理:內(nèi)部有計(jì)數(shù)器,實(shí)時(shí)記錄該共同空間被幾個(gè)智能指針指向;其本質(zhì)是對(duì)裸指針進(jìn)行分裝,下有封裝例子;
? ? ? ? ③導(dǎo)致引用計(jì)數(shù)增加的情況:用一個(gè)智能指針初始化另一個(gè)智能指針、函數(shù)傳參(傳遞一個(gè)智能指針,即復(fù)制),函數(shù)返回值(返回一個(gè)智能指針);
? ? ? ? ④引用計(jì)數(shù)減少的情況:給智能指針賦予新值指向一個(gè)新的對(duì)象、局部的智能指針離開(kāi)作用域;
? ? ? ??⑤語(yǔ)法:(注意:初始化不可以用 “ = ” 號(hào)),有兩種初始化的方法(手動(dòng)初始化、函數(shù)初始化std::make_shared函數(shù))
shared_ptr<int>pi(new int(5)); //(初始化1)內(nèi)部調(diào)用的構(gòu)造函數(shù)
shared_ptr<int>pi = new int(5); //錯(cuò)誤用法!
auto p = std::make_shared<string>("hello world"); //(初始化2)函數(shù)方法
int *p = pi.get(); //獲得pi的裸指針,注意智能指針一旦釋放,裸指針也就失效;
pi.reset(); //將pi置空;
pi.use_count(); //返回該智能指針的引用計(jì)數(shù);
pi.reset(new int (6)); //改變pi的指向;
delete p; //釋放;
delete p; //釋放數(shù)組指針;void test(shared_ptr<int>&p) //智能指針作為形參
{}reinterpret_pointer_cast<>(); //可以將任意類(lèi)型的指針進(jìn)行轉(zhuǎn)換;
? ? ? ? ⑥自定義刪除器,一些情況下,默認(rèn)刪除器處理不了(shared_ptr管理動(dòng)態(tài)數(shù)組),需要自己指定刪除器,下有詳細(xì)例子;
? ? ? ??⑧指針類(lèi)型轉(zhuǎn)換函數(shù)
static_pointer_cast //void*與裸指針轉(zhuǎn)換
dynamic_pointer_cast //向下類(lèi)型轉(zhuǎn)型(基類(lèi)→派生類(lèi)),基類(lèi)需要有虛函數(shù);
const_pointer_cast //去除裸指針的const屬性;
reinterpret_pointer_cast //任意類(lèi)型之間裸指針轉(zhuǎn)換;
? ? (4)unique_ptr:獨(dú)占指針
? ? ? ??①同一時(shí)刻,只能有一個(gè)unique_ptr指向這個(gè)對(duì)象,當(dāng)指針?shù)N毀,指向的對(duì)象也銷(xiāo)毀;
? ? ? ??②語(yǔ)法,(注意:初始化不可以用 “ = ” 號(hào)),有兩種初始化的方法(手動(dòng)初始化、函數(shù)初始化std::make_unique函數(shù))
unique_ptr<int>p(new int(5));
unique_ptr<int>p = new int(5); //錯(cuò)誤用法:不可以使用=
atuo pi = std::make_unique<string>("hello world");
? ? ? ? ③其他操作方法例如reset()、get()等于shared_ptr一樣;
? ? (5)weak_ptr:獨(dú)占指針
? ? ? ??①weak_ptr是弱指針,不是獨(dú)立的指針,不能單獨(dú)操作所指向的資源;
? ? ? ? ②用處一:weak_ptr輔助shared_ptr的使用(監(jiān)視shared_ptr 指向?qū)ο蟮纳芷?#xff09;;
? ? ? ? ③用處二:weak_ptr和shared_ptr之間可以相互轉(zhuǎn)換,shared_ptr可以賦給weak_ptr,反過(guò)來(lái)不可以;
????? (6)自己封裝的智能指針shared_ptr<>();
hpp:
#pragma once
#include <iostream>
using namespace std;
//自己封裝的智能指針shared_ptr<>();
template <typename T>
class Smartpointer
{
public:template <typename U>friend class Smartpointer; //聲明一種友元類(lèi)Smartpointer() : m_p(nullptr){//m_p = nullptr;m_count = new long(0);}explicit Smartpointer(T * p) //禁止隱式轉(zhuǎn)換,就是不可以使用=,只能使用()初始化{cout<<"explicit"<<endl;if(p != nullptr){m_p = p;m_count = new long(1); //次數(shù)計(jì)為1}else{m_p = nullptr;m_count = new long(0); //次數(shù)為0}}template <typename U>Smartpointer(T *p, Smartpointer<U> &other) //另外一種類(lèi)型U?{if(p != nullptr){m_p = other.m_p;m_count = other.m_count;(*m_count)++;}else{m_p = nullptr;m_count = other.m_count;}}Smartpointer(Smartpointer<T> &other) //拷貝構(gòu)造{cout<<"拷貝構(gòu)造"<<endl;if(other.m_p != nullptr){m_p = other.m_p;m_count = other.m_count;(*m_count)++;}else{m_p = nullptr;m_count = other.m_count;}}Smartpointer<T> & operator=(const Smartpointer<T> &other) //=重載{cout<<"operator="<<endl;if(other.m_p != nullptr){m_p = other.m_p;m_count = other.m_count;(*m_count)++;}else{m_p = other.m_p;m_count = other.m_count;}return *this;}Smartpointer(Smartpointer<T>&&other) //移動(dòng)構(gòu)造{cout<<"移動(dòng)構(gòu)造"<<endl;if(other.m_p = nullptr){m_p = other.m_p;other.m_p = nullptr;m_count = other.m_count;other.m_count = nullptr;}else{m_p = nullptr;m_count = other.m_count;}}~Smartpointer() //析構(gòu)函數(shù){if((m_p != nullptr) && (--(*m_count) == 0)){cout<<"~Smartpointer"<<endl;delete m_p;delete m_count;}else{m_p = nullptr;m_count = nullptr;}}long use_count() const //函數(shù)后面加const的作用?{return *m_count;}T * get() //返回裸指針{return m_p;}void reset(T *p){if(p = nullptr){m_p = p;(*m_count)--;m_count = new long(1); //引用計(jì)數(shù)從新置為1}else{m_p = nullptr;m_count = new long(0);}}void reset(){m_p = nullptr;(*m_count)--;}void swap(Smartpointer<T> &other){std::swap(m_p,other.m_p); //c庫(kù)內(nèi)置函數(shù)實(shí)現(xiàn)交換std::swap(m_count,other.m_count);}T operator*(){ cout<<"operator*"<<endl;return *m_p;}T * operator->(){return m_p;}operator bool(){return m_p != nullptr;}T operator[](int index) //重載[]{return m_p[index];}template <typename T1, typename U1>friend Smartpointer<T1> static_pointer_cast(Smartpointer<U1>&other); //聲明一種友元函數(shù),用于不同類(lèi)型的智能指針轉(zhuǎn)換private:T * m_p; //裸指針long * m_count; //引用計(jì)數(shù)指針
};template <typename T, typename U>
Smartpointer<T> static_pointer_cast(Smartpointer<U>&other)
{T * temp = static_cast<T *>(other.m_p);return Smartpointer(temp,other);
}
cpp:
#include "smart_pointer.hpp"
#include <iostream>
#include <string>using namespace std;int main(int argc, char **argv)
{Smartpointer<int>t1(new int (5));cout<<(*t1)<<endl;cout<<t1.use_count()<<endl;Smartpointer<int>t2(t1);cout<<t2.use_count()<<endl;cout<<t1.use_count()<<endl;Smartpointer<string>t3(new string ("hello world"));// Smartpointer<string>t2(t4,t3);//Smartpointer<int> static_pointer_cast(t3); //指針類(lèi)型轉(zhuǎn)換沒(méi)有成功?Smartpointer<int>t4 = t2;cout<<(*t4)<<endl;cout<<t4.use_count()<<endl;Smartpointer<int>t5(std::move(t4));return 0;
}
7. 內(nèi)存池
? ? (1)new內(nèi)存分配細(xì)節(jié)
? ? ? ??①new分配內(nèi)存實(shí)際是調(diào)用malloc函數(shù)進(jìn)行內(nèi)存分配的;
? ? ? ? ②malloc實(shí)際分配內(nèi)存時(shí),不單單分配需要的內(nèi)存大小,還要附加大量的附帶內(nèi)存,用以記錄相關(guān)使用信息,包括記錄分配了多少個(gè)字節(jié)(占4字節(jié))、調(diào)試信息(占30-60字節(jié))、邊界調(diào)整(占10字節(jié))、尾信息(4字節(jié)),也就是說(shuō)除了實(shí)際分配的內(nèi)存(比如給一個(gè)int變量分配4字節(jié)),還要附加70字節(jié)左右的附加內(nèi)存,內(nèi)存浪費(fèi)很?chē)?yán)重;尤其是頻繁申請(qǐng)小塊內(nèi)存時(shí),浪費(fèi)更加嚴(yán)重;
? ? ? ? ③重載new操作符、重載delete操作符
void *operator new(size_t size)
{int *p = (int *)malloc(sizeof(int)):return *p;
}void *operator new[](size_t size)
{void *p = malloc(size); //內(nèi)部會(huì)有轉(zhuǎn)換機(jī)制,將你輸入的個(gè)數(shù),乘以類(lèi)型的單位字節(jié),算出來(lái)總數(shù)字節(jié)賦給size;return p;
}void *operator delete[](void *p)
{free(p);
}void *operator delete(void *p)
{free(p);
}
? ? ? ? ④定位new(pleacement new)
? ? ? ? 在已經(jīng)分配的原始內(nèi)存中初始化一個(gè)對(duì)象,相當(dāng)于從之前已經(jīng)分配好的大塊內(nèi)存中取出一塊來(lái)給新的變量使用;通常應(yīng)用①在硬件設(shè)備地址與c++的類(lèi)直接關(guān)聯(lián);②容器也利用了預(yù)分配內(nèi)存,然后逐步使用的方法;
void *p1 = (void*)new char[sizeof((A))];
A *p2 = new (p1)A(); //調(diào)用了無(wú)參構(gòu)造函數(shù),使用的是原來(lái)p1的大塊內(nèi)存;//自定義有參的話,就可以使用A(12)帶參數(shù)了;
? ? (2)內(nèi)存池(池化技術(shù)是解決內(nèi)存開(kāi)銷(xiāo)問(wèn)題的,像線程池、內(nèi)存池,其內(nèi)部的機(jī)理是利用鏈表形成大內(nèi)存,后面每次使用時(shí),都分配一個(gè)結(jié)點(diǎn)空間給變量使用)
? ? ? ??①作用:減少malloc次數(shù),就減少了對(duì)內(nèi)存的浪費(fèi);
? ? ? ? ②原理:用malloc分配一大塊內(nèi)存,當(dāng)后面使用要分配時(shí),從這一大塊內(nèi)存中一點(diǎn)一點(diǎn)分派,當(dāng)一大塊內(nèi)存快用完時(shí),再用malloc申請(qǐng)一大塊內(nèi)存,然后再一點(diǎn)一點(diǎn)分配;
? ? ? ??③嵌入式指針,是借用A對(duì)象所占用的前8個(gè)字節(jié)(可能是2個(gè)整型數(shù)等),來(lái)充當(dāng)指針,當(dāng)被分配時(shí),就指針后移,將該部分空間分配給新變量,利用內(nèi)存共享,實(shí)現(xiàn)了空間的高效利用;
??????? 內(nèi)存池的代碼實(shí)現(xiàn):
#include <iostream>using namespace std;class Test
{
public:Test() = default;Test(int num){m_num = num;}void * operator new(size_t size){cout<<"size = "<<size<<endl;Test *temp;if(m_head == nullptr){cout<<"malloc"<<endl;m_head = (Test *)malloc(sizeof(Test) * 50);temp = m_head;for(int i = 0; i < 50; i++){temp->next = temp + 1;temp = temp->next;}temp->next = nullptr;}cout<<"mem"<<endl;temp = m_head;m_head = m_head->next;return m_head;}void * operator new[](size_t size){cout<<"new[] "<<size<<endl;void *p = malloc(size); //這里的size會(huì)自動(dòng)轉(zhuǎn)換為以字節(jié)為單位的大小return p;}void operator delete(void *p){Test * temp = (Test *)p;temp->next = m_head;m_head = temp;}void operator delete[](void *p){cout<<"delete[]"<<endl;free(p);}int m_t;int m_num;Test *next;static Test *m_head;
};Test * Test::m_head = nullptr;int main(int argc, char **argv)
{Test *p1 = new Test[10]; //這里的10指的是10個(gè)Test大小的內(nèi)存delete [] p1;cout<<"begin:"<<endl;Test *p2 = new Test(2); //??這里是重載new,為什么當(dāng)做構(gòu)造函數(shù)報(bào)錯(cuò)?,因?yàn)門(mén)est(2)先構(gòu)造一個(gè)對(duì)象,就像int(5)先初始化一個(gè)值為5的變量Test *p3 = new Test(); //為什么已添加這個(gè)就報(bào)錯(cuò)?p3->m_num = 3;p3->m_t = 4;cout<<p3->m_num<<" : "<<p3->m_t<<endl;return 0;
}