I have 3 classes. In it's simplest form, it looks like,
class tree
{
public:
tree_node* find_node(const std::string& text) {
return factory.find(text);
}
private:
tree_node_factory factory;
}
class tree_node
{
public:
tree_node(const std::string& text) : text_(text) {}
const std::string& text() const {
return text_;
}
void set_parent(const tree_node* new_parent);
private:
std::string text_;
}
class tree_node_factory
{
publ开发者_如何学JAVAic:
tree_node* find(const std::string& text);
private:
std::vector<tree_node*> allocated_nodes;
}
I don't want to allow users of tree
to modify the tree_node
returned by methods like find_node
. So I changed, find_node
and tree_node_factory::find
to,
const tree_node* find_node(const std::string& text) const {
return factory.find(text);
}
const tree_node* find(const std::string& text) const;
Problem is tree
internally should be able to modify the nodes and work on methods like set_parent
. But since factory returns only const
nodes, I ended up with adding another overload (non const version) of find
into the factory.
tree_node* find(const std::string& text);
I am wondering is this the correct way to handle these kind of problems? I see the code is getting duplicated in the const and non-const versions.
Any thoughts..?
Item 3 in Scott Meyers' book Effective C++ demonstrates a method to remove this code duplication. Basically, in your non-const function you will add const to this
, call the const version, then cast the const away. This is safe; though writing to a const-variable leads to undefined behavior, because this
was originally non-const it's okay.
Example:
const std::string& operator[](size_t index) const
{
// some other code
// since `this` isn't really const, this is modifiable
return mData[index];
}
std::string& operator[](size_t index)
{
return const_cast<std::string&> // (3) take const off result
(static_cast<const my_type&> // (1) add const
(*this)[index]); // (2) use const version
}
Normally it would all be on one line. You can also make a utility for it.
Note a caveat: if the const version returns a "real" const object, this method clearly results in undefined behavior. The constness of the return value must be reflected by the constness of the object referred to by this
. This is broken code:
const std::string& operator[](size_t index) const
{
static const std::string constString = "Don't modify me.";
if (index == 0)
{
// even though `this` isn't really const, this is NOT modifiable
return constString;
}
return mData[index - 1];
}
std::string& operator[](size_t index)
{
return const_cast<std::string&> // (3) !!! take const off result !!!
(static_cast<const my_type&> // (1)
(*this)[index]); // (2)
}
In practice, we avoid global state so this is rarely an issue. It's trivial to check, anyway.
Unfortunately C++ has no tools (besides macros) for eliminating source code duplication in function overloads that look largely identical but differ in constness. You can, however, implement one of the functions using the other and const_cast
.
精彩评论