1

function.h ファイルで、次のような関数をインラインとして宣言しました。

inline double foo(int &a)
{

     return a*double value
}

.cpp ファイルに function.h ファイルを含め、多くの場所で foo 関数を呼び出します。a.cpp ファイルにはクラス b のインスタンスもあり、b インスタンスで foo 関数を呼び出したいと考えています。b で関数を呼び出すとき、関数ポインタを b に渡して、function.h ファイルをインクルードせずに b で foo 関数を呼び出せるようにします。

b.cpp file
void b_foo(a function pointer to foo function)
{
     call foo
}

関数をインラインとして宣言したので、b.cpp に function.h ファイルを含めるだけでなく、関数ポインターを渡すことが効率的かどうかはわかりません。

何が違うのか見てみたいです。

4

4 に答える 4

1

まず第一に、C でプログラミングしているわけではありません。C++ でプログラミングしています。それは別の言語です。C には参照もクラスもありません。コードではクラスを使用していませんが、参照を使用しています (理由はありません)。

次に、「関数ポインタ」の意味を考えてみましょう。その関数のコードのアドレスです。ここで、「インライン」の意味を考えてみましょう。これは、関数がマシンコードに独立して存在しないことを意味します。そのソースは (概念的に) 呼び出された場所に配置され、呼び出し元の関数がコンパイルされます。したがって、存在しないもののアドレスを取得する必要があります。

短い答えは、あなたができないということです。

何を達成したいですか?常に b から foo を呼び出しますか? もしそうなら、間接化は必要ありません。インライン関数を呼び出して呼び出しサイトにインライン化することも、インライン化されていない関数を呼び出して実際の関数呼び出しを生成することもできます。

// a.c
double foo(int a) { return a * 3.1415926; }

// b.c
double foo(int a); // declaration, not definition.
void b(void) { printf("%f\n", foo(10)); }
int main() { b(); return 0; }

ビルド: gcc -O2 -c -o a.o a.c

bc をコンパイルするとき、コンパイラは関数 foo が何をするかを知らず、そのシグネチャしか知らないことに注意してください。 gcc -O2 -c -o b.o b.c

これにより、2 つの部分が 1 つのプログラムにリンクされ、実行できるようにする部分が最初と最後に追加されます。 gcc -o program a.o b.o

違いは効率です。インライン化により、多くの最適化が可能になります (ここのように、引数の値を使用して結果を事前計算し、コードを完全に削除するまで)。このような最適化が不可能なインライン化は、関数呼び出しのオーバーヘッドを排除し、より適切なレジスタ割り当てを可能にするだけでなく、コードがキャッシュ内にある可能性を高めることができるため、高速化することができます。しかし、インライン化は、コードの肥大化とキャッシュの汚染につながり、コードがキャッシュに存在する可能性が低くなるため、悪い場合があります。

コンパイル時に b から呼び出したい関数がわからない場合は、間接化が必要です。したがって、foo_1 と foo_2 があり、どちらかへのポインターを b に渡すと、そのポインターが指す関数がどちらであるかを知らずに呼び出されます。これにはパフォーマンス上のペナルティがありますが、場合によっては必要になります。

double foo_1(int a) { return a * 3.1415926; }
double foo_2(int a) { return a * 2.81; }

void b(double (*foo)(int)) { printf("%f\n", foo(10)); }
int main(int argc, char *argv[])
{
  if (argc < 2) return;
  b(argv[1][0]-48 ? &foo_1 : &foo_2);
  return 0;
}

C++ には、同じソース コードから関数 b の 2 つのバージョン (常に foo_1 を呼び出すものと常に foo_2 を呼び出すもの) を生成するようにコンパイラに指示できる機能があります。次に、foo_1 と foo_2 を両方の呼び出しサイトでインライン化することができ、foo_1 または foo_2 へのポインターを b に渡すことを選択する代わりに、b_with_foo_1 または b_with_foo_2 を呼び出すことを選択する必要があります。

于 2012-04-16T02:50:28.067 に答える
1

これを試して:

typedef double (*foo_ptr)(int &);

void b_foo(foo_ptr f)
{
  if (f != NULL) {
    double d = f(5);
  }
}

void f() {
  b_foo(foo);
}

これtypedefにより、関数へのポインター型を参照しやすくなります。

ポインターを呼び出すときにポインターを逆参照する必要はありません (可能ですが、必須ではありません)。また、パラメーターとして渡すときに関数のアドレスを取得する必要はありません (必要に応じて可能です)。

ご了承ください

inline double foo(int &a)  
{  
  return a*double value  
}

構文エラーがあるため無効です。試す:

inline double foo(int &a)  
{  
  return a*4.5; 
}

doubleダブルリテラルの代わりにローカル変数を宣言/使用することもできます

注: インスタンス内の関数について話している。実際にメンバー関数を呼び出したい場合は、次のものが必要です。

typedef double (*B::foo_ptr)(int &);

whereBは、関数が宣言されているクラスです。次に、次のように使用します。

B b;
foo_ptr f = B::foo;
b.*f(5);
于 2012-04-16T02:21:49.027 に答える
0

ここをクリックしてください"関数へのポインタ" これはあなたの助けになるかもしれません。わかりませんが、私はcが初めてです。私はあなたがそれを必要としていることを確信しています。

于 2012-04-16T02:22:44.957 に答える
0

実際のパフォーマンスの違いが生じるかどうかは、両方のバージョンを実装し、コンパイラの最適化をオンにして速度を比較することによってのみ確認できます。コードの残りの部分に関連する要因が多すぎるため、予測できません。それは明らかに、関数が実際に呼び出される回数に特に依存します。

しかし、一般的に言えば、インライン化は速度に大きなプラスの影響を与える可能性があります。

物事を柔軟に保つために、また、明らかに C++ (C ではなく) を使用していることを考えると、この状況に対処するためによりC++ に似た方法を使用することをお勧めします。さまざまな可能性とその意味については、この SO 投稿を参照してください。そこでの質問で概説されている戦略の 1 つに従うと、関数ポインターの柔軟性を維持しながら、可能な限りコンパイラーがインライン化される可能性があります。

于 2012-04-16T02:38:02.660 に答える