6

円、長方形、三角形などのさまざまな要素を使用した描画プログラムがあるとします。draw()自分自身を表示するなど、すべて同様の機能を必要とするさまざまな種類のオブジェクト。

プログラマーは、今日では通常ポリモーフィズムによって解決される問題にどのようにアプローチするのでしょうか。つまり、同一でない要素のコレクションを調べて、さまざまなオブジェクト間で共通の機能を呼び出します。

頭に浮かぶ1つの方法は、適切な関数への関数ポインター(または関数ポインター配列内のインデックス)と実際のインスタンスへのvoidポインターを持つ構造体を持ち、適切な型にキャストされるポインターを渡すことです。関数内。しかし、それはまさに私がそうする方法です-主題について無知な人はそれをするでしょう。

これはお粗末な質問かもしれませんが、私は「昔」の時代にいなかったので、この問題にどのように取り組んだのだろうと本当に思っています。手続き型プログラミングでどのようなアプローチが使用され、パフォーマンス上の利点がありましたか。仮想メソッドルックアップにより、C++などの高速言語でもポリモーフィズムにオーバーヘッドがあることは誰もが知っているからです。

4

3 に答える 3

2

本当に簡単な例です。

これに興味がある場合は、Linux カーネルでこれをさらに見つけることができます。

#include <stdio.h>                                                              

struct shape {                                                                  
    void (*say_hello)(void);                                                    
};                                                                              

void circle_say_hello(void)                                                     
{                                                                               
    printf("Hi I am circle!\n");                                                
}                                                                               

void square_say_hello(void)                                                     
{                                                                               
    printf("Meh I am square.\n");                                               
}                                                                               

#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))                                  

int main(int argc, char *argv[])                                                
{                                                                               
    struct shape circle = { .say_hello = circle_say_hello, };                   
    struct shape square = { .say_hello = square_say_hello, };                   
    struct shape* shapes[] = {&circle, &square};                                
    int i;                                                                      

    for (i = 0; i < ARRAY_SIZE(shapes); i++) {                                  
        if (shapes[i] && shapes[i]->say_hello)                                  
            shapes[i]->say_hello();                                             
    }                                                                           

    return 0;                                                                   
}  
于 2018-09-05T14:42:32.067 に答える
0

draw()Cなどの手続き型言語では、カスタムデータ型(おそらく構造体として表される)ごとに関数の個別の実装を定義することでこれに対処します。一般的な機能はすべて、各構造体の共有要素(それぞれに表示されるオブジェクトの中心のx座標とy座標など)を操作する個別の関数に分解されます。draw()コードと機能の観点から、これは、基本クラスで共有メソッドを実装し、特定のサブクラスでオーバーライドする必要があるポリモーフィズムを利用するOOPレイアウトと大差ありません。手続き型言語の場合、これらの関数定義を別々の「オブジェクト」に分割することはありません。

手続き型言語からオブジェクトのような動作を引き出すためのいくつかの素晴らしい方法があります。たとえば、共用体型や、特定の要素が使用されているかどうかを判断するための追加のブール値を持つ単一のモノリシック型などです。draw()これにより、有効になっている要素に基づいてロジックスイッチングを実行できる単一の関数を作成できます。実際、私がその多くを見た唯一の場所は、Cで記述されたプログラムがIDLを介して伝播されたOOP言語の動作の一部を模倣する必要があるCORBAベースのシステムです(つまり、Javaオブジェクトの構造体への変換) Cスタイルの構造体にデコードできます)。

C ++やJavaなどの言語での仮想メソッドルックアップのオーバーヘッドに関しては、オブジェクト指向言語では完全に回避することはできません。キーワードを適切に使用することで、かなり軽減できますfinal(これにより、コンパイラー/ JVMがメソッドルックアップテーブルを最適化できるようになります)。

于 2012-08-23T21:46:16.860 に答える
0

これはあなたの例に対する直接の回答ではなく、あなたのコメントへのアドレスであり、間違った視点を示しています IMHO

私はその特定の問題について疑問に思っていました.仮想メソッドのパフォーマンスオーバーヘッドを回避するより効率的な方法があるかどうかに主に興味があります.

ここで理解しておくべきことがあります。すべてにトレードオフがあります。設計パターンと OO には、私たちが愛するようになったすべての既知の利点がありますが、クラスが多すぎる、メモリのオーバーヘッド、多くのメソッド呼び出しによるパフォーマンスのオーバーヘッドなどの欠点もあります。

一方、古い「手続き型」の方法には、客観的であるという利点もありました。コードは「シンプル」で (システムの設計方法を考える必要はなく、すべてをメインに配置するだけです)、多くの面でオーバーヘッドが少なくなりました (必要なクラスが少なくなり、オブジェクトがコンパクトになるため、メモリのオーバーヘッドが少なくなり、仮想テーブルが不要になります)。 etc-メソッド呼び出しが少ないため、おそらくパフォーマンスが向上し、動的バインディングのパフォーマンスオーバーヘッドはありません-現在のオーバーヘッドが何であれ...-)。

しかし、それは特定の問題インスタンスのトレードオフが何であるかではなく、ソフトウェアを構築するための適切な方法が何であるかを経験が示しています。モジュール化されており、個別のテストを支援するコードの再利用(品質保証)、読み取り可能保守可能、柔軟に拡張できることは、ソフトウェア開発の主な原動力となるべき、よく理解されている属性です。

したがって、C/C++の本当に優れたプログラマーがあなたが言うように「古い方法」を実行できる場合がありますが、この特定のプログラムで発生するパフォーマンス上の利点は、誰も維持または管理できないという事実に値します。その後も維持?

別の同様の例を挙げると、同じように尋ねることができますか?
Web 開発で多層アーキテクチャを使用する理由 すべてを 1 つのサーバーに配置するだけで、バックエンドと UI のデータのすべてのレイヤーをクエリする際の待機時間や、リモート データベースのクエリのネットワーク待機時間などがないため、非常に高速になります。
確かに、あなたにはポイントがあります。しかし、自問してみてください。これは、負荷の増加に応じて拡張できますか? 答えはノーだ。スケーラビリティはあなたにとって重要ですか、それとも「すべてを 1 つのサーバーに入れる」という考えを維持したいですか? あなたの収入が e-site から来ている場合、最初の 100 件を非常に迅速に提供したからといって、より多くの顧客にサービスを提供できないという事実は、クライアントを満足させることにはなりません.とにかく、これは私の意見です.

于 2012-08-23T22:06:21.727 に答える