提问人: 提问时间:3/28/2023 最后编辑:General Grievance 更新时间:3/31/2023 访问量:82
关于const(成员和函数)的建议
Advice about const (member and function)
问:
我正在研究树探索,我为节点实现了一个类“CNode”,其中每个节点都有一个指向其父节点的指针成员,我的问题是关于 const 函数和 const 成员,请参阅下面的代码。
所以下面是 main 和 class 的简化版本。
class CNode
{
private:
int m_nID;
CNode* m_pParent;
public:
CNode() : m_nID(0), m_pParent(nullptr) {}
CNode(int n, CNode* pParent) : m_nID(n), m_pParent(pParent) {}
CNode* GetParent() const { return m_pParent; }
void PrintID() const { cout << m_nID << endl; }
CNode* CreateChild(int n)
{
CNode* pNode = new CNode(n, this);
return pNode;
}
};
int main()
{
CNode* pNodeRoot = new CNode(0, nullptr);
CNode* pNode = new CNode(1, pNodeRoot);
while (pNode != nullptr)
{
pNode->PrintID();
pNode = pNode->GetParent();
}
delete pNodeRoot;
delete pNode;
return 0;
}
所以这是有效的,但我不满意,因为成员函数:
CNode* CreateChild(int n)
应该是 const,所以让我们把它写成 const,但因为它使用“this”,所以我必须把成员“m_pParent”const(这似乎很正常,因为在我的情况下,节点不会改变父节点),最后它看起来像这样:
class CNode
{
private:
int m_nID;
const CNode* m_pParent;
public:
CNode() : m_nID(0), m_pParent(nullptr) {}
CNode(int n, CNode* pParent) : m_nID(n), m_pParent(pParent) {}
CNode* CreateChild(int n) const
{
CNode* pNode = new CNode(n, const_cast<CNode*>(this));
return pNode;
}
const CNode* GetParent() const { return m_pParent; }
void PrintID() const { cout << m_nID << endl; }
};
然后我必须使用const_cast更改 main 中的一行:
pNode = const_cast<CNode*>(pNode->GetParent());
它也在起作用。
两个版本都有效,我希望您的建议知道:
- 什么是最佳实践,
- 如果使用const_cast可以或不可以(我认为这是为了尽可能避免),
- 如果有另一种方法(更简单或更整洁)来做到这一点。
谢谢。
答:
不能让一个成员同时是可变的。你必须选择任何一个并承担后果。恒常性有点病毒式的东西。如果在任何地方公开为非指针,则它一定不是 。更改值是未定义的行为。const
m_pParent
const
const
const
然而,你可以随心所欲地投下心来。因此,您可以拥有一个可变对象并决定只分发一个指针。const
CNode* m_pParent
const
It is possible to overload on constness, so you could have two getters like this:
CNode* m_pParent; // no const before the *!
const CNode* GetParent() const;
CNode* GetParent();
The non- overload could only be called on a non-.const
const CNode
One of the reasons was introduced in the first place was to keep programmers from accidentally changing their own variables. See it as a tool in your tool box. So when the compiler complains about const violations, you should listen.const
In your case, it was because the child's would expose a mutable pointer and thus violate constness.GetParent()
Let's assume there's non- method and look at the following code:const
CNode::SetID(int)
const CNode foo; // can only call const methods on foo
foo.SetID(123); // ERROR! const violation!
foo.CreateChild(1)->GetParent()->SetID(123); // ERROR! you would change foo through its child.
Because it was brought up in a comment: the meaning of in relation to is: . It's in alphabetical order (data, pointer) if that serves as a mnemonic.const
*
const data * const pointer
下面是代码片段的 const-correct 版本,其中包含:const CNode* m_pParent
class CNode
{
private:
int m_nID;
const CNode* const m_pParent; // This is just a freebie.
public:
CNode() : m_nID(0), m_pParent(nullptr) {}
// Ctor now accepts a const CNode*. Everything is const now, no need for a const_cast
CNode(int n, const CNode* pParent) : m_nID(n), m_pParent(pParent) {}
CNode* CreateChild(int n) const
{
CNode* pNode = new CNode(n, this); // We use "this" without a const_cast :)
return pNode;
}
const CNode* GetParent() const { return m_pParent; }
void PrintID() const { std::cout << m_nID << "\n"; }
};
或者自己尝试一下 https://godbolt.org/z/4xs15P38a
另一方面,如果你需要有一个,从而向父级公开一个可变指针,那么就不能是简单明了的。CNode* GetParent()
CreateChild()
const
你又问了三个问题:
什么是最佳实践
尽可能使用,但不要超过这个频率。const
使用const_cast是否可以(我认为这是为了尽可能避免)
是的。根据经验:避免它!它是语言规范的一部分,并且有合法的用途。它比 C 型铸件更可取。
如果有另一种方法(更简单或更整洁)来做到这一点。
你必须决定是否要成为,并遵循其中一个决定以及它带来的一切。m_pParent
const
评论中提出的新问题的解决方案:https://godbolt.org/z/1crbxMxco
如果方法返回 a,则需要 a 来分配返回值。只有一个隐式演员表(出于显而易见的原因)。const T*
const T*
const
评论
const_cast
pParent
const
const_cast
m_pParent
CNode* CreateChild(int n) const
m_Parent
const
*m_Parent
CNode *const``, not a