在我的程序中,我决定用一个名为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*
错误。我已经阅读了有关此错误的here,here,here,我明白这意味着什么。无论如何,我找不到办法“解决”这个问题。
我的问题是:因为我想返回指针(并且指针的大小相同),有没有办法强制编译器接受sockaddr*
作为{{1}}?而且,如果有办法,我该怎么做?
(如果没有办法,解决这个问题的“正确方法”是什么?)
答案 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”或类似的东西。