1

次の非常に省略されたクラスでは、一連のデータベース レコードを反復処理するメソッド (ProcessLines) をベースで定義し、子クラスでのみ定義されている関数に各レコードをパラメーターとして渡します。明らかに、Base はそれ自体ではインスタンス化されない仮想クラスです。

Class Base {
 public:
  typedef ProcLineFunc( Long *Line );
  void ProcessLines( ProcLineFunc pf);
}

Class Child{
  void DoWork( Long *Line) { //do something}
}

これを実装する方法がわかりません。子で ProcessLines を再宣言して親メソッドを呼び出すと、子を作成するコードで ProcessLines を呼び出した場合と同じエラー メッセージが表示されます。

Child c(//something);
c.ProcessLines(c.DoWork);

コンパイラ メッセージが表示されます。

[BCC32 エラー] main.cpp(67): E2034 Cannot convert 'bool (* (_closure )(long *))(long )' >to 'int ( )(long *)'
完全なパーサー コンテキスト
main.cpp(56) : class Add2Chan
main.cpp(78): インスタンス化の決定: bool Add2Chan::ProcessByLines()
--- インスタンス化のためのパーサー コンテキストをリセットしています...
main.cpp(67): 解析: bool Add2Chan::ProcessByLines()

私は c++ にかなり慣れていないので、E2034 エラー メッセージは私を怖がらせます。

助けてください。typedef を使用して、子クラスで ProcessLines を複数回呼び出し、さまざまな関数を渡すことができるようにしました。

4

3 に答える 3

4

通常、この種のことは、保護された純粋仮想関数を使用して行います。

class Base {
  public:
    ProcessLines() {
        //Logic to process lines here, obviously psuedo-code
        while(moreLines) {
            ProcessLine(line);
        }
    }
  protected:
    virtual void ProcessLine(const Line& line) = 0;
}

class Child : public Base {
  protected:
    void ProcessLine(const Line& line) { //Logic to process the line for this type }
};

class DifferentChild : public Base {
  protected:
    void ProcessLine(const Line& line) { //Logic to process the line for DifferentChild }
};

これはあなたが探しているようなものだと思います。奇妙な方法でポリモーフィズムを実装しようとしているように見えますが、これはC++で実装する通常の方法です。

于 2012-06-10T20:09:14.707 に答える
1

関数へのポインターを使用する代わりに、オブジェクトへのポインターを使用します。あなたの関数が呼び出されるという制限を受け入れてくださいDoWork。各クラスにはそのような関数が 1 つしか存在できません。これは悪い制限ではありません。(純粋仮想) 関数をクラス (インターフェイスと呼ばれる) で宣言し、そこからクラスを派生させます (それらはインターフェイスを実装すると言われています)。

struct DoingWork
{
    virtual void DoWork(long *Line) = 0; // does some work on a list
};

struct DoingGreatWork: DoingWork
{
    virtual void DoWork(long *Line) {printf("Great work\n");}
};

struct DoingSlightWork: DoingWork
{
    virtual void DoWork(long *Line) {printf("Slight work\n");}
};

この例を使用すると:

class Base {
  public:
    void ProcessLines(DoingWork& object) {
        //Logic to process lines here
        while(moreLines) {
            object.DoWork(line);
        }
    }
};

class Whatever // no need to derive from Base
{
    void DoStuff()
    {
        Base object;

        object.ProcessLines(DoingGreatWork());
        object.ProcessLines(DoingSlightWork());
    }
}

作業オブジェクトが呼び出し元オブジェクトにアクセスする必要がある場合は、次のように初期化します。

class Whatever // no need to derive from Base
{
    struct DoingElaborateWork: DoingWork
    {
        Whatever& caller;
        DoingElaborateWork(Whatever& caller): caller(caller) {}
        virtual void DoWork(long *Line)
        {
            printf("Doing work requested by %s\n", caller.name());
        }
    };

    void DoStuff()
    {
        Base object;
        object.ProcessLines(DoingElaborateWork(*this));
    }

    const char* name() {return "Whatever";}
}

PS彼らは、「C ++ 03の関数は二流市民です」と言っています。これは、オブジェクトでできることを関数で行うことができないためです(このソリューションのように、私が提供します)。C++11では関数がかなり改善されたと聞きましたが、詳細はよくわかりません。

于 2012-06-10T22:13:19.977 に答える
0

C++Builder でこれを行っているため、その__closure拡張機能を使用して、要求したことを正確に行うことができます (VCL の一部は、独自のコールバックに対して正確にこれを行います)。

class Base
{ 
public: 
    virtual ~Base() {}

    typedef void (__closure *ProcLineFunc)( Long *Line ); 
    void ProcessLines( ProcLineFunc pf); 
}; 

class Child : public Base
{ 
public:
    void DoWork( Long *Line) { //do something} 
}; 

Child c(...); 
c.ProcessLines(c.DoWork); 
于 2012-06-27T00:40:07.763 に答える