Thank you for your explanation. I realized that the difference in understanding between him and me stems from our understanding of “inheritance.”
My understanding of inheritance is a polymorphic implementation method characterized by coupling implementation-specific data with public implementation data. This is achieved by embedding the public implementation within the implementation. There are various polymorphic implementations competing with inheritance. Composition-based polymorphism is characterized by separating implementation-specific data from public implementation data. This is achieved by having the public implementation reference a pointer to the implementation-specific data. Interface-based polymorphism (I think this is the origin of the word “interface” for polymorphism. When other polymorphisms also use the word “interface”, I think it spreads backward from here. The “interface” that I repeatedly tried to explain above does not actually refer to the “interface” here, but I feel that the other party wants to express this meaning) expresses a simplified polymorphic implementation that only provides methods but not public data implementation. For some languages, interface-based polymorphism does not necessarily require vtb or similar back-end implementations, but is merely a semantic convention.
As for the placement of the vtable pointer, if the content of the vtable in a polymorphic state does not change at runtime, it can be placed anywhere, in the public implementation, or outside, together with the interface (well, this is what I just tried to explain the meaning of the “interface” I wanted to express, which is completely another thing and has nothing to do with interface-based polymorphism here). So, when I discussed “Allocator-style interfaces” above, I was actually discussing the pattern of moving the vtable from the implementation to the interface. I admit that the term “interface” here is easily confused with the “interface-style polymorphism” mentioned earlier, and the misunderstanding is understandable. In my testing, this pattern does indeed lead to better anti-virtualization optimization.
Because I’ve always had a need for these polymorphic implementations in C, I don’t really attach much sentimentality to terms like “inheritance,” “composition,” and “interface.” So, when I saw Io.Writer being considered inherited, I was met with a lot of pushback, which was quite baffling. To me, this implementation is the inheritance-based polymorphism I’m familiar with.
After your clarification, I now understand that many programmers don’t think of “inheritance” as a specific way to implement polymorphism, but rather as a way of thinking about how to design polymorphism using crude taxonomies, like “penguins inherit birds.” However, in practical areas like GUIs, where inheritance is particularly effective, inheritance-based polymorphism isn’t actually used in this way.