對于不打算用作提供給外部客戶端的 API 的一部分的嚴格內部類,將類指標成員變數初始化為自身而不是NULL或是否有任何內在的邪惡nullptr?
請參閱下面的代碼作為示例。
#include <iostream>
class Foo
{
public:
Foo() :
m_link(this)
{
}
Foo* getLink()
{
return m_link;
}
void setLink(Foo& rhs)
{
m_link = &rhs;
// Do other things too.
// Obviously, the name shouldn't be setLink() if the real code is doing multiple things,
// but this is a code sample.
}
void changeState()
{
// This is a code sample, but play along and assume there are actual states to change.
std::cout << "Changing a state." << std::endl;
}
private:
Foo* m_link;
};
void doSomething(Foo& foo)
{
Foo* link = foo.getLink();
if (link == &foo)
{
std::cout << "A is not linked to anything." << std::endl;
}
else
{
std::cout << "A is linked to something else. Need to change the state on the link." << std::endl;
link->changeState();
}
}
int main(int argc, char** argv)
{
Foo a;
doSomething(a);
std::cout << "-------------------" << std::endl;
// This is a mere code sample.
// In the real code, I'm fetching B from a container.
Foo b;
a.setLink(b);
doSomething(a);
return 0;
}
輸出
A is not linked to anything.
-------------------
A is linked to something else. Need to change the state on the link.
Changing a state.
優點
將指標變數 初始化Foo::link為自身的好處是避免意外的 NULL 取消參考。由于指標永遠不能為NULL,那么在最壞的情況下,程式將產生錯誤輸出而不是分段錯誤。
缺點
然而,這種策略的明顯缺點是它似乎是非常規的。大多數程式員習慣于檢查 NULL,因此不期望檢查與呼叫指標的物件是否相等。因此,在針對外部消費者的代碼庫中使用這種技術是不明智的,也就是說,開發人員希望將此代碼庫用作庫。
結語
任何其他人的想法?有沒有其他人在這個主題上說過任何實質性的東西,尤其是在考慮 C 98 的情況下?請注意,我使用帶有以下標志的 GCC 編譯器編譯了此代碼:-std=c 98 -Wall并且沒有發現任何問題。
PS 請隨時編輯這篇文章以改進我在這里使用的任何術語。
編輯
- This question is asked in the spirit of other good practice questions, such as this question about deleting references.
- A more extensive code example has been provided to clear up confusion. To be specific, the sample is now 63 lines which is an increase from the initial 30 lines. Thus, the variable names have been changed and therefore comments referencing
Foo:pshould apply toFoo:link.
uj5u.com熱心網友回復:
開始時這是一個壞主意,但作為空取消參考的解決方案,這是一個可怕的想法。
您不會隱藏空取消參考。曾經。空取消參考是錯誤,而不是錯誤。當錯誤發生時,程式中的所有不變性都會消失,并且無法保證任何行為。不允許錯誤立即出現并不會使程式在任何意義上都是正確的,它只會混淆并使除錯變得更加困難。
除此之外,指向自身的結構是一個粗糙的蠕蟲罐。考慮您的副本分配
Foo& operator=(const Foo& rhs) {
if(this != &rhs)
return *this;
if(rhs->m_link != &rhs)
m_link = this;
else
m_link = rhs->m_link;
}
您現在必須檢查是否每次復制時都指向自己,因為它的值可能與它自己的身份相關聯。
事實證明,在很多情況下都需要進行此類檢查。swap應該如何實施?
void swap(Foo& x, Foo& y) noexcept {
Foo* tx, *ty;
if(x.m_link == &x)
tx = &y;
else
tx = x.m_link;
if(y.m_link == &y)
ty = &x;
else
ty = y.m_link;
x.m_link = ty;
y.m_link = tx;
}
假設Foo有某種指標/參考語意,那么你的相等性現在也很重要
bool operator==(const Foo& rhs) const {
return m_link == rhs.m_link || (m_link == this && rhs.m_link == &rhs);
}
不要指向自己。只是不要。
uj5u.com熱心網友回復:
Foo 負責自己的狀態。特別是它向用戶公開的指標。
如果你以這種方式公開一個指標,作為一個公共成員,這是一個非常奇怪的設計決定。在過去的 30 多年里,我的直覺告訴我,像這樣的指標并不是處理 Foo 狀態的負責任方式。
考慮為這個指標提供吸氣劑。
Foo* getP() {
// create a safe pointer for user
// and indicate an error state. (exceptions might be an alternative)
}
除非你分享更多關于 Foo 的背景關系,否則很難提供建議。
uj5u.com熱心網友回復:
將類指標成員變數初始化為自身而不是
NULLor有什么本質上的邪惡nullptr嗎?
不。但正如您所指出的,根據用例可能會有不同的考慮。
我不確定這在大多數情況下是否相關,但在某些情況下,物件需要持有其自己型別的指標,因此它實際上只與這些情況相關。
例如,單向鏈表中的元素將有一個指向下一個元素的指標,因此串列中的最后一個元素通常會有一個 NULL 指標,以表明沒有其他元素。所以使用這個例子,結束元素可以改為指向自己而不是 NULL 來表示它是最后一個元素。這實際上僅取決于個人實施偏好。
很多時候,當你過于努力地使其防崩潰時,最終可能會不必要地混淆代碼。根據情況,您可能會掩蓋問題并使問題更難除錯。例如,回到單鏈接的例子,如果使用了指向自身的指標初始化方法,并且程式中的一個錯誤試圖從串列中的末尾元素訪問下一個元素,則串列將回傳末尾再次元素。這很可能會導致程式永遠“遍歷”串列。這可能比簡單地讓程式崩潰并通過除錯工具找到罪魁禍首更難找到/理解。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/402943.html
標籤:
下一篇:我在C中的結構鏈表有問題
