C++核心準(zhǔn)則R.3: 原始指針(T*)不應(yīng)擁有所有權(quán)

R.3: A raw pointer (a?T*) is non-owning
R.3:?原始指針(T*)不應(yīng)擁有所有權(quán)
Reason(原因)
There is nothing (in the C++ standard or in most code) to say otherwise and most raw pointers are non-owning. We want owning pointers identified so that we can reliably and efficiently delete the objects pointed to by owning pointers.
這一點(diǎn)不存在任何例外(無(wú)論是C++標(biāo)準(zhǔn)還是大部分代碼)而且大多數(shù)原始指針就是沒(méi)有所有權(quán)的。我們需要所有權(quán)指針被定義出來(lái),這樣就可以可靠地,高效地刪除所有權(quán)指針指向的對(duì)象。
Example(示例)
void f()
{
int* p1 = new int{7}; // bad: raw owning pointer
auto p2 = make_unique(7); // OK: the int is owned by a unique pointer
// ...
}
The?unique_ptr?protects against leaks by guaranteeing the deletion of its object (even in the presence of exceptions). The?T*?does not.
unique_ptr通過(guò)(即使在發(fā)生異常的情況下)保證對(duì)象的刪除來(lái)防止內(nèi)存泄露。而T*不會(huì)。
Example(示例)
template
class X {
public:
T* p; // bad: it is unclear whether p is owning or not
T* q; // bad: it is unclear whether q is owning or not
// ...
};
We can fix that problem by making ownership explicit:
我們可以通過(guò)明確地賦予所有權(quán)來(lái)解決這個(gè)問(wèn)題。
template
class X2 {
public:
owner p; // OK: p is owning
T* q; // OK: q is not owning
// ...
}; Exception(例外)
A major class of exception is legacy code, especially code that must remain compilable as C or interface with C and C-style C++ through ABIs. The fact that there are billions of lines of code that violate this rule against owning?T*s cannot be ignored. We'd love to see program transformation tools turning 20-year-old "legacy" code into shiny modern code, we encourage the development, deployment and use of such tools, we hope the guidelines will help the development of such tools, and we even contributed (and contribute) to the research and development in this area. However, it will take time: "legacy code" is generated faster than we can renovate old code, and so it will be for a few years.
例外主要來(lái)源于既有代碼,特別是那些由于ABIs必須保持C兼容性或者包含C或C風(fēng)格C++接口的情況。事實(shí)上存在成百萬(wàn)行的代碼違反這條反對(duì)T*持有所有權(quán)的準(zhǔn)則,它們不能被忽略。我們很高興地看到軟件工具可以將20年以上的老代碼轉(zhuǎn)換為嶄新的現(xiàn)代C++代碼,我們鼓勵(lì)這類工具的開(kāi)發(fā)、部署和使用,我們甚至向這個(gè)領(lǐng)域的研究做出了貢獻(xiàn),而且將繼續(xù)做出貢獻(xiàn)。然而這需要時(shí)間:“既有代碼”的產(chǎn)生快于我們翻新舊代碼的速度,因此這個(gè)過(guò)程將會(huì)持續(xù)一些年。
This code cannot all be rewritten (even assuming good code transformation software), especially not soon. This problem cannot be solved (at scale) by transforming all owning pointers to?unique_ptrs and?shared_ptrs, partly because we need/use owning "raw pointers" as well as simple pointers in the implementation of our fundamental resource handles. For example, common?vector?implementations have one owning pointer and two non-owning pointers. Many ABIs (and essentially all interfaces to C code) use?T*s, some of them owning. Some interfaces cannot be simply annotated with?owner?because they need to remain compilable as C (although this would be a rare good use for a macro, that expands to?owner?in C++ mode only).
不可能所有的代碼都被重寫(即使存在優(yōu)秀的轉(zhuǎn)換軟件),很快重寫更不可能。這個(gè)問(wèn)題不可能通過(guò)將所有具有所有權(quán)指針轉(zhuǎn)換為unique_ptr和shared_ptr來(lái)解決,一部分的原因是基礎(chǔ)的資源持有者需要/使用具有所有權(quán)的原始指針(同時(shí)也是簡(jiǎn)單指針)。例如,普通的vector實(shí)現(xiàn)包含一個(gè)所有權(quán)指針和兩個(gè)非所有權(quán)指針。很多ABI(本質(zhì)上講所有面向C語(yǔ)言的接口)使用T*,其中有些具有所有權(quán)。因?yàn)樾枰S持C語(yǔ)言可編譯,因此有些接口不能簡(jiǎn)單地加注所有權(quán)(雖然這是很少見(jiàn)的宏的好用法,它只是在C++模式時(shí)展開(kāi)成為所有權(quán)指針)。
Note(注意)
owner
owner
Example, bad(反面示例)
Returning a (raw) pointer imposes a lifetime management uncertainty on the caller; that is, who deletes the pointed-to object?
返回一個(gè)(原始)指針會(huì)增加調(diào)用者生命周期管理的不確定性;即:誰(shuí)應(yīng)該銷毀指針指向的對(duì)象?
Gadget* make_gadget(int n)
{
auto p = new Gadget{n};
// ...
return p;
}
void caller(int n)
{
auto p = make_gadget(n); // remember to delete p
// ...
delete p;
}In addition to suffering from the problem from?leak, this adds a spurious allocation and deallocation operation, and is needlessly verbose. If Gadget is cheap to move out of a function (i.e., is small or has an efficient move operation), just return it "by value" (see?"out" return values):
這段代碼可以排除來(lái)自內(nèi)存泄漏問(wèn)題的痛苦,但是增加了虛假的分配和釋放操作和沒(méi)有必要的冗長(zhǎng)性。如果Gadget很容易移出函數(shù)(也就是說(shuō),很小或者存在高效的移動(dòng)操作),直接只用傳值(參照“輸出“返回值)。
Gadget make_gadget(int n)
{
Gadget g{n};
// ...
return g;
}Note(注意)
This rule applies to factory functions.
這條準(zhǔn)則適用于工廠函數(shù)。
Note(注意)
If pointer semantics are required (e.g., because the return type needs to refer to a base class of a class hierarchy (an interface)), return a "smart pointer."
如果需要指針語(yǔ)義(例如因?yàn)榉祷氐闹羔樞枰赶蝾悓哟沃械幕?某個(gè)接口)),返回一個(gè)智能指針。
Enforcement(實(shí)施建議)
(Simple) Warn on?delete?of a raw pointer that is not an?owner
. (簡(jiǎn)單)警告銷毀owner
類型之外的原始指針的情況。 (Moderate) Warn on failure to either?reset?or explicitly?delete?an?owner
?pointer on every code path. (中等)如果沒(méi)有在所有代碼路徑上重置或者銷毀onwer
指針,發(fā)出警告。 (Simple) Warn if the return value of?new?is assigned to a raw pointer.
(簡(jiǎn)單)如果new的返回值賦給原始指針,發(fā)出警告。
(Simple) Warn if a function returns an object that was allocated within the function but has a move constructor. Suggest considering returning it by value instead.、
(簡(jiǎn)單)如果一個(gè)函數(shù)返回的對(duì)象在函數(shù)內(nèi)分配內(nèi)存,但是包含移動(dòng)構(gòu)造函數(shù)。提出建議通過(guò)傳值方式返回。
原文鏈接:
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#r3-a-raw-pointer-a-t-is-non-owning
覺(jué)得本文有幫助?請(qǐng)分享給更多人。
關(guān)注【面向?qū)ο笏伎肌枯p松學(xué)習(xí)每一天!
面向?qū)ο箝_(kāi)發(fā),面向?qū)ο笏伎迹?/span>
