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