お世話になります。
現在、C++を勉強しているのですが、関数オブジェクトの呼び出し方について
教えて頂けないでしょうか。
下記のサンプルコードの★でrandom_shuffleの第3引数に関数オブジェクト
を指定すると思っているのですが、なぜRandomクラスのインスタンスrを指定
しても関数オブジェクトを呼ぶことができるのでしょうか。
r ではなく Random() ではないのでしょうか。
どうかご教授をお願いできないでしょうか。
よろしくお願いします。
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <ctime>
class Random
{
public:
// コンストラクタ
Random() {
srand(static_cast<unsigned int>( time(NULL) ));
}
// 関数オブジェクト
unsigned int operator() (unsigned int max) {
double tmp = static_cast<double>( rand() ) / static_cast<double>(RAND_MAX);
return static_cast<unsigned int>( tmp * max );
}
};
int main()
{
using namespace std;
vector<int> nums;
int i;
for(i = 0; i < 10; i++) nums.push_back(i);
Random r;
random_shuffle( nums.begin(), nums.end(), r); // ★
for(i = 0; i < 10; i++)
cout << nums[i] << endl;
return 0;
}
> r ではなく Random() ではないのでしょうか。
おんなじやん。
>r ではなく Random() ではないのでしょうか。
Random r;
random_shuffle( nums.begin(), nums.end(), Random()); // ★
とした場合、
Random r;
の行でわざわざ作ったRandomのインスタンス r が
使われずに無駄になりますよねぇ・・・もったいないですし(vv;)。
ちょっと不親切だったかもしれないので補足。
Random r; // Randomのインスタンス r を作った
random_shuffle(
nums.begin(),
nums.end(),
Random()); // rとは別のRandomのインスタンスをスタック上に作って
// その参照を渡した
ということですね。
提示例題は
http://www.geocities.jp/ky_webid/cpp/library/018.html
まあ答えは既に出ているとおり「同じ」なのだが、なぜ同じかを解説してみるテスト
関数オブジェクトという名称があまりにも適切すぎるため、
わかっている人間は言い換えようとも思わないわけだ。だが、
わからない人間には何のことだかさっぱりわからん・・・ということで。
関数オブジェクト=関数のように振舞うオブジェクト
≒関数のように振舞う変数
と書き換えると、ある程度わかりやすくなるかな?
(オブジェクトのうちの1つに変数がある)
関数のように振舞う「変数」だから、ごく普通に変数を作ることができる
int i; // のように int 型の変数を作るのと同じく
Random r; // のように「関数のように振舞う変数」を作っていい。
random_shuffle(..., r); のようにその変数を渡しても何の問題もない。
元発言者氏は(Random r; を除去して)random_shuffle(..., Random()); と書くべきではないか?
という疑問を持っているのだろう。この場合 r を再利用していないので、どっちでもいい。
--- 以下もうちょっと高度な解説
オブジェクトという単語/文言は抽象的過ぎてわかりにくい。
この単語を、ある観点から大きく2つに分類してみる。
「名前のあるオブジェクト」
「名前のないオブジェクト」(一時オブジェクト)
int i, j, k; と書くと変数 i j k が作られ、どれも [名前のあるオブジェクト] だ。
先ほどの例での Random r; も r という [名前のあるオブジェクト] を作っている。
k=i+j+1; という式を考えてみよう。
この式の中には [名前のないオブジェクト] が現れている。
i+j というの演算途中の一時的結果も int 型のオブジェクトなわけだ。
この [名前のないオブジェクト] が一時的に [コンストラクト] されている。
次に、この [名前のないオブジェクト] +1 という演算が行われる。
この演算結果もまた [別の、名前のないオブジェクト] なわけだ。
最後に [別の、名前のないオブジェクト] の値を k に代入し、
2つの [名前のないオブジェクト] 2つが [デストラクト] される。
というのが C++ 的考え方。
[名前のないオブジェクト] とは演算中に一時的に作られ、不必要になったとき即処分される [もの] 。
一時オブジェクトともいう。
[名前のあるオブジェクト] とは { 中で作ると対応する } まで存在する [もの] 。
([もの]=オブジェクトである。=変数 と考えてさほど間違いではない)
random_shuffle(..., Random()); と書くと、この Random() は一時オブジェクトの生成になる。
名前のない変数 Random() は 式文の終了の ; 到達時点でデストラクトされる。
つまり再利用することができない。
Random r; と書くと、これを括る } 到達まで r は存在し続けるので、
(その必要があれば) r を再利用できる。
という違いは、ある。
関数オブジェクトという文言を使うときはたいてい [一時オブジェクト] として使うので
多くのサンプルでは Random() のように使っているわけだ。
ツイート | ![]() |