3

STL コンテナーに関する簡単な質問const_castとベスト プラクティスがあります。classFooにプライベート 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() ) );
}

が const への参照をパラメーターとして受け取ることを考えるとhasWidget、呼び出し時に constness をキャストする必要がありますmap::find( wiget_mapfrom Wiget*to int)。私が知る限り、このアプローチは賢明で望ましいものですが、経験豊富な C++ プログラマーからのフィードバックがなければ、このアプローチを受け入れるのは気が進まないのです。

const_castキャストの結果を STL メソッドに渡すことを考えると、これは適切に使用できる数少ないケースの 1 つに思えます。私は正しいですか?

この質問の他の順列がすでに提起されていることを認識しています(たとえば、object を使用した vector の const_cast)が、上記に直接対処しているようには見えません。

前もって感謝します。

4

6 に答える 6

1

私の答えは「主観的で論争的」に陥ると思いますが、試してみます...

私は恐れていませんconst_castが、あなたのデザインには懐疑的です。メンバー関数hasWidgetは、const refによってそのパラメーターを受け取ります:これはクライアントに何を言いますか?クライアントの観点から、実装がわからない場合は、それぞれがパラメーターを使用して値Widgetで比較されていると思います。私の場合、インターフェイスは、アドレスごとに比較する実際の動作を反映していません。Widget

たとえば、現在の署名では一時的なWidgetものを渡すことができますが、この場合は戻り値を渡すことはできませんtrue。私は個人的に署名を次のように変更します(私が追加したことに注意してくださいconst):

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

使ってみませんmap<const Widget*,int>か?マップ内のキーが指すウィジェットを変更することはないようです。

正当な理由があると仮定すると、そうです、あなたは正しいと思います。ポインタの参照を変更しないことが保証されているコードを呼び出すときは、constをキャストしても安全です。ポインターのコンテナーがテンプレート化される方法のため、それらの関数のいずれもその参照を直接変更することはありませんが、含まれる型がconstポインターである場合、ユーザーは参照を変更することもできません(constキャストなし)。2つのうちのいずれかでなければならない場合は、変更する前にconstをキャストするよりも、検索する前にconstをキャストする方が確かに安全です...

ところで、では​​なくhasWidgetを使用すると短くなります。また、一般的に(この場合ではなく)使用する方がわずかにconst-safeです。これを使用すると、ウィジェットの変更に使用できるイテレーターが返されますが、そうではありません。したがって、の戻り値がどうなるかを心配する必要はありません。明らかにここでは、その戻り値はとにかく完全に制御されています。countfindcountfindconst_castcountcount

于 2010-11-30T10:55:35.220 に答える
1

はい、それはの合理的な使用法ですconst_cast<>。hasWidgetの作成を検討する必要がありますconst

于 2010-11-30T10:56:32.627 に答える
1

に変えhasWidgetてみませんWidget*か?実際にはアドレスでウィジェットを探しているときに、下層のマップで値でウィジェットを探していることを暗示しているため、現時点ではインターフェイスは危険です。メソッドも である必要がありますconst

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

キーがポインタであるキーを持つマップは扱いにくいです。それを調べる唯一の方法は、同じポインタを持つことです。hasWidgetこれが機能するには、メソッドが同じアドレスを持つオブジェクトで呼び出されることを保証する必要があります!

!Widgetのキーとして機能する正しい演算子がオーバーロードされるように、適切に実装する必要があります。std::mapマップでは、次のことを簡単に行うことができます。

std::map<Widget, int>

そして、あなたの検索にはconst_cast!は必要ありません。

于 2010-11-30T11:05:20.907 に答える
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 に答える