// 靜態內存(static) 局部static, 類static, 定義在任何函數之外的變量,由編譯器自動創建和銷燬,程序使用前創建,程序結束時銷燬,
// 棧(stack) 函數內的非static 對象,由編譯器自動創建和銷燬,僅在其定義的程序塊運行時存在
// 堆(heap) 動態分配的對象,運行時分配的對象,程序顯式的銷燬
// 動態內存的管理
// new 在動態內存中爲對象分配空間並返回指向該對象的指針,可選擇進行初始化
// delete 接受動態對象的指針,銷燬該對象,釋放與之關聯的空間
#include<memory>
#include<iostream>
#include<string>
#include<initializer_list>
#include<vector>
#include<stdexcept>
#include<new>
using std::shared_ptr;
using std::unique_ptr;
using std::make_shared;
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::initializer_list;
using std::vector;
using std::out_of_range;
using std::nothrow;
// 對象間共享數據類定義
class StrBlob{
public:
typedef vector< string>::size_type size_type;
// 構造函數
StrBlob();
StrBlob( initializer_list< string> il);
// 普通成員函數
size_type size() const {
return data->size();
}
size_type count() const {
return data.use_count();
}
bool empty() const {
return data->empty();
}
// 添加刪除
void push_back( const string &s){
data->push_back( s);
}
void pop_back();
// 訪問
string& front() const;
string& back() const;
private:
// 數據
shared_ptr< vector< string>> data;
// 檢查 data[i] 的合法,拋出異常
void check( size_type i, const string &s) const;
};
// 構造函數
StrBlob::StrBlob() : data( make_shared< vector< string>>()){}
StrBlob::StrBlob( initializer_list< string> il) :
data( make_shared< vector< string>>( il)){}
// 檢查 data[i] 的合法,拋出異常
void StrBlob::check( size_type i, const string &s) const{
if( i >= data->size())
throw out_of_range( s);
}
// 刪除
void StrBlob::pop_back(){
check(0, "pop_back on empty strBlob");
data->pop_back();
}
// 訪問
string& StrBlob::front() const {
check(0, "front on empty strBlob");
data->front();
}
string& StrBlob::back() const {
check(0, "back on empty strBlob");
data->back();
}
// 返回動態內存
vector< int> *get_pvi(){
return new vector< int>();
}
void save_pvi(vector< int> * pvi){
int n = 0;
while( cin >> n)
pvi->push_back( n);
}
void print_pvi(vector< int> * pvi){
for( auto &w : *pvi)
cout << w << endl;
}
int main(){
// 智能指針(smart pointer),定義於 memory.h
// shared_ptr 允許多個指針指向同一個對象
// unique_ptr 獨佔所指向的對象
// week_ptr 弱引用,指向 shared_ptr 所管理的對象
// shared_ptr unique_ptr 都支持的操作
// 1.創建時必須指明一個指針可以指向的類型,默認初始化爲空指針
shared_ptr< string> sp1, sp2;
unique_ptr< string> up;
// 2.用作判斷條件,若指向一個對象,則爲 true
if( sp1 && sp1->empty()){ //4.調用指向對象的成員
// 3.解引用,獲得指向對象的左值
*sp1 = "hello";
// 6.返回智能指針中保存的指針
cout << *( sp1.get()) << endl;
}
// 5.交換兩個指針
swap( sp1, sp2);
sp1.swap( sp2);
// shared_ptr 獨有的操作
// 1> make_shared 返回一個 shared_ptr,指向一個動態分配的類型爲 string的對象,並初始化爲 world
// 最安全的分配和使用動態內存的方法,初始化的參數必須是指定類型的構造函數的參數
sp2 = make_shared< string>( "world");
// 每個shared_ptr 都有一個引用計數(reference count),當拷貝時,計數器增加,賦值或銷燬時,計數器減少,當變爲0時,自動釋放所管理的對象
// 2> sp3是 sp2 一個拷貝,同時增加 sp2的計數器,sp2 的指針必須能轉換爲 string *
shared_ptr< string> sp3( sp2);
// 3> sp1 和 sp3 所保存的指針必須能夠相互轉換,同時遞減sp1計數,若爲0,則釋放其管理的內存,遞增 sp3 的計數
sp1 = sp3;
// 4> 如果 use_count() 爲1, 則unique() 爲true,否則爲 false
if( sp3.unique())
cout << "count == 1" << endl;
else
// use_count() 返回與 sp3 共享對象的智能指針數量,比較慢,主要用於測試
cout << "count : " << sp3.use_count() << endl;
// 使用動態內存的情況
// 不知道要使用多少對象
// 不知道對象的確切類型
// 在多個對象之間共享數據
StrBlob sb1, sb2{"hello", "world"};
cout << "sb1.count = " << sb1.size() << endl;
cout << "sb2.count = " << sb2.size() << endl;
{
StrBlob sb3{"test"};
sb1 = sb3;
cout << "sb3.count = " << sb3.size() << endl;
cout << "sb1.count = " << sb1.size() << endl;
sb3.push_back( "strblob");
cout << "sb3.count = " << sb3.size() << endl;
cout << "sb1.count = " << sb1.size() << endl;
}
const StrBlob sb4{ "say", "yeah!~"};
cout << sb4.front() << " < > " << sb4.back() << endl;
cout << sb2.front() << " < > " << sb2.back() << endl;
cout << "sb1.count = " << sb1.size() << endl;
cout << "sb2.count = " << sb2.size() << endl;
sb1 = sb2;
cout << "sb1.count = " << sb1.size() << endl;
cout << "sb2.count = " << sb2.size() << endl;
// 直接管理內存 new 分配內存,delete 釋放 new 分配的內存
// 注意區別
int *pi1 = new int; // 默認初始化,對於內置類型,其值是未定義的
int *pi2 = new int(); // 值初始化
auto *pi3 = new auto(5); // 自動推斷類型
const int *pi4 = new const int(1024); // const 對象必須初始化,如果類類型有默認構造函數可以隱式初始化
cout << *pi1 << " - " << *pi2 << " - " << *pi3 << " - " << *pi4 << endl;
// 如果內存耗盡,new 表達式失敗,會拋出 bad_alloc 異常
// bad_alloc nothrow new 定義於 new.h
int *pi5 = new (nothrow) int(); // 定位 new (placement new),如果分配失敗,不會拋出異常,返回一個空指針
// 釋放動態內存 (delete expression)
// 釋放一塊並非 new 分配的內存或將相同的指針釋放多次,其行爲是未定義的
delete pi5; // 兩步,銷燬指向的對象,釋放對應內存,pi5必須是指向動態分配的內存或是一個空指針
// 動態內存有效期直到顯式釋放
// 如果使用了返回動態內存指針的函數,使用者使用完必須顯式釋放
// 常見問題:
// 1.忘記 delete 分配的內存,很難檢測,多數情況下,等內存耗盡才發現
// 2.使用已經釋放掉的對象,雖然指針已失效但多數情況下仍保存了原地址,類似於未定義的指針,建議置爲空指針,
// 但是如果有多個指針指向該內存,逐個釋放也很麻煩
// 3.釋放已經釋放過的內存,未定義行爲
auto pvi = get_pvi();
save_pvi( pvi);
print_pvi( pvi);
delete pvi;
// 智能指針的構造函數是 explicit, 不能進行隱式轉換,只能使用直接初始化
// 錯誤:shared_ptr< string> ps1 = new string("hello world");
shared_ptr< string> ps2( new string("hello world"));
// 定義改變 shared_ptr 的其他方法
// shared_ptr< T> p( q) p 管理 q指向的對象,q必須指向 new 分配的內存,且能夠轉換爲 T* 類型
// shared_ptr< T> p( u) p 從 unique_ptr u 那裏接管了對象的所有權,將 u 置空
// shared_ptr< T> p( q, d) p 從 內置類型指針 q 那裏接管了對象的所有權,q 必須能轉換爲 T*, p 將使用可調用對象 d 代替 delete
// shared_ptr< T> p( p2, d) p 是 shared_ptr p2 的拷貝, 但 p 可以使用可調用對象 d 代替 delete
// p.reset() 若 p 是唯一指向其對象的 shared_ptr,reset會釋放此對象
// p.reset( q) 若同時傳遞了可選的內置指針 q,會另 p 指向 q, 否則爲空
// p.reset( q, d) 若同時還傳遞了 d,使用可調用對象 d 代替 delete
// 不要使用 get() 初始化另一個智能指針或爲智能指針賦值
shared_ptr< int> spi1( new int( 99));
// pi1 和 spi1 指向同一個內存
int *pi1 = spi1.get();
{
// pi1 spi2 和 spi1 指向同一個內存,但分別是2個不同的智能指針,且計數都爲1,當spi2 脫離作用域,其指向的內存會被釋放,後續的解引用行爲未定義
shared_ptr< int> ( spi2);
}
int vspi1 = *spi1; //未定義行爲
// 智能指針使用規範:
// 不使用相同的內置指針初始化多個智能指針
// 不 delete get() 返回的指針
// 不使用 get() 初始化或reset 另一個智能指針
// 如果使用了 get() 指針,當其最後一個對應的智能指針銷燬後,指針變爲無效
// 如果使用的智能指針管理的資源不是 new 分配的內存,記住傳遞一個刪除器
// unique_ptr
// 定義改變 unique_ptr 的其他方法
// unique_ptr< T> p 指向 T 類型的對象, 空 unique_ptr, 可以使用 delete 釋放
// unique_ptr< T, D> p 同上,使用 D 類型的可調用對象來釋放指針
// unique_ptr< T, D> p ( d) 同上,使用 D 類型的可調用對象 d來釋放指針
// p = nullptr 釋放 p 指向的對象,將 p 置空
// p.release() 放棄控制權,返回指針,將 p 置空
// p.reset() 釋放此對象
// p.reset( q) 若同時傳遞了可選的內置指針 q,會另 p 指向 q, 否則爲空
// p.reset( nullptr)
// 不支持賦值和拷貝, 可以通過 release() 或 reset() 轉移控制權
unique< int> upi1;
unique< int> upi2 ( new int ( 99));
upi1.reset( upi2.release());
// weak_ptr
// 指向 shared_ptr 關聯的對象,但不增加其引用計數,當對象最後一個 shared_ptr 被銷燬,即使有 weak_ptr 仍會銷燬
// weak_ptr< T> w 空 weak_ptr 指向類型 T 的對象
// weak_ptr< T> w( sp) 與 shared_ptr sp 指向同一個對象, T 必須能轉換爲 sp 指向的類型
// w = p p 可以是 shared_ptr 或 weak_ptr,賦值後共享對象
// w.reset() 置空 w
// w.use_count() 與 w 共享對象的 shared_ptr 數量
// w.expired() 若 w.use_count() 爲0,返回 true,否則爲 false
// w.lock() 如果 w.expired() 爲真,返回空 shared_ptr, 否則返回指向 w 對象的 shared_ptr
return 1;
}
c++ primer(第五版)筆記 第十二章 動態內存(1)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.