4

継承に基づいて TCP サーバー モデルを作成しようとしましたが、さまざまな成功を収めています。これらのサーバーは、これらのサーバーのシャットダウンとその他の単純なメンテナンス機能をタスクとするシングルトンによって管理されます。

class TCPServer {
public:
    TCPServer();
    ~TCPServer();

    void Bind(TCPDaemon *daemon) {
        if(!daemon->IsRunning()) {
            throw TCPBindException("Daemon is inactive");
        }

        // if the port is not taken, bind this daemon to it
        if(this->servers.count(daemon->port())==0) {
            this->servers[daemon->port()]=daemon;
            ...
        } else {
            throw TCPBindException("Port is taken");
        }
    }

    void Shutdown() {
        MASON::UINT16 i;
        for(i=0;i<this->servers.size();i++) {
            this->Shutdown((*this->servers.begin()).first);
        }
    }

    void Shutdown(unsigned short port)  {
        if(this->servers.count(port)) {

            if(this->servers[port]->IsRunning()) {
                this->servers[port]->Stop();
            }

            delete this->servers[port];
            this->servers.erase(port);

        }
    }

private:
    std::map<unsigned short, TCPDaemon*> servers;

};

TCPDaemon クラスの Stop() 関数は純粋仮想関数です。私の問題は、Shutdown() 関数が呼び出されたときに、派生クラスのバージョンではなく、この純粋な仮想を呼び出そうとしていることです。どうすれば正しいことを強制できますか?

前もって感謝します

[編集] 申し訳ありませんが、以前に TCPDaemon コードを含めていませんでした。これは TCPSocket クラスから派生しています (これは 100% 動作することを確認しており、かなり一目瞭然です)。ここにあります:

class TCPDaemon: public TCPSocket {
public:
    TCPDaemon(unsigned short port) {
        this->_enabled=false;
        this->_host.ipaddr(INADDR_ANY);
        this->_host.port(port);
        this->paused=false;

        struct sockaddr_in opts=this->_host.Compile();

        #ifdef PLATFORM_WINDOWS
            WSADATA wsaData;
            if(WSAStartup(0x0202, &wsaData)) {
                throw TCPDaemonException("Failed to start WSA");
            }
        #endif

        this->raw_socket=socket(AF_INET, SOCK_STREAM, 0);
        if(this->raw_socket<=0) {
            throw TCPDaemonException("Failed to create socket");
        }

        if(int status=bind(this->raw_socket, (sockaddr*)&opts, sizeof(sockaddr))) {
            printf("error [%i]\r\n", status);
            throw TCPDaemonException("Failed to bind to port");
        }

        if(listen(this->raw_socket, 5)) {
            throw TCPDaemonException("Failed to listen on port");
        }

        this->_enabled=true;

    }

    virtual ~TCPDaemon() {
        this->Shutdown();
    }

    virtual void Start()=0;
    virtual void Run(TCPSocket*)=0;
    virtual void Stop()=0;

    unsigned short port() {
        return this->host().port();
    }

    bool IsRunning() {
        return this->_enabled;
    }

    TCPSocket *Accept() {
        SOCKET client;
        struct sockaddr client_addr;
        int len=sizeof(client_addr);
        client=accept(this->raw_socket, &client_addr, &len);

        return new TCPSocket(client, &client_addr);
    }

    void Shutdown() {

    }

private:
    bool _enabled;
    bool paused;

};

サンプルの派生サーバーとその作成方法を次に示します。

   class EchoServer: public TCPDaemon {
    public:
        EchoServer(MASON::UINT16 port): TCPDaemon(port) {
        }

        ~EchoServer() {}

        virtual void Start() {

        }

        virtual void Run(TCPSocket *client) {
            printf("RUN\r\n");
            Accessor<TCPSocket> acc_client=client;
            acc_client->Write(Accessor<Blob> (new Blob(std::string("hello!"))));
            acc_client->Disconnect();
        }

        virtual void Stop() {

        }

    };

myTCPServer->Bind(new EchoServer(8008));

[編集+1] 問題はこれに要約されると思います(私は簡単に間違っている可能性があります): 私は、純粋な仮想/抽象関数 Stop() を持つ基本クラス TCPDaemon の std::map を持っています。マップ内のエントリの 1 つを介して Stop() を呼び出すと、オーバーライド関数 EchoServer::Stop() ではなく、TCPDaemon::Stop() を呼び出そうとしているようです。これが問題でしょうか?もしそうなら、どうすれば解決できますか?

4

4 に答える 4

1

宣言する内容の構文を確認してください。

class TCPDaemon
{
    virtual void stop() = 0;
};

class MyDaemon : public TCPDaemon
{
    virtual void stop()
    {
        //Do stuff here.
    }
};

これは、コードを追加せずに実行できる最善の方法です。

編集:

わかりましたので、抽象関数を使用しているようです。次の質問はこれです: あなたが得ているエラーは何ですか? TCPDeamon から Stop() を呼び出そうとしているのではないことは確かです。実装すらされていないので、それは不可能です。

于 2010-08-19T21:38:57.323 に答える
1

いただいたご意見のおかげで、最終的にはうまくいきました。問題は、TCPServer::Shutdown(unsigned short) の削除呼び出しにあり、コードのまったく別の部分でメモリ アクセス違反が発生していました。

フィードバックをお寄せいただきありがとうございます。

于 2010-08-19T22:10:11.397 に答える
0

派生クラスのバージョンではなく純粋仮想メソッドを呼び出すと言う場合、それは派生クラスがそれを呼び出すことを意味します。例えば

class t1
{
 public:
virtual ~t1(){};
virtual void foo()=0        
  {
    std::cout << "pure virtual";
};
};

class t2:public t1
{
public :

virtual void foo() 
    {
    t1::foo();
    std::cout << "derived class ";
};
};

私が覚えている限りでは、派生クラスのオブジェクトのみが基本クラスの純粋仮想関数を呼び出すことができます (基本クラスに実装されている場合)

于 2010-08-19T21:30:27.403 に答える
0

これがあなたが見ている問題かどうかはわかりませんが、次の関数に問題があることは確かです。

void Shutdown() {
    MASON::UINT16 i;
    for(i=0;i<this->servers.size();i++) {
        this->Shutdown((*this->servers.begin()).first);
    }
}

各ループ反復子の後、i1 ずつ増加しsize、減少するため、サーバーの半分だけをシャットダウンします。開いたままにしておくと、さらにエラーが発生する可能性があります。

次のいずれかを実行できます。

void Shutdown() {
    MASON::UINT16 i;
    std::size_t nrServers = this->servers.size();
    for(i=0;i<nrServers;i++) {
        this->Shutdown((*this->servers.begin()).first);
    }
}

または、コードの意図をよりよく示しているので、どちらを好むか:

void Shutdown() {
    while (!this->servers.empty()) {
        this->Shutdown((*this->servers.begin()).first);
    }
}
于 2010-08-19T22:09:51.293 に答える