TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

Polymorphism in C

8 点作者 gwoplock大约 3 年前

1 comment

kazinator大约 3 年前
<p><pre><code> ShapeInterface *CircleAsShape = &amp;(ShapeInterface) { .Area = (double (*)(void *)) circle_Area }; </code></pre> Phooey! Just<p><pre><code> ShapeInterface CircleAsShape = { .Area = (double (*)(void *)) circle_Area }; </code></pre> then refer to &amp;CircleAsShape.<p>This is error-prone because of the cast. circle_Area could have the wrong parameters, or the wrong function could be used by accident and it will compile.<p>Conformance wise, it is undefined behavior: a function that takes a Circle * is being called via a pointer to a function that takes a double *.<p>You really want the circle_Area to have a Shape * argument.<p>Firstly:<p><pre><code> double shape_Area(Shape *shape) { return (shape-&gt;interface-&gt;Area)(shape); &#x2F;&#x2F; we lose shape-&gt;instance here, just shape } </code></pre> Then:<p><pre><code> double circle_Area(Shape *shape) { Circle *circle = (Circle *) shape-&gt;instance; ... } </code></pre> We now have a cast here; but that is now new. The same conversion was being done through the function pointer punning, without a cast (formally undefined in ISO C). Now we have well-defined behavior: the shape-&gt;instance pointer is really pointing to a Circle; it is valid to convert that pointer back to a Circle *.<p>Then we have:<p><pre><code> ShapeInterface CircleAsShape = { .Area = circle_Area }; </code></pre> So instead of a function pointer cast with undefined behavior, we have a well-defined function pointer treatment, with a correct cast between data pointers.<p>Another nice thing now is that type-specific implementation like circle_Area has access to the Shape pointer, and can invoke shape operations on it.<p><pre><code> double rectangle_Area(Shape *shape) { double length = shape_NominalLength(shape); double width = shape_NominalWidth(shape); return length * width; } </code></pre> Now you can have subtypes of rectangle which have a completely different internal organization, and don&#x27;t have to reimplement Area; rectangle_Area works for them. E.g. a PNG image could be a kind of rectangle, using rectangle_Area for its Area implementation. It just provides NominalLength and NominalWidth:<p><pre><code> double pngImage_NominalLength(Shape *shape) { PNGImage *image = (PNGImage *) shape-&gt;instance; return PNGIMage_width(image); &#x2F;&#x2F; PNGImage is an opaque type from some third party lib. } </code></pre> Whereas if rectangle_Area is like this:<p><pre><code> double rectangle_Area(Rectangle *rect) { return rect-&gt;length * rect-&gt;width; } </code></pre> that is not possible, even if you&#x27;re prepared to modify rectangle_Area somehow to try to make it work. It doesn&#x27;t have access to the shape container to be able to invoke the abstract shape call; there is no way it can work with PNGImage.