3

const_cast我有一个关于 STL 容器的简单问题和最佳实践。考虑以下,其中类Foo有一个私有 STL std::mapfrom Widget*to int

宣言:

#include <map>  
using std::map;

class Widget;

class Foo {
public:
     Foo(int n);
     virtual ~Foo();

     bool hasWidget(const Widget&);

private:
     map<Widget*,int> widget_map;
};

定义:

#include <map>
#include "Foo.h"
#include "Widget.h"

using std::map;

Foo::Foo(int n)
{
     for (int i = 0; i < n; i++) {
          widget_map[new Widget()] = 1;
     }
}

Foo::~Foo()
{
     map<Widget*, int>::iterator it;
     for (it = widget_map.begin(); it != widget_map.end(); it++) {
          delete it->first;
     }
}

bool Foo::hasWidget(const Widget& w)
{
     map<Widget*, int>::iterator it;
     it = this->widget_map.find(const_cast<Widget*>(&w));
     return ( ! ( it == widget_map.end() ) );
}

鉴于hasWidget将引用 const 作为其参数,因此在调用时需要丢弃 const map::findwiget_mapWiget*to int)。据我所知,这种方法既明智又可取——但如果没有更有经验的 C++ 程序员的反馈,我不愿意接受它。

在我看来,这是为数不多的const_cast适当使用的案例之一,因为我们将强制转换的结果传递给 STL 方法。我对么?

我意识到这个问题的其他排列已经提出(例如,const_cast for vector with object)但似乎没有一个直接解决上述问题。

提前致谢。

4

6 回答 6

1

为什么不hasWidget换取一个Widget*?该界面目前是狡猾的,因为它暗示您是在底层地图中按值查找小部件,而实际上您是按地址查找它们。该方法也应该是const,我认为:

bool hasWidget(Widget *) const;
于 2010-11-30T11:04:56.437 回答
1

一个键是指针的映射是笨拙的 - 查找它的唯一方法是使用相同的指针。为此,您必须保证hasWidget将使用具有相同地址的对象调用该方法!

当然,您应该Widget正确实施,以使其具有正确的运算符重载以充当 a 中的键std::map!在您的地图中,您可以简单地拥有:

std::map<Widget, int>

然后你的 find 不需要 a const_cast

于 2010-11-30T11:05:20.907 回答
1

是的,这是合理使用const_cast<>. 您应该考虑制作 hasWidget const

于 2010-11-30T10:56:32.627 回答
1

我想我会通过我的回答陷入“主观和争论”,但我会试一试......

我并不害怕const_cast,但我对你的设计持怀疑态度。成员函数hasWidget通过 const ref 获取其参数:这对客户端说了什么?从客户的角度来看,如果我不知道实现,我可能会认为每个Widget都是通过值与参数进行比较的。对我来说,界面并没有反映实际行为,它比较了Widget by address

例如,当前签名允许传递临时签名,尽管在这种情况下Widget永远不会返回值。true我会亲自将签名更改为(请注意,我添加了 a const):

bool hasWidget(const Widget *) const;
于 2010-11-30T10:55:22.537 回答
1

为什么不使用map<const Widget*,int>? 您似乎从未修改过地图中任何键指向的 Widget。

假设有充分的理由,那么是的,我认为你是对的。当调用保证不修改指针的引用的代码时,丢弃 const 是安全的。由于指针容器的模板化方式,它们的任何函数都不会直接修改该引用,但如果包含的类型是 const 指针,那么用户也无法修改引用(没有 const 强制转换)。在搜索之前抛弃 const 肯定比在修改之前抛弃 const 更安全,如果它必须是两者之一......

顺便说一句,hasWidget如果您使用count而不是find. 通常(在这种情况下不是)使用 const 也稍微安全一些count,因为find这样const_cast会返回一个可用于修改 Widget 的迭代器,而count不会。所以你不必担心返回值会发生什么count。显然,无论如何,返回值都完全在控制之下。

于 2010-11-30T10:55:35.220 回答
1

这对我来说看起来很笨拙。通过物理地址识别对象非常“特殊”,诚然它是独一无二的,但也很奇怪。

我会强烈考虑反转地图:

std::map<Widget::Id, Widget*>

whereWidget::Id可能只是一个int或类似的。

那么 const-ness 就不会有任何问题。

要深入研究,您还可以查看Boost Pointer Container库:

boost::ptr_map<Widget::Id, Widget>

这将缓解内存管理问题。

于 2010-11-30T14:26:53.903 回答