1

我见过一些人创建了一种“is-a”关系,如下所示:

class TCPClient : public Socket
{
public:
    TCPClient(const std::string& host, unsigned short port);
};

其中 Socket 类实现了 Winsock 函数,例如 Connect()、Close()、Bind() 等。

例子

但这对我是套接字编程的新手来说并不自然。

上面的层次结构是否比下面的“有”对应物更合乎逻辑?

class TCPClient
{
public:
    TCPClient(const std::string& host, unsigned short port);
    ....
private:
    Socket m_socket;
};
4

4 回答 4

4

TCPClient 使用套接字或具有套接字,但它本身不是套接字,并且您通常不会期望能够在任何需要套接字的地方替换 TCPClient。因此,公共继承没有意义。

在这种情况下,您可以使用私有继承,但是(至少在典型情况下)它可能也没有多大意义。私有继承主要在基类提供至少一个您计划在子类中覆盖的虚函数时才有意义。如果你有一个虚函数并且需要重写它,你别无选择,只能使用继承。不过,我不希望 Socket 类具有虚函数。这通常不适用于这里。

这基本上导致了您的第二个解决方案: TCPClient 应该包含一个 Socket 的实例,而不是使用继承。

但是,我应该补充一点,Socket您所展示的类似乎将实际套接字的概念与地址的概念混为一谈。我的第一个套接字课程(几年前)就是这样工作的,但从那以后我得出结论,这并不是一个真正理想的设计。我已经确信将地址的概念与套接字本身分开是值得的。虽然我的不太精细,但我发现有趣的是,我想出的东西看起来几乎就像它可能是 Boost ASIO 的原型。它更小更简单,但很多基本想法通常都非常相似。

这引出了我的下一个建议:看看 Boost ASIO。缺乏一个相当具体的理由不这样做,这是我在大多数新代码中建议(并且通常使用)的。尽管(正如我上面所说)这些年来我已经编写了几个套接字类,但我已经有很长一段时间没有在很多(任何?)新代码中使用它们中的任何一个了——它们实际上只有两个可能优于 ASIO . 第一个仅适用于我:因为我在 ASIO 存在之前编写并使用了它们,所以我已经了解它们以及它们是如何工作的。第二个可能类似:至少对我来说,它们看起来更小更简单(但是,这可能只是因为我先使用它们)。即便如此,(例如)使用其他人已经理解的东西的优势很容易胜过那些。

于 2012-07-01T15:45:54.253 回答
2

使用has-a. TCPClient 使用套接字就像人使用电话一样。你会从电话中派生出一个人吗?

于 2012-07-01T15:25:00.850 回答
0
class TCPClient : public Socket
{
public:
    TCPClient(const std::string& host, unsigned short port);
};

网络套接字不仅用于 TCP/IP,而且如果您打算重用“Socket”类来使用网络套接字实现其他协议,上述设计更适合。例如:

class UDPClient : public Socket
{
};
于 2012-07-01T14:35:08.523 回答
0

我会这么说。套接字是一个抽象,一个文件描述符(UNIX)或句柄(Windows),它具有与之关联的资源并由操作系统管理。如果我们考虑 OSI 模型,套接字非常适合表示层(它表示或描述两个节点之间的通信通道),而使用套接字的程序位于应用层。

考虑到这一点,我宁愿不从套接字继承,除非我实现一种高级套接字(通过类比:C 指针与智能指针)来呈现和处理逻辑连接并以某种方式管理与套接字关联的资源。如果XYZClient是一个应用程序,其目标是实现一些业务或数据处理逻辑,我不会将这两个概念混合在一起并使用第二种方法(has-a)。

我会拆分基础架构/资源特定和业务/应用程序特定的逻辑。

于 2012-07-01T15:08:38.603 回答