C++自修入門實境秀、C++ Primer 5版研讀秀 78/ ~ v12動態記憶體(dynamic memory)12.1.2. Manag...
Using new to Dynamically Allocate and Initialize Objects
配置在free stroe的物件是不具名的(未命名的)
因此new並不提供為它所配置的物件命名的服務/機制;也就是,並不會為它所配置的物件命名;而是instead,new運算子會回傳一個指向它所配置的物件的指標(pointer);注意,這不是智慧指標,只是一般指標;所以叫直接管理記憶體,就不是假智慧指標之手來管理動態記憶體(dynamic memory)了
也可見,new是在free store 中配置資源給物件的
int *pi = new int; // pi points to a dynamically allocated,
// unnamed, uninitialized int
13:08
new運算式(表達式)是在free store建構了一個不具名的物件
This new expression constructs an object of type int on the free store and returns a pointer to that object.
預設情況下,動態配置的物件都是預設初始化的:
By default, dynamically allocated objects are default initialized (§ 2.2.1, p. 43),which means that objects of built-in or compound type have undefined value; objects of class type are initialized by their default constructor:
頁459
20:00
可以用直接初始化(direct initialize)來初始化動態配置的物件(dynamically allocated object)
int *pi = new int(1024); // object to which pi points has value 1024
string *ps = new string(10, '9'); // *ps is "9999999999"
// vector with ten elements with values from 0 to 9
vector<int> *pv = new vector<int>{0,1,2,3,4,5,6,7,8,9};
我們也可以值初始化(§3.3.1,頁98)一個動態配置的物件,方法是在型別名稱後接著一對空的括弧:
string *ps1 = new string; // default initialized to the empty string
string *ps = new string(); // value initialized to the empty string
int *pi1 = new int; // default initialized; *pi1 is undefined
int *pi2 = new int(); // value initialized to 0; *pi2 is 0
27:59
值初始化(value initialize)與預設初始化的異同
對於類別型別(class type)而言,不管寫碼的形式如何,要求一個值初始化,其實都是藉由該類別的預設建構器(default constructor),因此在類別型別的值初始化與預設初始化之間並沒有實質上的不同;然而對於內建型別來說,這其間的差別就很明顯。同樣的,在類別內的資料成員(data member)若是內建型別,而類別本體內並未對其做適當的初始化,而是仰賴編譯器湊合(合成)的建構器的版本來做類別成員的初始化,那麼,在這樣的情況下,值初始化這些類別成員也將是未初始化的狀態。
仰賴合成預設建構器的類別中內建型別的成員,如果沒在類別主體中初始化就會依然是 未初始化的(§7.1.4,頁263)。
定義預設建構器的第二個理由是,對於某些類別來說,合成的預設建構器所做的事情是錯的。還記得我們說過,定義在一個區塊內的內建或複合型別(例如陣列或指標)物件在預設初始化的時候會有未定義的值(§2.2.1,頁43、44、459—此即處)。同樣的規則也適用於預設初始化的內建型別成員。因此,具有內建或複合型別成員的類別一般都應該在類別內初始化那些成員,或定義它們自己版本的預設建構器。否則,使用者可能會創造出其成員帶有未定義值的物件。(頁263)
46:30
養成好習慣(Best Practises):就好像為什麼我們要習慣去初始化一般的變數一樣,我們也最好養成初始化動態配置的物件(dynamically allocated object)的好習慣
51:00
當在圓括號內指定初始器的時侯,可以用auto來作為想要用這個初始器來初始化的動態配置物件其型別的推測。在這種情況下,因為編譯器要根據所給定的初始器型別來推測要動態配置物件的型別,所以這個在圓括號內的初始器就只能有一個,以作為推斷的依據。
auto p1 = new auto(obj); // p1 points to an object of the type of obj
// that object is initialized from obj
auto p2 = new auto {a, b, c}; // error: must use parentheses for the initializer
p1 points to英文版誤作「p points to」中文版也未更正
59:00
Dynamically Allocated const Objects
動態配置的const物件
用new來配置常值const的物件是可以的
// allocate and initialize a const int
const int *pci = new const int(1024);
// allocate a default-initialized const empty string
const string *pcs = new const string;
頁460
1:1:50
就像其他的const物件,動態配置的常值物件也必須在配置時就被初始化(給定一個後來不能再改變的值)
動態配置的常值物件(dynamically allocated object),只要是一個具有預設建構器的型別,就可以被該預設建構器隱含地/直接地/預設地/默認地……初始化
如果是其他的型別,就必須明確地初始化
1:11:00
Memory Exhaustion
記憶體耗盡
free store(自由存放區)
自由存放區耗盡永遠都還是可能發生。
→自由存放區還是有可能被耗盡的
自己比較看看,哪一種才是真國文
可別老是要「老師」來幫您們改作文好嗎?敬告翻譯者
一旦應用程式用罄了它所有可用的記憶體後,new的述句/表達式/運算式就會出錯/失敗
預設的情況是,當一個new無法再支配記憶體資源時,就會丟出一個bad_alloc型別的例外情形
可以藉由另外一種形式的new的使用(nothrow)來避免new碰到這樣的情形,丟出例外的情況
1:24:00
// if allocation fails, new returns a null pointer
int *p1 = new int; // if allocation fails, new throws std::bad_alloc
int *p2 = new (nothrow) int; // if allocation fails, new returns a null
pointer
因為我們會在§ 19.1.2(頁823、824)中解釋的原因,這個形式的new被稱為placement new (放置型 new)。
nothrow是一個在程式庫定義的物件,在這裡作為傳遞給new的一個額外的引數
用了這種形式的new後,一旦無法成功配置所需的儲存空間(記憶體空間),那麼就會回傳一個null指標(空指標)
bad_alloc和nothrow都被程式庫定義在new標頭檔中
Freeing Dynamic Memory
釋放動態記憶體
為了避免記憶體耗盡,我們必須將用完的/不用的動態配置記憶體還給作業系統
用delete這個關鍵字(運算式)就可以將不用的動態配置記憶體還給作業系統
delete運算式帶了一個指標指向我們想要釋放/解消/解銷/註銷的物件
delete p; // p must point to a dynamically allocated object or be null
Pointer Values and delete
1:39:20
指標值和delete
同一個指標值是不允訊多次delete的!只能刪一次
delete也不能用在非new配置後回傳的指標上
1:44:00
int i, *pi1 = &i, *pi2 = nullptr;
double *pd = new double(33), *pd2 = pd;
delete i; // error: i is not a pointer
delete pi1; // undefined: pi1 refers to a local
delete pd; // ok
delete pd2; // undefined: the memory pointed to by pd2 was already freed
delete pi2; // ok: it is always ok to delete a null pointer
*pd2 = pd;就是在講同一個值不能刪兩次(以上)
想成是「傳址(pass by reference)」而不是拷貝(傳值(pass by value))就好理解了。因為是連動的,刪除一個值也會刪掉參考到它的值(指標)
pi2就是死豬不怕熱水燙,所以怎麼對它delete都無妨
頁461
1:51:30
statically和 dynamically相對
statically字根是static
1:54:40
常值物件的值雖然不能被改動,但物件它本身卻是可以被摧毀/註銷的
const int *pci = new const int(1024);
delete pci; // ok: deletes a const object
Dynamically Allocated Objects Exist until They Are Freed
動態配置的物件(dynamically allocated object)在被釋放前,都一直存在/有效
1:58:10
動態配置的物件會持續存在直到它們被釋放為止
用內建型別(builtin type)的指標,而不是智慧指標(smart pointer),如shared_ptr來管理的記憶體的情形,就不是和shared_ptr一樣,在最後一個shared_ptr不再有效後,也連帶地摧毀了它所指向的動態配置物件、釋放出動態配置的記憶體空間。用內建型別指標管理的動態配置物件會在明確地使用delete來delete這個指標前都一直存在
回傳內建型別指標的函式將delete的工作,交給它的呼叫端去處理 2:8:30
// factory returns a pointer to a dynamically allocated object
Foo *factory(T arg)
{
// process arg as appropriate
return new Foo(arg); // caller is responsible for deleting this memory
}
2:14:00
void use_factory(T arg)
{
Foo *p = factory(arg);
// use p but do not delete it
} // p goes out of scope, but the memory to which p points is not freed!
p只是區域變數,出了use_factory函式後就不再有有效、能用了;而是它指向的動態配置的物件(dynamically allocated object)卻依然存在,霸佔著記憶體、尸位素餐、浪費系統資源。
頁462
2:20:10
In this example, p was the only pointer to the memory allocated by factory. Once use_factory returns, the program has no way to free that memory.
就變成懸空奇案了,破不了了。這樣的配置,就成了一個爛尾了。
void use_factory(T arg)
{
Foo *p = factory(arg);
//使用p
delete p; //既然不再需要它,記得釋放記憶體
}
2:33:50
VBA和IE瀏覽器
【VBA教學】Excel VBA 幫我們自動輸入帳號及密碼登入網站 https://youtu.be/XbhtQA6q6CY 4:7:00
2:50 程式碼 借老師菩薩的這裡做個筆記,感恩感恩 讚歎讚歎 南無阿彌陀佛
06 IE物件相關與用MOVE方法排序 https://youtu.be/TYfRDfUyByY
4:37:49 4:52:40
Application.Wait(Now+TimeValue("0:00:05")
Application.OnTime()
VBAandBrowser.xlsm
3:33:30 開發測試查詢《國語辭典》功能。用IE,成功了 3:46:00
Sub IEBrowser()
Dim whatWhere As String, rw As Long, URL As String
rw = ActiveCell.Row
If ActiveCell.Column > 1 Then
Cells(rw, 1).Select
End If
whatWhere = ActiveCell.Value
Select Case whatWhere
Case "查詢《國語辭典》"
URL = Range("c1").Value
If URL = "" Then URL = "http://dict.revised.moe.edu.tw/cbdic/search.htm"
Case Else
Exit Sub
End Select
With CreateObject("internetexplorer.application")
.navigate URL
Do While .Busy Or .readyState <> 4
DoEvents '4:8:00
Loop
'DOM物件文件物件模型(Document Object Model, DOM)是HTML、XML 和SVG 文件的程式介面。
With .document
Select Case whatWhere
Case "查詢《國語辭典》"
.all("qs0").Value = Range("b" & rw).Value'.all是IE專用的方法
.all("button").Click
Case Else
Exit Sub
End Select
End With
.Visible = True
End With
End Sub
4:53:40 5:16:30 7:4:00
完成本講義教材的查詢國語辭典ctrl+F12、Google Atl+G或者Ctrl+Shift+G的程式,啟用內容後,讀者均可用。只要選取要查詢的字串(文字)即可。7:49:00 測試成功
VBA、VB與Chorme瀏覽器
Google Chrome does not provide a Visual Basic interface like Internet Explorer does, so you cannot access any of it's properties (e.g. Document). https://stackoverflow.com/questions/30018858/using-chrome-browser-instead-of-internetexplorer-application
Google Chrome is not a .NET application, therefore you cant create a VBA object. https://superuser.com/questions/1268144/how-to-get-data-from-google-chrome-using-vba
可見VBA或VB和Chrome的互動應是無解
5:38:20
C#與Chorme瀏覽器
需要引用CefSharp參考、程式庫
2:59:00
3:10:00
如何藉由輸入指令來執行控制台工具
https://support.microsoft.com/zh-tw/help/192806/how-to-run-control-panel-tools-by-typing-a-command
控制台工具 命令
-----------------------------------------------------------------
協助工具選項 control access.cpl
新增/移除程式 control appwiz.cpl
日期/時間內容 control timedate.cpl
字型資料夾 control fonts
網際網路內容 control inetcpl.cpl
網路內容 control netcpl.cpl
印表機資料夾 control printers
地區設定 control intl.cpl
音效內容 control mmsys.cpl sounds 即喇叭設定
系統內容 control sysdm.cpl
Description of Control Panel (.cpl) Files
https://support.microsoft.com/zh-tw/help/149648/description-of-control-panel-cpl-files
5:59:59
警告:使用new與delete來動態管理記憶體有三種可能的錯誤:
1.忘了delete掉new動態配置的記憶體
這種情形就就叫做記憶體外洩/浪費/虛耗(memory leak),因為忘了delete掉的已配置動態記憶體(dynamic memory)是永遠也不個回歸到free store裡中的。而想要檢測這樣的虛耗/洩漏,是非常困難的。因為除非應用程式運行久到可以耗盡記憶體時,這樣的記憶體外洩才容易被偵測出來。
2.用到一個已經被delete的物件
這種錯誤則可以藉由在delete物件後,設定指向該物件的指標為空(null、nullptr)來偵測
3.對一個物件delete兩次(以上)
對同一個動態配置的物件(dynamically allocated object)若delete兩次,就會出錯;因為該物件已經被delete不存在了,如何再被delete一次?
If we subsequently delete the second pointer, then the free store may be corrupted.
如果我們後續delete 了第二個指標,那麼自由存放區就有可能毀損
原來自由存放區(free store)還會損毀。
以上「這些種類的錯誤很容易犯,但要找出它們或修正它們非常困難。」
6:15:05
養成好習慣
我們可以藉由僅利用智慧指標(smart pointer)來管理動態記憶體(dynamic memory),就可以完全避免發生以上任何一種情況的錯誤
中文版翻譯有誤:
You can avoid all of these problems by using smart pointers exclusively.
你可以全都使用智慧指標以避免所有的這些問題。
清除不必要的動態配置的物件,釋放動態記憶體的工作,智慧指標會負責;只在沒有任何智慧指標再指向該動態配置物件時,智慧指標就會清除該動態配置物件,而釋放出動態配置記憶體,將之歸還給自由存放區(free store)
Resetting the Value of a Pointer after a delete ...
在delete後重置一個指標的值…
6:22:05
在執行delete後,被delete的指標會失效;但一個失效的指標在許多計算機上仍然會指向那個已經被釋出的動態記憶體位址
在經過delete後的指標,就是所謂的「懸置指標(dangling pointer)」
一個懸置指標指的是一個曾經存放過物件的記憶體位址,但那個位址現在已經不再存放該物件了
中文版雖照原文、忠於原文,但流於板滯,也太文謅謅了:
一個懸置指標就是指向曾經存放某個物件的記憶體但不再如此的一種指標。
7:53:09
頁463
如果想保留一個在delete後的指標,我們可以指定那個指標的值為nullptr,也就是本標題「Resetting the Value of a Pointer after a delete ...」
8:3:50
想要找出所有指向同一記憶體位址的指標幾乎是不可能的事
In real systems, finding all the pointers that point to the same memory is surprisingly difficult.
8:6:40
練習12.6
#include<iostream>
#include<iterator>
#include<vector>
//#include<new>
using namespace std;
vector<int>* returnDynamicallyAllocatedVec() {
return new(vector<int>);
}
void read_give_values_to_the_elements(vector<int>* vp){
istream_iterator<int>in(cin), end;
while (in!=end)
{
vp->push_back(*in++);
}
}
void print_the_values_that_were_read(vector<int>* vp) {
ostream_iterator<int>out(cout, ",");
for (int i :*vp)
{
*out++=i;
}
}
int main() {
vector<int>* vp = returnDynamicallyAllocatedVec();
read_give_values_to_the_elements(vp);
print_the_values_that_were_read(vp);
delete vp;
vp = nullptr;//這一行在此例中應是可有可無
}
8:25:40
練習12.7
#include<iostream>
#include<iterator>
#include<vector>
//#include<memory>
//#include<new>
using namespace std;
shared_ptr<vector<int>> returnDynamicallyAllocatedVec() {
vector<int> vi;
return make_shared<vector<int>>(vi);
}
void read_give_values_to_the_elements(const shared_ptr<vector<int>>& vp){
istream_iterator<int>in(cin), end;
while (in!=end)
{
vp->push_back(*in++);
}
}
void print_the_values_that_were_read(const shared_ptr<vector<int>>& vp) {
ostream_iterator<int>out(cout, ",");
for (int i :*vp)
{
*out++=i;
}
}
int main() {
shared_ptr<vector<int>> vp = returnDynamicallyAllocatedVec();
read_give_values_to_the_elements(vp);
print_the_values_that_were_read(vp);
cout << vp.use_count() << endl;
if (vp.unique())
{
cout << vp.use_count() << endl;
}
//vp = nullptr;
//cout << vp.use_count() << endl;
vp.reset();//和vp = nullptr是一樣的
//A shared_ptr stops owning a resource when it's reassigned or reset. https://docs.microsoft.com/en-us/cpp/standard-library/shared-ptr-class?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev16.query%3FappId%3DDev16IDEF1%26l%3DEN-US%26k%3Dk(MEMORY%2Fstd%3A%3Ashared_ptr%3A%3Areset);k(std%3A%3Ashared_ptr%3A%3Areset);k(reset);k(DevLang-C%2B%2B);k(TargetOS-Windows)%26rd%3Dtrue%26f%3D255%26MSPPError%3D-2147217396&view=vs-2019
cout << vp.use_count() << endl;
}
8:54:50
練習12.8
#include<iostream>
using namespace std;
bool b() {
int* p = new int;
// ...
return p;//沒有delete,則成記憶體洩漏、浪費記憶體了;因為出此範疇,則new所配置的
//動態記憶體就無法被清除了。p是不會變成懸置指標(dangling pointer),因為出此b函式範疇就自動銷毀(自動物件、區域變數)。
}
int main() {
if (b() == 0) cout << "p 沒有指向任何物件" << endl;
else cout << b() << endl;
}
9:12:40
練習12.9
#include<iostream>
using namespace std;
int main() {
int* q = new int(42), * r = new int(100);
r = q;//因為r現在不再指向new出來的int(100),這個int(100)沒有delete掉,就成了記憶體外洩
//int(42)現在則有二個指標指向它
auto q2 = make_shared<int>(42), r2 = make_shared<int>(100);
r2 = q2;//r2原來指向的動態配置的「<int>(100)」因為沒有指向它的智慧指標,就隨之銷毀了
//"<int>(42)"這個動態配置物件則會有兩個智慧指標指向它
//q2.use_count=2,r2.use_count=0---印出來卻是2!
cout << q2.use_count()<< endl;
cout <<r2.use_count() << endl;
}
留言