我会选择Java作为一个例子,大多数人都知道它,尽管其他所有OO语言都可以正常工作.
与许多其他语言一样,Java具有接口继承和实现继承.例如,Java类可以从另一个继承,并且每个在其中具有实现的方法(假设父级不是抽象的)也是继承的.这意味着接口是继承的,也是此方法的实现.我可以覆盖它,但我没有.如果我不覆盖它,我继承了实现.
但是,我的类也可以"继承"(不是用Java术语)只是一个接口,没有实现.实际上接口实际上是用Java命名的,它们提供了接口继承,但没有继承任何实现,因为接口的所有方法都没有实现.
现在有这篇文章,说继承接口比实现更好,你可能想读它(至少在第一页的前半部分),这很有意思.它避免了脆弱的基类问题.到目前为止,这一切都很有意义,文章中说的许多其他内容对我来说很有意义.
让我感到困惑的是,实现继承意味着代码重用,这是OO语言最重要的属性之一.现在,如果Java没有类(如James Gosling,Java的教父希望根据本文),它解决了实现继承的所有问题,但是如何使代码重用成为可能呢?
例如,如果我有一个类Car和Car有一个方法move(),这会使Car移动.现在我可以为不同类型的汽车分类汽车,这些都是汽车,但都是汽车的专用版本.有些可能以不同的方式移动,这些需要覆盖move(),但大多数只会保留继承的移动,因为它们像抽象的父Car一样移动.现在假设在Java中只有接口,只有接口可以相互继承,一个类可以实现接口,但所有类都是final,所以没有类可以从任何其他类继承.
如果你有一个Interface Car和100个Car类,你需要为每个类实现相同的move()方法吗?OO世界中存在除了实现继承之外的代码重用的哪些概念?
有些语言有Mixins.Mixins是我问题的答案吗?我读到了它们,但我无法想象Mixins如何在Java世界中运行,以及它们是否真的可以在这里解决问题.
另一个想法是,有一个类只实现Car接口,让我们称之为AbstractCar,并实现move()方法.现在其他汽车也实现了Car接口,在内部他们创建了一个AbstractCar实例,他们通过在内部抽象Car上调用move()来实现自己的move()方法.但这不会浪费资源(一种方法只调用另一种方法 - 好吧,JIT可以内联代码,但仍然)并使用额外的内存来保存内部对象,你甚至不需要实现继承?(毕竟每个对象都需要更多的内存,而不仅仅是封装数据的总和)对于程序员来说,编写虚拟方法也不是很尴尬
public void move() {
abstractCarObject.move();
}
Run Code Online (Sandbox Code Playgroud)
?
任何人都可以想象一个更好的想法如何避免实现继承,仍然能够以简单的方式重用代码?
不要被震惊.这是很多文字,但我害怕没有提供一些详细的信息,我无法真正展示这是什么(并可能得到很多答案,并没有真正解决我的问题).而这绝对不是一项任务(正如他的评论中有人可笑地声称的那样).
由于除非至少设置了一些先决条件,否则根本无法回答此问题,以下是先决条件:
此外,我们需要更好地定义"更好".必须考虑几个属性:
现在通过或多或少的操作码实际意味着我的例子.它可能看起来实际上设置了操作码的数量,因为每个操作需要一个操作码.然而,它并不那么容易.
你可以进行类似的操作
ADD R1, R2, R3
Run Code Online (Sandbox Code Playgroud)
添加R1和R2的值,将结果写入R3.现在考虑以下特殊情况:
ADD R1, R2, R2
ADD R1, 1, R1
Run Code Online (Sandbox Code Playgroud)
这些是您在许多应用程序中可以找到的常见操作.您可以使用现有的操作码来表达它们(除非您需要不同的操作码,因为最后一个操作码具有int值而不是寄存器).但是,您也可以为这些创建特殊的操作码:
ADD2 R1, R2
INC R1
Run Code Online (Sandbox Code Playgroud)
和之前一样.优势在哪里?ADD2只需要两个参数,而不是3个,INC甚至只需要一个参数.因此,这可以在磁盘和/或内存中更紧凑地编码.由于将任何一种形式转换为另一种形式也很容易,因此解码步骤可以在两种方式之间转换以表达这些语句.不过,我不确定这两种形式会影响执行速度.
现在让我们假设您有一个ADD_RRR(R表示寄存器)和一个LOAD来将数据加载到寄存器中.
LOAD value, R2
ADD_RRR R1, R2, R3
Run Code Online (Sandbox Code Playgroud)
您可以拥有这两个操作码并始终在整个代码中使用这样的构造...或者您可以将它们组合成一个名为ADD_RMR(M代表内存)的新操作码
ADD_RMR R1, value, R3
Run Code Online (Sandbox Code Playgroud)
假设您有16位整数和32位整数作为本机类型.寄存器为32位,因此数据类型适合.现在,当您添加两个寄存器时,可以将数据类型设为参数:
ADD int16, R1, R2, R3
ADD int32, R1, R2, R3
Run Code Online (Sandbox Code Playgroud)
例如,对于有符号和无符号整数也是如此.这样,ADD可以是一个短操作码,一个字节,然后你有另一个字节(或者可能只是4位)告诉VM如何解释寄存器(它们是否保持16位或32位值).或者您可以废弃类型编码,而是有两个操作码:
ADD16 R1, R2, R3
ADD32 R1, R2, R3
Run Code Online (Sandbox Code Playgroud)
有些人可能会说两者完全相同 - 只是解释第一种方式,因为16位操作码可行.是的,但是一个非常幼稚的翻译可能看起来很不一样.例如,如果每个操作码有一个函数并使用switch语句调度(不是最好的方式,函数调用开销,switch语句也许不是最优的,我知道),这两个操作码可能如下所示:
case ADD16: add16(p1, p2, …Run Code Online (Sandbox Code Playgroud) 我需要创建32位数字(有符号或无符号无关紧要,最高位永远不会被设置),每个数字必须设置给定的位数.
最简单的解决方案当然是从零开始.在循环内,数字现在增加1,计数位数,如果计数具有所需值,则数字存储到列表中,否则循环重复.如果找到足够的数字,则停止循环.当然这很好用,但是一旦所需位数变得非常高,它就会非常慢.
具有(比方说)5位的最简单的数字是设置前5位的数字.这个号码可以很容易地创建.在循环内,第一个位置位,数字向左移一个.这个循环运行5次,我找到第一个设置了5位的数字.接下来的几个数字也很容易创建.我们现在假装数字为6位宽,最高位数未设置.现在我们开始将第一个零位向右移动,因此我们得到101111,110111,111011,111101,1111110.我们可以通过在前面添加另一个0并重复此过程来重复此操作.0111110,1011110,1101110等.然而,这种方式的数字增长速度将超过必要的速度,因为使用这种简单的方法,我们省略了数字,如1010111.
那么有没有更好的方法来创建所有可能的排列,一种可以使用的通用方法,无论下一个数字将包含多少位,无论我们需要设置多少个位?
OO编程中的典型问题是钻石问题.我有父类A,有两个子类B和C.A有一个抽象方法,B和C实现它.现在我有一个子类D,它继承了B 和 C.钻石问题现在,D使用什么实现,B或C之一?
人们声称Java不知道钻石问题.我只能有接口的多重继承,因为它们没有实现,我没有钻石问题.这是真的吗?我不这么认为.见下文:
[删除车辆示例]
钻石问题总是导致糟糕的类设计,也不是程序员和编译器需要解决的问题,因为它首先不应该存在?
更新:也许我的榜样选择不当.
看到这个图像

(来源:suffolk.edu)
当然你可以用C++创建Person虚拟,因此你只有一个人在内存中的实例,但真正的问题仍然存在恕我直言.你如何为GradTeachingFellow实现getDepartment()?考虑一下,他可能是一个系的学生,另一个系教学.所以你可以退回一个部门或另一个部门; 对于这个问题没有完美的解决方案,并且没有实现可以继承(例如学生和教师都可以作为接口)似乎并没有解决问题.
请考虑以下源代码,它完全符合POSIX:
#include <stdio.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>
int main (int argc, char ** argv) {
pthread_cond_t c;
pthread_mutex_t m;
char printTime[UCHAR_MAX];
pthread_mutex_init(&m, NULL);
pthread_cond_init(&c, NULL);
for (;;) {
struct tm * tm;
struct timeval tv;
struct timespec ts;
gettimeofday(&tv, NULL);
printf("sleep (%ld)\n", (long)tv.tv_sec);
sleep(3);
tm = gmtime(&tv.tv_sec);
strftime(printTime, UCHAR_MAX, "%Y-%m-%d %H:%M:%S", tm);
printf("%s (%ld)\n", printTime, (long)tv.tv_sec);
ts.tv_sec = tv.tv_sec + 5;
ts.tv_nsec = tv.tv_usec * 1000;
pthread_mutex_lock(&m);
pthread_cond_timedwait(&c, &m, &ts);
pthread_mutex_unlock(&m);
}
return 0; …Run Code Online (Sandbox Code Playgroud) Apple通常在表格()下面放置+/ -按钮NSTableView,例如在网络或用户和组的系统偏好设置中.见下图:

如何在Interface Builder中直接在我的表下面放置相同的按钮,而无需操作代码中的任何接口元素或继承任何元素类?
我需要一位真正的C大师的帮助来分析我的代码中的崩溃.不是为了解决崩溃; 我可以很容易地解决它,但在这之前我想了解这种崩溃是如何可能的,因为这对我来说似乎完全不可能.
此崩溃只发生在客户机器上,我无法在本地重现(因此我无法使用调试器逐步执行代码),因为我无法获取此用户数据库的副本.我的公司也不允许我只更改代码中的几行并为该客户进行自定义构建(因此我无法添加一些printf行并让他再次运行代码)当然客户的构建没有调试符号.换句话说,我的补偿能力非常有限.尽管如此,我可以确定崩溃并获得一些调试信息.但是,当我查看该信息然后在代码中,我无法理解程序流程如何能够到达相关行.代码应该在到达该行之前很久就已经崩溃了.我完全迷失在这里.
让我们从相关代码开始.这是非常少的代码:
// ... code above skipped, not relevant ...
if (data == NULL) return -1;
information = parseData(data);
if (information == NULL) return -1;
/* Check if name has been correctly \0 terminated */
if (information->kind.name->data[information->kind.name->length] != '\0') {
freeParsedData(information);
return -1;
}
/* Copy the name */
realLength = information->kind.name->length + 1;
*result = malloc(realLength);
if (*result == NULL) {
freeParsedData(information);
return -1;
}
strlcpy(*result, (char *)information->kind.name->data, realLength);
// ... code below skipped, not relevant ... …Run Code Online (Sandbox Code Playgroud) 我正在尝试安装libxml-ruby.我安装了libxml2,libxslt和coreutils
我还阅读了有关该问题的其他帖子,但没有一个解决它.
brew list libxslt
/opt/boxen/homebrew/Cellar/libxslt/1.1.28_1/bin/xslt-config
/opt/boxen/homebrew/Cellar/libxslt/1.1.28_1/bin/xsltproc
/opt/boxen/homebrew/Cellar/libxslt/1.1.28_1/include/libexslt/ (3 files)
/opt/boxen/homebrew/Cellar/libxslt/1.1.28_1/include/libxslt/ (21 files)
/opt/boxen/homebrew/Cellar/libxslt/1.1.28_1/lib/libexslt.0.dylib
/opt/boxen/homebrew/Cellar/libxslt/1.1.28_1/lib/libxslt.1.dylib
/opt/boxen/homebrew/Cellar/libxslt/1.1.28_1/lib/pkgconfig/ (2 files)
/opt/boxen/homebrew/Cellar/libxslt/1.1.28_1/lib/ (5 other files)
/opt/boxen/homebrew/Cellar/libxslt/1.1.28_1/share/aclocal/libxslt.m4
/opt/boxen/homebrew/Cellar/libxslt/1.1.28_1/share/doc/ (90 files)
/opt/boxen/homebrew/Cellar/libxslt/1.1.28_1/share/man/ (3 files)
Run Code Online (Sandbox Code Playgroud)
brew list libxml2
/opt/boxen/homebrew/Cellar/libxml2/2.9.4/bin/xml2-config
/opt/boxen/homebrew/Cellar/libxml2/2.9.4/bin/xmlcatalog
/opt/boxen/homebrew/Cellar/libxml2/2.9.4/bin/xmllint
/opt/boxen/homebrew/Cellar/libxml2/2.9.4/include/libxml2/ (47 files)
/opt/boxen/homebrew/Cellar/libxml2/2.9.4/lib/libxml2.2.dylib
/opt/boxen/homebrew/Cellar/libxml2/2.9.4/lib/cmake/libxml2/libxml2-config.cmake
/opt/boxen/homebrew/Cellar/libxml2/2.9.4/lib/pkgconfig/libxml-2.0.pc
/opt/boxen/homebrew/Cellar/libxml2/2.9.4/lib/ (3 other files)
/opt/boxen/homebrew/Cellar/libxml2/2.9.4/share/aclocal/libxml.m4
/opt/boxen/homebrew/Cellar/libxml2/2.9.4/share/doc/ (153 files)
/opt/boxen/homebrew/Cellar/libxml2/2.9.4/share/gtk-doc/ (55 files)
/opt/boxen/homebrew/Cellar/libxml2/2.9.4/share/man/ (4 files)
Run Code Online (Sandbox Code Playgroud)
gem install libxml-ruby 产生以下错误:
Building native extensions. This could take a while...
ERROR: Error installing libxml-ruby:
ERROR: Failed …Run Code Online (Sandbox Code Playgroud) 交叉编译工具时,通常需要提供"目标三元组".给出的例子
等等...
这些三元组有时实际上是四个组件,具有以下形式:
<CPU>-<MANUFACTURER>[-<KERNEL>]-<OS>
内核是可选的,制造商可以是"未知"或"无"之类的东西,因为它通常是不相关的.
我还没有找到一个记录所有可能值的页面.我知道所有组件都是"自由风格",因此没有官方标准会强制您使用官方标准化列表中的组件.然而,工具设计人员和配置脚本编写者希望用户将这些三元组指定给他们的工具/脚本,因此他们必须考虑到一些可能的值,并且应该有类似"非官方列表"工具制造商,脚本编写者和用户可以使用的一个参考.
有没有人找到过这样的清单?
c ×2
macos ×2
oop ×2
unix ×2
algorithm ×1
autoconf ×1
binary ×1
clock ×1
cocoa ×1
combinations ×1
crash ×1
file-io ×1
homebrew ×1
interpreter ×1
java ×1
libxml-ruby ×1
libxml2 ×1
opcode ×1
performance ×1
permutation ×1
posix ×1
powerpc ×1
pthreads ×1
rubygems ×1
time ×1