C++ - 从抽象基指针调用派生函数
我一直在尝试创建一个基于继承的 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]我认为问题归结为这样(我很容易错): 我有一个基类 TCPDaemon 的 std::map,它有一个纯虚/抽象函数 Stop()。看起来,当我通过映射中的条目之一调用 Stop() 时,它试图调用 TCPDaemon::Stop(),而不是覆盖函数 EchoServer::Stop()。这可能是问题所在吗?如果是这样,我该如何解决?
I have been trying to create a TCP Server model based on inheritance, with varying success. These servers are managed by a singleton whose task it is to shut these servers down and other simple maintenance functions:
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;
};
The TCPDaemon class's Stop() function is a pure virtual. My problem is that when the Shutdown() function is called, it is attempting to call this pure virtual instead of the derived class' version. How can I force it to do the right thing?
Thanks in advance
[edit] sorry I did not include the TCPDaemon code before, it derives from a TCPSocket class (which I have checked to be 100% working and is fairly self-explanatory). Here it is:
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;
};
and here is a sample derived server and its method of creation:
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));
[edit+1] I think what the problem boils down to is this (i could easily be wrong):
I have a std::map of the base class, TCPDaemon, which has a pure virtual/abstract function Stop(). It appears that when I call Stop() through one of the entries in the map, it is attempting to call TCPDaemon::Stop(), as opposed to the overriding function EchoServer::Stop(). Could this be the issue? If so, how do I resolve it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我不确定这是否是您遇到的问题,但以下函数肯定存在问题。
在每个循环迭代器之后,
i
将增加 1 并且size
将减少,因此您只需关闭一半的服务器。也许保持某些开放状态会导致进一步的错误。您可以执行以下任一操作:
或者,我更喜欢这样做,因为它可以更好地显示代码的意图:
I'm not sure if this is the problem you're seeing, but there's certainly a problem in the following function.
After each loop iterator,
i
will increase by one ANDsize
will decrease, so you'll only ShutDown half your servers. Maybe keeping some open is the cause of further errors.You could do either:
or, which I would prefer as it better shows the intention of the code:
检查您所声明内容的语法:
这是我在没有更多代码的情况下能做的最好的事情。
编辑:
好的,看来您正在使用抽象函数。下一个问题是:您遇到的错误是什么?我可以肯定地说,它没有尝试从 TCPDeamon 调用 Stop()。这是不可能的,因为它甚至没有实施。
Check the syntax of what you are declaring:
That's the best I can do without more code.
EDIT:
Ok, so it seems like you are using abstract functions. Next question is this: what is the error you are getting? I can say for certain that it is not attempting to call Stop() from TCPDeamon. That would be impossible, as it is not even implemented.
感谢我收到的意见,我最终解决了这个问题。问题出在 TCPServer::Shutdown(unsigned Short) 中的删除调用,这导致代码的完全不同部分发生内存访问冲突......这是一个相当新手的错误,我将尽快将智能指针包装在所有内容上。
感谢您的所有反馈!
I finally worked it out in the end, thanks to the input I have received. The problem was with the delete call in TCPServer::Shutdown(unsigned short), which was causing a memory access violation in a completely different part of the code... A rather noobie mistake, I shall be wrapping smart pointers around everything ASAP.
Thanks for all your feedback!
如果你说它调用纯虚方法而不是派生类的版本,那就意味着你的派生类调用它。例如,
据我记得只有派生类的对象可以调用基类的纯虚函数(当然,如果它是在基类中实现的)
If you say that it calls pure virtual method instead of derived class' version, that means that your derived class calls it. E.g.
As far as I remember only derived class' object can call base class' pure virtual function (surely, if it's implemented in base class)