C++自修入門實境秀、C++ Primer 5版研讀秀 80 ~ v12動態記憶體(dynamic memory)12.1.4~12.1.6
12.1.4. Smart Pointers and Exceptions
智慧指標與例外情形
剛才才碰到例外情形。呵呵。
複習頁196 Caution: Writing Exception Safe Code is Hard
用智慧指標來管理動態配置記憶體,以保證例外情形的安全性 exception safe(頁196)
即使執行區域block還沒結束、執行被迫中斷,智慧指標也能在適當時機下釋出用不到的記憶體,不致讓記憶體洩漏
42:00
void f()//f:function
{
shared_ptr<int> sp(new int(42)); // allocate a new object
// code that throws an exception that is not caught inside f
} // shared_ptr freed automatically when the function ends
//只要離開了f的block區域scope範疇,shared_ptr摧毀後,參考計數為0,就會釋出記憶體
然而如果我們是直接管理記憶體,當例外情形一旦發生時,那麼該記憶體就不會自動被釋放了
如果我們用內建指標(built-in pointer)來管理記憶體,而例外情形發生在new之後、在對應的delete之前,那麼該記憶體也無法被自動釋放
52:00
頁468
若在區域中沒能抓住這種例外情形處理的話,那麼這個記憶體,因為沒有任何指標指向它了,就可能變成懸置的記憶體了。在應用程式執行期間,永遠都會徒佔記憶體位置而無法釋出其資源給作業系統利用了
Smart Pointers and Dumb Classes
智慧指標和愚類別(Dumb Classes)
2:28:50愚類別即沒有解構器(destructor)的類別。可以藉由智慧指標的特性來管理這樣的類別的資源配置
可見智慧指標的特性就是當它不再需要被用到的時候(當其悉數都摧毀時)也能將其指向的東西,一併清除,釋放該記憶體資源
雖然大部分的C++類別與所有程式庫類別都會定義該類別的解構器(destructor)來清除該類別物件所佔用的系統資源(記憶體資源),但仍有一些類別並無此種設施(However, not all
classes are so well behaved.中文版翻成「乖巧聽話」)——尤其是那些為了要兼容、會通,而由C++語言與C語言共用的類別,都傾向於讓使用該類別的使用者負責自行清除、釋放所使用到的資源
if an exception happens between when the resource is allocated and when it is freed, the program will leak that resource.
leak這裡可以翻成「死角、罩門」。這樣的情況下,該應用程式就有資源利用上的死角、出現資源利用上的罩門。
leak that resource.就是 記憶體洩漏memory_leak
這裡的資源(resource)多即記憶體之同義詞
而我們可以套用管理動態配置記憶體一樣的方法來管理對這樣類別的應用,補足其沒有內定適當解構器(destructor)的缺漏。
imagine we’re using a network library that is used by both C and C++.
想像我們正在使用C和C++都能使用的一個網路程式庫。
network恐怕是程式庫的專名,應該不能翻成「網路程式庫」
connection
struct destination; // represents what we are connecting to
struct connection; // information needed to use the connection
connection connect(destination *); // open the connection
void disconnect(connection); // close the given connection
void f(destination &d /* other parameters */)
{
// get a connection; must remember to close it when done
connection c = connect(&d);
// use the connection
// if we forget to call disconnect before exiting f, there will be no way to close c
}
It turns out that we can also use a shared_ptr to ensure that the connection is properly closed.
事實證明,我們也能使用一個shared_ptr來確保connection有正確 關閉。
It turns out that當翻成「可見」就好。
Using Our Own Deletion Code
使用我們自己的刪除程式碼
1:29:00預設情況下智慧指標shared_ptr都是認為它們指向的是動態記憶體(dynamic memory),因為在指向某物件的最後一個shared_ptr被摧毀時,它就會調用delete述句來釋放那個物件的記憶體
頁469
要用智慧指標shared_ptr來管理connection這樣的類別物件,我們首先要準備好一個它通用的解構器(destructor)函式來取代shared_ptr預設會調用的delete,以釋放該物件的資源
To use a shared_ptr to manage a connection, we must first define a function to use in place of delete.
It must be possible to call this deleter function with the pointer stored inside the shared_ptr. In this case, our deleter must take a single argument of type connection*:
1:38:30 刪除器(deleter)
刪除器就是Table 12.3. Other Ways to Define and Change shared_ptrs 表12.3 :定義和變更shared_ptr的其他方式所謂的可呼叫物件(callable object)
void end_connection(connection *p) { disconnect (*p); }
這樣,當我們建構一個shared_ptr智慧指標來管理像connection這樣沒有解構器(destructor)的類別物件時,我們就可以傳遞一個函式指標(function pointer)給它shared_ptr來調用這個刪除器(deleter)
void f(destination &d /* other parameters */)
{
connection c = connect(&d);
shared_ptr<connection> p(&c, end_connection);
// use the connection
// when f exits, even if by an exception, the connection will be properly closed
}
和陣列一樣,直接用函式名稱作為引數即是函式指標(參見前頁247)
又參見前Table 12.3. Other Ways to Define and Change shared_ptrs 表12.3 :定義和變更shared_ptr的其他方式「shared_ptr<T> p(q, d)」:「&c」應該是個指標,「&」此乃取址運算子,對C作取址的值就是普通指標,非參考才是?!
1:52:00
可見內建指標(built-in pointer)不等於普通指標(plain pointer、ordinary pointer),像這裡的
void end_connection(connection *p) 這個函式它的參數就是普通指標,而不是內建指標
內建指標指的是內建型別(builtin type)的指標
1:56:40
Caution: Smart Pointer Pitfalls
使用智慧指標要留意的風險
中文版翻成
注意:智慧指標常見的陷阱
是錯的。就好像「自傳的目的、遙控器的目的」這樣的構句一樣的不通!應改寫成「在使用智慧指標時常見的陷阱」或「智慧指標隱藏的陷阱」,智慧指標(名詞、靜態)自己不會有陷阱,是使用時才會有陷阱。
2:14:20讀完以下內容後,才發現這標題該翻成「使用智慧指標要注意的事項」或「使用智慧指標容易忽略的事項」就好!用「容易忽略」或「容易忘掉」來翻「pitfall」就好
2:4:01
正確使用智慧指標的方式:
1)不要用同一個內建指標的值去初始化或重設reset多個智慧指標。最好是一對一,就好。
2)不要在get成員函式回傳的指標上調用delete;也就是不要去刪除get傳回的指標指向的物件
3)不要用get成員函式回傳的指標去初始化或reset重設另一個智慧指標
4)如果要用到get成員函式回傳的指標,要切記該get上的智慧指標若都被摧毀了,就會刪除它們所指向的物件,而此回傳的普通指標也就會失效,成了懸置指標。
5)要用智慧指標而不是用new來管理資源的話,就要在建置這個智慧指標時同時傳給它一個適當的刪除器(deleter)以作為刪除該資源之用。
這裡的資源resource,就不僅指記憶體而言,而是與某種類別配置記憶體資源相關的了。(參見頁468、471)
If you use a smart pointer to manage a resource other than memory allocated by new, remember to pass a deleter (§ 12.1.4, p. 468, and § 12.1.5, p. 471).
如果你使用一個智慧指標來管理new所配置的記憶體以外的資源,記得傳入一個刪除器 (§12.1.4、§12.1.5)。
2:32:20
練習12.14
暫略
練習12.15
暫略
頁470
12.1.5. unique_ptr
3:1:40
unique_ptr擁有(獨佔)它所指的物件
不同於shared_ptr,在同一時間內,有且只有單一一個unique_ptr智慧指標可以指向某一物件
表12.4列出了unique_ptr獨有的運算
Table 12.4. unique_ptr Operations (See Also Table 12.1 (p. 452))
表12_4 : uniqae_ptr的運算(也請參照表12.1 )
unique_ptr<T> u1 unique_ptr<T, D> u2 可以指向型別為T的物件的null unique_ptr。u1會使用 delete來釋放其指標;u2會使用一個型別為D的可呼叫物件來 釋放其指標。Null unique_ptrs that can point to objects of type T. u1 will use delete to free its pointer; u2 will use a callable object of type D to free its pointer.
unique_ptr<T, D> u(d) 指向型別為T並使用d來取代delete的物件的null unique_ ptr,d必須是型別為D的一個物件。Null unique_ptr that point to objects of type T that uses d,which must be an object of type D in place of delete.
u = nullptr 删除u所指的物件;使得u變為null。Deletes the object to which u points; makes u null.
u.release() 放棄u所持有的指標之控制權;回傳u所持有的指標,並使u變 為 null。Relinquishes control of the pointer u had held; returns the pointer u had held and makes u null.
u.reset ()
u.reset(q)
u.reset(nullptr) 刪除u所指的物件。Deletes the object to which u points;
如果提供了內建指標q,就讓u指向那個物件。If the built-in pointer q is supplied, makes u point to that object.
否則使u變為null。Otherwise makes u null.
3:30:30
unique_ptr並沒有shared_ptr類似的make_shared程式庫函式可用
當我們定義一個unique_ptr時,我們實際上是將它繫結(bind)至一個new回傳回來的指標上
和shared_ptr一樣 ,unique_ptr也必須以直接初始化(direct initialize)來初始化
unique_ptr<double> p1; // unique_ptr that can point at a double
unique_ptr<int> p2(new int(42)); // p2 points to int with value 42
參見Table 12.1. Operations Common to shared_ptr and unique_ptr
Table 12.4. unique_ptr Operations (See Also Table 12.1 (p. 452))
因為unique_ptr對它所指物件的獨佔性,所以它並不提供一般的拷貝和指定運算
unique_ptr<string> p1(new string("Stegosaurus"));
unique_ptr<string> p2(p1); // error: no copy for unique_ptr
unique_ptr<string> p3;
p3 = p2; // error: no assign for unique_ptr
因為一經拷貝參考計數(reference count)會加減,有違unique之旨;此二性質乃互斥者!
正因為沒有拷貝與指定這樣的運算,所以unique_ptr提供了release這樣的成員函式/方法,來將一個非常值(nonconst)的unique_ptr的對所指物件的控制權或所有權轉移至另一個unique_ptr。這種運算,也可以配合reset成員函式來操作:
// transfers ownership from p1 (which points to the string Stegosaurus) to p2
unique_ptr<string> p2(p1.release()); // release makes p1 null
unique_ptr<string> p3(new string("Trex"));
// transfers ownership from p3 to p2
p2.reset(p3.release()); // reset deletes the memory to which p2 had pointed
頁471
3:51:10
p2.release(); // WRONG: p2 won't free the memory and we've lost the pointer
auto p = p2.release(); // ok, but we must remember to delete(p)
4:1:03
Passing and Returning unique_ptrs
我們可以拷貝或指定即將被摧毀的 unique_ptr
最常見的例子就是當我們由一個函式回傳一個unique_ptr時,這個unique_ptr就可以被拷貝
unique_ptr<int> clone(int p)
{
// ok: explicitly create a unique_ptr<int> from int*
return unique_ptr<int>(new int(p));
}
unique_ptr<int> clone(int p)
{
unique_ptr<int> ret(new int(p));
// . . .
return ret;
}
在要回傳的unique_ptr要摧毀前編譯器會做出一種特殊的拷貝動作,將在§13.6.2(頁534)再討論
4:13:20
Backward Compatibility: auto_ptr
對舊版的相容
回溯相容性:auto_ptr
早期程式庫中有一個叫auto_ptr這樣的類別,具有和unique_ptr部分類似的功能
這種型別是無法儲存在容器(container)中的,也不能在函式中加以回傳
雖然它還是標準程式庫的一部分,但應該盡量改用unique_ptr才好
Passing a Deleter to unique_ptr
傳入刪除器給unique_ptr
和shared_ptr一樣,unique_ptr預設也是用delete來釋放所指物件所佔用的記憶體資源
然而unique_ptr對刪除器(deleter)的處置是和shared_ptr有所不同的。其原因在§16.1.6(頁676)會加以說明
頁472
Overridding the deleter in a unique_ptr affects the unique_ptr type as well as how we construct (or reset) objects of that type.
覆寫一個unique_ptr中的刪除器會影響到unique_ptr型別,以及我們建構(或reset)該型別物件的方式。
類似於覆寫關聯式容器的比較運算(§11.2.2,頁425),我們必須在角括號 (angle brackets)內提供刪除器型別,連同unique_ptr能夠指的型別。我們會在創建或 reset這種型別的物件時提供指定型別的一個可呼叫物件:
在角括弧內同時提供刪除器(deleter)的型別及unique_ptr可以指向的型別
// p points to an object of type objT and uses an object of type delT to free that object
// it will call an object named fcn of type delT
unique_ptr<objT, delT>p(new objT, fcn);
4:33:50
將前connection的程式,改用unique_ptr來寫:
void f(destination &d /* other needed parameters */)
{
connection c = connect(&d); // open the connection
// when p is destroyed, the connection will be closed//因為p是unique參考計數(reference count)恆定為1
unique_ptr<connection, decltype(end_connection) *>
p(&c, end_connection);
// use the connection
// when f exits, even if by an exception, the connection will be properly closed
}
decltype參見頁70、250。decltype回傳的是函式型別(function type),而非對函式型別的指標,故後要再加「*」。
4:42:47
練習12.16
using namespace std;
int main() {
unique_ptr<int>up(new int(12));
unique_ptr<int>up1=up;
}
Severity Code Description
Error (active) E1776 function "std::unique_ptr<_Ty, _Dx>::operator=(const std::unique_ptr<_Ty, _Dx> &) [with _Ty=int, _Dx=std::default_delete<int>]" (declared at line 1915 of "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.23.28105\include\memory") cannot be referenced -- it is a deleted function
Severity Code Description
Error (active) E1776 function "std::unique_ptr<_Ty, _Dx>::unique_ptr(const std::unique_ptr<_Ty, _Dx> &) [with _Ty=int, _Dx=std::default_delete<int>]" (declared at line 1914 of "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.23.28105\include\memory") cannot be referenced -- it is a deleted function
4:59:10
練習12.17
5:12:50
using namespace std;
int main() {
int ix = 1024, * pi = &ix, * pi2 = new int(2048);
typedef unique_ptr<int> IntP;
//IntP p0(ix);//(a) :不能直接用int來初始化
IntP p2(pi2);//(c) :要用new回傳的pointer
//IntP p1(pi);//(b)竟然連普通取址運算子回傳的指標也可以。只有在編撰時才行;若執行,仍會出錯!
//IntP p3(&ix);//(d)和(b)是一樣的:prog1.exe has triggered a breakpoint.occurred
//IntP p4(new int(2048));//(e)和(c)一樣
IntP p5(p2.get());//(f) 和(b)(d)是一樣的,因為p2.get()回傳的是一般指標,不會new回傳的指標:prog1.exe has triggered a breakpoint. occurred
}
5:17:55
練習12.18
shared_ptr就是一直share出去的,不怕重複、複本,當然不必release;而unique_ptr因為僅只能一個對應到它所指向的動態配置記憶體,所以才必須有release將其所指向的記憶體作轉移用。
頁473
12.1.6. weak_ptr
weak這裡或許可以翻成「柔性的、軟式的、鴿派的」,果然,它不會去決定(判定)它所指物件的「生死」:
A weak_ptr (Table 12.5) is a smart pointer that does not control the lifetime of the object to which it points.
它所指之物件之生死反而是由shared_ptr來決定的
將一個weak_ptr繫結(bind)至shared_ptr並不會改變該shared_ptr的參考計數
參考計數(reference count)
不影響它繫結(bind)的shared_ptr的參考計數
和shared_ptr一樣,可以有好幾個weak_ptr同時指向一個物件,但與shared_ptr不同,weak_ptr並不會主宰它所指物件的生死,反而是由它所繫結(bind)的shared_ptr來主宰,且當隨著shared_ptr 參考計數歸零或shared_ptr都消亡後,該物件也消亡,而指向該物件的所有的weak_ptr卻仍會存在,不會隨之消亡。由此意義上,才叫做weak_ptr。它與它指向的物件是一種弱關聯的關係
即使還有weak_ptr指向該物件,該物件也不會甩它們,兀自消亡去
That object will be deleted even if there are weak_ptrs pointing to it—hence the name weak_ptr, which captures the idea that a weak_ptr shares its object “weakly.”
5:36:00 5:38:57
Table 12.5. weak_ptrs
表 12.5 : weak_ptr
weak_ptr<T> w 能夠指向型別為T的物件的null weak_ptr。
weak_ptr<T> w(sp) 指向與shared_ptr sp相同物件的weak_ptr。T必須可轉換為 sp所指的型別。
w = p p可以是一個shared_ptr或weak_ptr。指定之後,w會與p共享所有權。
w.reset() 讓w變為null。
w.use_count() 與w共享所有權的shared_ptr數目。
w.expired() 如果w.use_count()是零,就回傳true,否則為false。
w.lock() 如果expired為ture,就回傳一個null shared_ptr,否則回傳一個 shared_ptr給w所指的物件。
中文版此表排版有誤
weak_ptr<T> w
weak_ptr<T> w(sp)
建構一個weak_ptr是藉由一個shared_ptr來初始化
auto p = make_shared<int>(42);
weak_ptr<int> wp(p); // wp weakly shares with p; use count in p is unchanged
這裡的「use count」就是參考計數(reference count),可見「use_count()」成員函式所回傳的就是參考計數之值
中文版這裡又翻成「計數參考」,可能是筆誤爾
就是因為weak_ptr無法掌握它所指物件的生死權,所以我們並不能直接用weak_ptr對其所指物件來存取,若要存取該物件,則須用成員函式(方法method)lock ,這個方法會先檢查叫喚它的(call on) weak_ptr所指向的物件是否還存在,如果於物件仍然有效(存在),那麼lock就會回傳一個指向該物件的shared_ptr來
6:3:00
這裡英文版有誤:
if (shared_ptr<int> np = wp.lock())//因為null指標的值為0,所以條件式才能成立
{ // true if np is not null
// inside the if, np shares its object with p
}
「p」應該是「wp」
中文版也未訂正,真是「愚忠」翻譯啊
Checked Pointer Class
經過檢查的指標類別
8:44:40 中文版這樣翻好像是這個是個指標類別,可以實際上從下文看來,似非如此。
已檢查過的指標類別
指標經過檢查的類別
但下文又有:
If check succeeds, p is a shared_ptr to the vector to which this StrBlobPtr points.
那麼StrBlobPtr還真像一個指向vector的指標了。因為它的成員函式check回傳的和StrBlob不同(StrBlob不回傳void void check(size_type i, const std::string& msg) const;);它回傳的是一個智慧指標(smart pointer)shared_ptr!
可見調用一個類別物件,往往並非直接使用該類別,而是想要用到該類別定義的一些特殊運算或資料(也難怪class或struck叫做資料結構 Data Structures 了)
也因此,這個「Checked Pointer Class」標題應該是翻成
「能/會主動、自主性地檢查其指標有效性的類別」或者
「能/會主動、自主性地檢查其自身指標有效性的指標類別」
既然它的指標會經過檢查,自然它的指標就是「checked」英文的已經(過去式)是這樣的意義!不能呆呆地直接翻成過去式或已經語氣!翻譯學 9:14:00
頁474
class StrBlobPtr
6:20:20
參考Defining the StrBlob Class 頁456
唯StrBlob必須加上以下這行:
friend class StrBlobPtr;//頁269-270,279-280;不加「class」的就會當作函式編譯
這裡的blob意思當是這個:
二進位大型物件- 维基百科,自由的百科全书
二進位大型物件(英語:binary large object ,或英語:basic large object,縮寫為Blob、BLOB、BLOb),在資料庫管理系統中,將二進位資料儲存為一個單一個體的集合 ...
Blob (物件) 儲存體簡介- Azure 儲存體| Microsoft Docs
https://docs.microsoft.com › storage › blobs › storage-blobs-introduction
Azure Blob 儲存體可儲存大量的非結構化物件資料,例如文字或二進位資料。 Azure Blob 儲存體具有高度擴充性與可用性。 用戶端可從PowerShell ...
可見有時不能光查字典,得看看一般文章中或參考資料裡,人家是怎麼用該字的
⑨對錯問題,回到二作
須複習參考前面有關friend(建立朋友關係)的論述
// StrBlobPtr會在試著存取一個不存在的元素時擲出一個例外
class StrBlobPtr
{
public:
StrBlobPtr() : curr(0) {}
StrBlobPtr(StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {}
std::string& deref() const;
StrBlobPtr& incr(); // 前缀版本(prefix version)
private:
//如果檢查成功,check會回傳一個shared_ptr指向vector
std::shared_ptr<std::vector<std::string>>
check(std::size_t, const std::string&) const;
//儲存一個weak_ptr,這表示底層的vector可能被摧毁了
// store a weak_ptr, which means the underlying vector might be destroyed
std::weak_ptr<std::vector<std::string>> wptr;
std::size_t curr; //在陣列中的目前位置// current position within the array
};
6:55:40 若未提供初始器串列(initializer list)則會:
implicitly initializes wptr as a null weak_ptr.
所以對weak_ptr的預設初始化即是一個空的智慧指標null_ptr
We use a default argument (§ 6.5.1, p. 236) to initialize curr to denote the first element by default.
As we’ll see, the sz parameter will be used by the end member of StrBlob.
中文版翻譯又有誤:
我們使用一個預設引數(§6.5.1)來將curr預設初始化為代表第一個元素。如我們所見,sz參數會被 StrBlob的end成員所用。
目前尚未見「end」此資料成員(data member)也。8:7:00 應當翻成「如我們之後(頁475)會見到的,」。可見譯者在此未盡心碻譯!9:42:00
8:8:40
要注意並不能將StrBlobPtr和常值的StrBlob聯繫在一塊
這個限制只是因為StrBlobPtr的第2個建構器(constructor)帶的是一個對非常值(nonconst)StrBlob的參考:
StrBlobPtr(StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {}
中文版為了顧及原文,翻得文謅謅的:
值得注意的是,我們無法將一個StrBlobPtr繫結至一個const StrBlob物件。這種限制源自於「建構器接受對型別為StrBlob的非const物件的一個參考」這個事實。
參考本人此帖論述。https://www.facebook.com/oscarsun72/posts/2416659278445192
可不可以「in English」,一樣地「in Chinese」不要那麼洋涇浜!又不是在做文學翻譯,非得一句一字忠於原文呈現。「依義不依語」!
翻譯學 8:22:00
8:35:00
std::shared_ptr<std::vector<std::string>>
StrBlobPtr::check(std::size_t i, const std::string &msg)
const
{
auto ret = wptr.lock(); // is the vector still around?
if (!ret)
throw std::runtime_error("unbound StrBlobPtr");
if (i >= ret->size())
throw std::out_of_range(msg);
return ret; // otherwise, return a shared_ptr to the vector
}
8:32:00
這裡就可理解weak_ptr中的lock成員函式的意思就是weak_ptr有沒有對應到的物件,有的話,就會lock起來(和該物件繫結(bind))。
8;44:03
頁475
Pointer Operations
指標運算
這裡也是,翻譯成「指標運算」還以為是什麼了不起的新東西,看了以下「實作」的內容,也不過就是一些已經學過的舊玩意兒,其實在講的也不過就是「和指標相關的運算」或「針對指標進行運算」 ⑧找對主詞
本書第14章會教怎麼定義自己的運算子
8:59:40
std::string &StrBlobPtr::deref() const//參見練習12.21
{
auto p = check(curr, "dereference past end");
return (*p)[curr]; // (*p) is the vector to which this object points
}
因為StrBlobPtr所具的資料(個性)及運算、操縱(行為模式)都如指標所具之特性,故謂之指標類別。
// prefix: return a reference to the incremented object
StrBlobPtr &StrBlobPtr::incr()
{
// if curr already points past the end of the container, can't increment it
check(curr, "increment past end of StrBlobPtr");
++curr; // advance the current state
return *this;
}
9:31:00
看吧!這時才出現end!
這裡pdf舊版又與google圖書版不同了:
Of course, in order to access the data member, our pointer class will have to be a friend of StrBlob (§ 7.3.4, p. 279). We’ll also give our StrBlob class begin and end operations that return a StrBlobPtr pointing to itself:
新版(google圖書版):
We’ll also give our StrBlob class begin and end operations. These members will return StrBlobPtrs pointing to the first or one past the last element in the StrBlob itself. In addition, because StrBlobPtr accesses the data member of StrBlob , we must also make StrBlobPtr a friend of StrBlob (§ 7.3.4 , p. 279 ):
這裡「we must also make」原文未空格,成了「wemust alsomake」
這裡中文版是照新版翻:
我們也賦予我們的StrBlob類別begin和end運算。這些成員會回傳StrBlobPtr指向 StrBlob中的第一個元素或超出最後一個元素一個位置處。此外,因為StrBlobPtr會存取 StrBlob 的 data 成員,我們也必須使 StrBlobPtr 是 StrBlob 的一個 friend ( § 7.3.4 ,頁279):
可見「StrBlob」是藉由程式庫型別(library type)vector來建構的一個自訂容器(尤其是由動態配置記憶體配置的一個容器)
程式碼也不同,此乃pdf舊版:
// forward declaration needed for friend declaration in StrBlob
class StrBlobPtr; //可以重複宣告,卻不能重複定義;若無此行,則StrBlob中成員函式用 到StrBlobPtr都會在編譯時期出錯「use of undefined type 'StrBlobPtr'」
class StrBlob
{
friend class StrBlobPtr;
// other members as in § 12.1.1 (p. 456)
// return StrBlobPtr to the first and one past the last elements
StrBlobPtr begin() { return StrBlobPtr(*this); }//都是用第2個解構器(destructor),此乃用預設引數(default argument)
StrBlobPtr end()
{
auto ret = StrBlobPtr(*this, data->size());
return ret;
}
};
完整的程式碼要看這裡:page474_class_StrBlobPtr 10:14:00
如果直接在StrBlobr中用「StrBlobPtr」作為定義(函式的主體內容),則會出如上開之錯錯
新版的(google圖書)就依此作出了訂正:
class StrBlob
{
friend class StrBlobPtr;
// other members as in § 12.1.1 (p. 456)
StrBlobPtr begin(); // return StrBlobPtr to the first element
StrBlobPtr end(); // and one past the last element
};
// these members can’t be defined until StrStrBlob and StrStrBlobPtr are defined
StrBlobPtr StrBlob::begin() { return StrBlobPtr(*this); }
StrBlobPtr StrBlob::end() { return StrBlobPtr(*this, data->size()); }
結果「StrStrBlob and StrStrBlobPtr」還是拼錯,可見新版出版之急就章。當作「StrBlob and StrBlobPtr」!
10:19:30
頁476
練習12.19
參見前面練習12.2
練習12.20
練習12.21
10:44:00
原來(課文正文)的版本比較清楚,但若顧慮行內函式(inline function),則可考慮如此寫法:
std::string &deref() const
{ return (*check(curr, "dereference past end"))[curr];}
10:53:00
練習12.22
StrBlobPtr(StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {}
改成
StrBlobPtr(const StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {}
即可
Caution: Writing Exception Safe Code is Hard
Programs that properly “clean up” during exception handling are said to be exception safe.
跳過部分的程式可能意味著一個物件 會處在無效或不完整的狀態,或有某項資源未釋放,諸如此類的。會在例外處理過程中進行適 當「清理(clean up)」的程式,我們就稱之為exception safe。
Writing exception safe code is surprisingly hard, and (largely) beyond the scope of this language Primer.
surprisingly hard出乎意料地難,而且遠遠超過(largely) beyond我們這種基礎課程所能討論/涵蓋(cover)的範圍 第80集 14:00
第80集16:00
某些程式使用例外單純是為了在有例外情況發生時終結程式。這種程式一般不會擔心例外的安全性。
當然,因為程式終了,通常就會被釋出記憶體了。(乃由作業系統管理記憶體,就不需要程序員/程式設計師來擔心)
會處理例外並繼續執行的通常都必須一直注意是否可能發生例外,以及程式必須做什麼來確保 物件處於有效狀態、資源不會外漏,還有程式如何回復到適當的狀態。
這裡的資源都不出記憶體的範疇。資源外漏,就如在後面動態記憶體(dynamic memory)章裡談到的記憶體外洩:記憶體洩漏memory_leak
我們偶爾會指出一些常用來提升例外安全性的特殊技巧。
第80集 25:50 諸如「智慧指標(smart pointer)」
然而,程式需要穩固的例外處理的讀 者應該注意到,我們所涵蓋的技巧本身並不足以達到滿足例外安全性的需求。
程式需要穩固的例外處理的讀 者應該注意到,我們→讀這本書的讀者,若是您的程式需要一個可靠又穩當的例外處理程序,會發現本書…… 這就是中文能力有差別,影響翻譯品質好壞的例證。
7.3.4. Friendship Revisited
再談一談朋友關係or 更進一步了解朋友關係第80集7:43:00
7.3.4重訪朋友關係
除了函式之外,類別也可以互為朋友
還可以宣告一個在它之前已經定義好的類別中的成員函式作為它的朋友
Friendship between Classes
類別之間的朋友關係
宣告類別為朋友,關鍵在friend後緊接著是class這個關鍵字 第80集7:44:10
頁280
It is important to understand that friendship is not transitive.
朋友的朋友沒有權限,一定要本人認可才行。就如臉書分享。朋友與「朋友的朋友」是有不同的權限的。
transitive就是推己及人、愛屋及烏的「及」。
遞移性(transitive)、轉讓、讓渡 第80集7:32:30
留言