C ++ - вызов производной функции от абстрактного базового указателя - PullRequest
4 голосов
/ 20 августа 2010

Я пытался создать модель 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;

};

Функция Stop () класса TCPDaemon является чисто виртуальной.Моя проблема в том, что когда вызывается функция 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] Я думаю, что проблема сводится к следующему(я легко могу ошибаться): у меня есть std :: map базового класса, TCPDaemon, который имеет чисто виртуальную / абстрактную функцию Stop ().Похоже, что когда я вызываю Stop () через одну из записей на карте, он пытается вызвать TCPDaemon :: Stop (), а не переопределяющую функцию EchoServer :: Stop ().Может ли это быть проблемой?Если так, как я могу решить это?

Ответы [ 4 ]

1 голос
/ 20 августа 2010

Я наконец-то решил это, в конце концов, благодаря полученной информации. Проблема была с вызовом delete в TCPServer :: Shutdown (unsigned short), который вызывал нарушение доступа к памяти в совершенно другой части кода ... Довольно грубая ошибка, я буду оборачивать умные указатели как можно скорее

Спасибо за все ваши отзывы!

1 голос
/ 20 августа 2010

Проверьте синтаксис того, что вы объявляете:

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

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

Это лучшее, что я могу сделать без кода.

EDIT:

Хорошо, похоже, вы используете абстрактные функции. Следующий вопрос таков: какую ошибку вы получаете? Я могу с уверенностью сказать, что не пытается вызвать Stop () из TCPDeamon. Это было бы невозможно, поскольку оно даже не реализовано.

0 голосов
/ 20 августа 2010

Я не уверен, что это проблема, которую вы видите, но, безусловно, проблема в следующей функции.

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

После каждого итератора цикла i будет увеличиваться на 1, а 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);
    }
}
0 голосов
/ 20 августа 2010

Если вы говорите, что он вызывает чисто виртуальный метод вместо версии производного класса, это означает, что ваш производный класс вызывает его. Э.Г.

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 ";
};
};

Насколько я помню, только производный класс "объект может вызывать чисто виртуальную функцию базового класса" (конечно, если он реализован в базовом классе)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...