6

假设我们有一个包含不同元素的绘图程序,例如圆形、矩形、三角形等。不同种类的对象都需要类似的功能,例如draw()展示自己。

我想知道程序员将如何处理如今通常由多态性解决的问题,即通过一组不相同的元素并跨不同对象调用通用功能。

想到的一种方法是拥有一个结构,其中包含指向适当函数(或函数指针数组中的索引)的函数指针以及指向实际实例的 void 指针,并传递转​​换为正确类型的指针在函数中。但这就是我 - 一个对这个问题一无所知的人会这样做的方式。

我确实意识到这可能是一个愚蠢的问题,但由于我在“过去”的日子里没有去过,我真的想知道这个问题是如何解决的。在过程编程中使用了什么样的方法,它是否具有性能优势,因为我们都知道多态性即使在像 C++ 这样的快速语言中也会因为虚方法查找而产生开销。

4

3 回答 3

2

一个非常简单的例子。

如果您对此感兴趣,您可以在 Linux Kernel 中找到更多内容。

#include <stdio.h>                                                              

struct shape {                                                                  
    void (*say_hello)(void);                                                    
};                                                                              

void circle_say_hello(void)                                                     
{                                                                               
    printf("Hi I am circle!\n");                                                
}                                                                               

void square_say_hello(void)                                                     
{                                                                               
    printf("Meh I am square.\n");                                               
}                                                                               

#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))                                  

int main(int argc, char *argv[])                                                
{                                                                               
    struct shape circle = { .say_hello = circle_say_hello, };                   
    struct shape square = { .say_hello = square_say_hello, };                   
    struct shape* shapes[] = {&circle, &square};                                
    int i;                                                                      

    for (i = 0; i < ARRAY_SIZE(shapes); i++) {                                  
        if (shapes[i] && shapes[i]->say_hello)                                  
            shapes[i]->say_hello();                                             
    }                                                                           

    return 0;                                                                   
}  
于 2018-09-05T14:42:32.067 回答
0

这不是对您的示例的直接回答,而是对您的评论的地址,这显示了错误的观点恕我直言

我只是想知道那个特定的问题,如果有一种更有效的方法可以避免虚拟方法的性能开销,我最感兴趣

这里有一些东西要理解。一切都有权衡。设计模式和 OO 具有我们喜爱的所有已知优点,但也有缺点,例如太多的类、内存开销、由于许多方法调用导致的性能开销等。

另一方面,旧的“程序”方式也有一些优点,即客观性;代码“简单”(无需考虑如何设计系统,只需将所有内容放在 main 中)并且在许多方面具有更少的开销(更少的内存开销,因为需要更少的类和更紧凑的对象 - 不需要虚拟表等等-和更少的方法调用,因此可能有更好的性能,没有动态绑定的性能开销——不管现在的开销是什么......-)。

但这不是特定问题实例的权衡取舍,而是经验表明什么是构建软件的正确方法。模块化代码的重用和有助于单独测试(质量保证)、可读性可维护性、灵活扩展的代码是已被充分理解的属性,应该是软件开发的主要驱动力。

所以在某些情况下,一个真正优秀的 C/C++ 程序员可以按照你所说的那样做“旧方式”,但是它为这个特定程序带来的性能优势值得没有人能够维护或以后能坚持吗?

再举一个类似的例子:你可以用同样的方式问吗?
为什么在 Web 开发中使用多层架构?只需将所有内容都放入一台服务器,它会快很多,因为查询后端和 UI 数据的所有层或查询远程数据库的网络延迟等不会有延迟。
当然,你有一定道理。但是然后问问你自己,这可以随着负载的增加而扩展吗?答案是不。那么可扩展性对您来说很重要,还是您想保持“将所有东西放在一个服务器中”的想法?如果您的收入来自电子网站,那么您无法为更多客户提供服务这一事实不会仅仅因为您服务前 100 名客户真的很快就让您的客户满意......无论如何这是我的看法

于 2012-08-23T22:06:21.727 回答
0

在诸如 C 之类的过程语言中,这将通过draw()为每个自定义数据类型(可能表示为结构)定义函数的单独实现来解决。任何通用功能都将被分解为一个单独的函数,该函数对每个结构的共享元素进行操作(例如对象中心的 x 和 y 坐标,它们将出现在每个结构中)。从代码和功能的角度来看,这与使用多态性的 OOP 布局没有太大区别,在这种布局中,您仍然必须draw()在基类中实现共享方法并在特定子类中覆盖它。在过程语言的情况下,我们只是不会将这些函数定义拆分为单独的“对象”。

有一些奇特的方法可以从过程语言中获得类似对象的行为,例如联合类型或带有额外布尔值的单个整体类型,以确定是否正在使用特定元素。这将允许您编写一个draw()函数,该函数可以根据启用的元素执行逻辑切换。在实践中,我唯一看到的大部分是在基于 CORBA 的系统中,其中用 C 编写的程序必须模仿通过 IDL 传播的 OOP 语言的某些行为(即,将 Java 对象转换为构造可以解码成 C 风格的结构)。

至于 C++ 和 Java 等语言中虚方法查找的开销,这在面向对象语言中是无法完全避免的。final正确使用关键字(它允许编译器/JVM 优化方法查找表)可以很好地缓解这种情况。

于 2012-08-23T21:46:16.860 回答