派生的虚函数返回不同的类型 - 指针

时间:2012-09-05 18:10:00

标签: c++ sockets inheritance virtual covariance

在我的程序中,我决定用一个名为Socket的类来包装“套接字接口”(实际上只是我正在使用的部分)。我正在使用两个不同的“域名”:AF_PACKET和AF_INET。因为这些域具有不同的地址结构,所以我决定也包装它们的地址。

我做了一个像下面这样的小班:

class SockAddress
{
    public:
    SockAddress();

    virtual sockaddr* getAddress();
    virtual socklen_t getAddrLen();

    private:

    sockaddr address_;
};

基于这个类,我创建了另一个类,而不是返回一个sockaddr*返回一个sockaddr_ll *`

virtual sockaddr_ll* getAddress();
...
sockaddr_ll address_;

在C中,它们都是指针。我只是知道,只要sockaddr*需要sockaddr_ll*,如果我传递socklen_t(并传递相应的invalid covariant return type大小值),就没有问题。

但在C ++中,尝试编译时,出现sockaddr_ll*错误。我已经阅读了有关此错误的hereherehere,我明白这意味着什么。无论如何,我找不到办法“解决”这个问题。

我的问题是:因为我想返回指针(并且指针的大小相同),有没有办法强制编译器接受sockaddr*作为{{1}}?而且,如果有办法,我该怎么做?

(如果没有办法,解决这个问题的“正确方法”是什么?)

3 个答案:

答案 0 :(得分:2)

通常,如果您的公共API暴露指针并鼓励类型转换,则需要重新设计。

当你在一个类中包装东西时,重点是隐藏细节,而不只是为它们制作类似结构的东西。

您正在设计您希望采用内置的类型。这意味着隐藏了类中所有那些讨厌的指针。

例如,创建一个只引用抽象事物的顶级类,然后创建子类,如果你想支持不同的低级API ......但是完全将这些API封装在抽象操作后面的子类中,比如listen / accept /连接。

class Socket
{
   public:
      Socket(IPAddress address, int port);
      virtual ~Socket() = 0; // close/clean up resources
      virtual void connect() = 0;
      ...
};  

然后继承Socket类以封装低级API的细节。

另一个选择是反转事物,以便SockAddress隐藏操作并使抽象套接字处理:

class SockAddress
{
      void doSomethingForSocket(Socket *s) { ... }
}

我通常会告诉人们首先编写使用API​​的程序,并假设你有一个摇滚明星的API为你做了很酷的事情:

Socket s("192.168.5.100", 80);
s.connect();
int i;
i << s;
...
ServerSocket server("*", 80);
Socket *client;
while((client = server.accept()) != null) {
   ...
}

然后去制作那个API。在构建API本身时使用相同的技术。在编写connect()时,可能存在的下一组“好东西”是什么,真的很好......然后制作那些。

答案 1 :(得分:0)

  

在C中,它们都是指针。我只是知道,如果我传递了sockaddr_ll *,无论在哪里都需要sockaddr *

我想要一个肯定的参考。我很确定它打破了C的别名规则。

当我在使用的编译器上没有它时,我已经实现了这样的协变返回。

class SockAddress
{
public:
    SockAddress();

    sockaddr* getAddress() { return doGetAddress(); }

protected:
    virtual sockaddr* doGetAddress() { return ...; } 
};

class SockAddress_ll: public SockAddress
{
public:
    SockAddress();
    sockaddr_ll* getAddress() { return static_cast<sockaddr_ll*>(doGetAddress()); }

protected:
    sockaddr* doGetAddress() { return ...; }
};

通过reinterpret_cast更改static_cast可能会做你想要的。这样做多么明智是另一回事。

答案 2 :(得分:0)

首先,在虚函数中,您确实需要相同的返回类型。这是一种给定的,因为使用了虚函数,因此您可以拥有相同函数的不同版本,并根据对象类型使用正确的函数。因此,无论谁调用你的函数都不知道将使用哪个版本的函数,因此如果它们不同则无法知道返回类型。

因此,在任何一种情况下都返回sockaddr。如果您发现调用者需要知道返回的实际类型,您可能不希望使用虚函数,或者您想要非虚拟的“getSpecificAddress”或类似的东西。