从接口(c+;+;)分离表示的含义 我正在阅读Stroustrup在C++编程中的第2章。当他从具体类型过渡到抽象类型时,他提到具体不与表示耦合。因此,如果类堆栈发生重大变化,用户必须重新编译

从接口(c+;+;)分离表示的含义 我正在阅读Stroustrup在C++编程中的第2章。当他从具体类型过渡到抽象类型时,他提到具体不与表示耦合。因此,如果类堆栈发生重大变化,用户必须重新编译,c++,terminology,C++,Terminology,但是我看不出堆栈是抽象的,并且他以同样的方式使用派生类的情况有什么不同。那么,接口的解耦实际上做了什么呢?为什么在某些或大多数情况下它是可取的 《编辑:书》是《C++程序设计语言》专版(2000)。第2章,p5.4。抱歉。接口定义了实现必须支持的逻辑操作,以允许客户端代码访问某些功能。例如,支持输出操作的抽象类型可能是: struct Abstract_Output { virtual void blocking_write(const char* p, size_t n) = 0; }

但是我看不出堆栈是抽象的,并且他以同样的方式使用派生类的情况有什么不同。那么,接口的解耦实际上做了什么呢?为什么在某些或大多数情况下它是可取的


《编辑:书》是《C++程序设计语言》专版(2000)。第2章,p5.4。抱歉。

接口定义了实现必须支持的逻辑操作,以允许客户端代码访问某些功能。例如,支持输出操作的抽象类型可能是:

struct Abstract_Output
{
    virtual void blocking_write(const char* p, size_t n) = 0;
};
许多不同的输出设备可以有自己的实现来满足该接口。例如,一个最小的低级TCP库可能会在发送至少部分消息(告诉您写入了多少字节)后报告,但可能不支持自动重试,直到传输了所有特定数量的字节,不管需要多长时间。实现可能如下所示:

struct TCP_Output : Abstract_Output
{
    TCP_Output(const char* server_name, int port) : tcp_(server_name, port) { }

    void blocking_write(const char* p, size_t n) override
    {
        size_t bytes_written = 0;
        while (n && (bytes_written = tcp_.write(p, n)) > 0)
        {
            p += bytes_written;
            n -= bytes_written;
        }
        if (n > 0) throw std::runtime_error("incomplete TCP write");
    }
  private:
    TCP tcp_;
};
另一方面,如果您正在写入
std::ostream
对象,它将阻塞,直到写入请求的确切字节数,因此我们可以写入:

struct Stream_Output : Abstract_Output
{
    Stream_Output(std::ostream& os) : os_(os) { }

    void blocking_write(const char* p, size_t n) override
    {
        os_.write(p, n_);
    }
};
然后,您可以通过抽象类/结构使用运行时多态性编写可用于任何类型输出对象的函数:

void report(Abstract_Output& o)
{
    std::ostringstream oss("/--- REPORT --/\n");
    for (auto& x : stocks)
        oss << x << '\n';
    o.blocking_write(oss.str().c_str(), oss.str().data());
}
将上述所有内容与您的问题联系起来:

当他从具体类型过渡到抽象类型时,他提到具体不与表示耦合。因此,如果类堆栈发生重大变化,用户必须重新编译

[[请同时引用他的确切文本,然后我们可以看看您是否误解了它。]]我们通过使用抽象接口实现的是不将
report
等函数与
TCP\u output
Stream\u output
等具体输出实现耦合。像
report
这样的函数可以放在它们自己的头文件/实现文件中,如果某个地方的客户机代码想要用不同的
抽象输出调用它们,则不需要重新编译

但是我看不出堆栈是抽象的,并且他以同样的方式使用派生类的情况有什么不同。那么,接口的解耦实际上做了什么呢?为什么在某些或大多数情况下它是可取的


因此-如上所述,翻译单元只需要查看抽象类,并提供类似于
report
的功能。此外,
报告
可用于(重新链接后)向输出设备发送报告,这些设备甚至在编写
报告
功能时都没有设想过,更不用说实现了。这就是脱钩。

对不起,我是个固执的人,但我找不到一本叫这个名字的书。到底是哪一个?@kristiandazze这是@kristiandazze“我想”你能举个例子吗?@jiggunjer如果你能提供一些信息,我们可以在那里找到你所指的部分,这也会很有用。这本书的哪一版本,哪几页?谢谢,我想已经很清楚了。但解耦是通过继承实现的,而不是虚拟函数。因此,即使抽象_输出有一个正常的虚拟函数,您的示例对于报告函数仍然可以正常工作?因此,虚拟函数似乎只强制程序员使用一致的名称,但解耦来自继承,而不是抽象类型本身?您不能让报表使用模板获取通用输入,完全放弃继承的需要?@jiggunjer:inheritation本身不允许
report
函数对派生类型进行操作-这只起作用,因为
virtual
函数可用于调用派生类实现。你自己试试看。使用模板时,使用一致的名称就足够了(即参数多态性/duck类型),但是如果您不理解为什么运行时多态性有时比编译时更可取,那么您应该进行更多的后台阅读。谷歌,如果你喜欢,或尝试我的“顶部”答案的概述。。。。
Stream_Output stream_output(std::cout);
report(stream_output); // report to std::cout
TCP_Output tcp_output("localhost", 9191);
report(tcp_output);  // write report to the TCP server listening on port :9191