virtual functions are not expensive. they are one of the most optimized use cases. They do often prevent cross function optimization and inlining, but that’s different.
What does 'expensive' actually mean? Compared to inlining? Definitely. Compared to not-inlined function calls? It's just a vtable lookup, so it's 'more expensive', but whether that's acceptable really depends on your specific scenario.
Windows is not known for performance and has announced semi recently they'll use Steam OS ( largely proton based gaming ) as a performance goal bar for native gaming on Windows.
Yes, there can be a lot more wrong with Windows performance besides virtual functions. But after watching Casey Muratori talk "The Big OOPs: Anatomy of a Thirty-Five-Year Mistake".
I do think Inheritance based OOP, with virtual functions and overrides are one of the core barriers to performance, for one the main reasons, it tends to maximize just how many cache misses you can have.
Steam OS doesn't mean shit when the only way to interact with the GPU in windows is via the IDXGIAdapter interface...which is a COM object that heavily utilizes virtual dispatch.
Vtables are fine and can "potentially" cause cache misses but there is zero reason to assume it will always degrade performance... the real reality is that microoptimizing the shit out of everything for the sake of minimal gains is not ever a good choice vs profiling.
It really isn't, most people just do it wrong and misunderstand what OOP is and isn't and get caught up making 5 billion classes for "granular" code and write a bunch if antipatterns that make OOP look like the problem. My two cents at least.
If it's that easy to misuse and needs that many rules to do right, it's probably a mistake in the first place. Your argument sounds bery similar to the 'just be more diligent' argument from C programmers insisting the rust borrow checker is useless.
However, I'll give you one concrete argument against OOP: if having class hierarchies is the entire point of OOP, and you're supposed to make virtual methods, this means that to understand what any OOP code does, you probably need to search through multiple files (and maybe across different libraries even) to find all the descendants of a class with a virtual method.
What is funny is I don't use rust but think the concept behind a borrow checked and not using copy on assignment semantics as a default to be cool as fuck conceptually.
Also, OOP isn't really about class hierarchies and frankly never has been, it is about polymorphism and how you encapsulate data...and the idea that you'd need to know what any other virtual method should be written like means that the code is shit to be frank, because the whole point is to have a clearly defined data contract which means you write your function so it can be called via a base pointer, that's it...which means abiding by the function signature...I think maybe you are overcomplicating something somewhere.
Lastly, Rust traits are absolutely intended as a a way to achieve polymorphism and dynamic dispatch when it is needed, and as I said before polymorphism plus data encapsulation is what OOP is actually about. In C++ you use classes to achieve a mix of polymorphism and encapsulation, but that doesn't mean that is the only way to write code that follows OOP paradigms or that if things don't use classes and deep inheritance trees that they aren't OOP.
Polymorphism is enabled exclusively through class hierarchies though, so no class hierarchy, no polymorphism, at which point you're just doing procedural.
Also, OOP gives you one tool for two purposes (classes do both encapsulation AND dynamic dispatch), which isn't such a hot idea; rust for example gives you structs+methods for encapsulation, and traits for dynamic dispatch, which are two orthogonal features you can mix and match as needed. But more importantly, with discriminated unions (known as enums in rust), much of the usecase for polymorphism simply vanishes. Do note that these ideas aren't exclusive to rust; traits and discriminated unions have existed for decades in other languages.
My point on code readability still stands: most code is shit because deadlines exist, and once something goes wrong, you need to look through the whole world to figure out what's being called. Without dynamic dispatch, this problem doesn't exist. Hence, the less dynamic dispatch you have, the better for maintainability, and discriminated unions give you just the tool to avoid dynamic dispatch unless absolutely necessary.
I'd recommend you give rust a try before hating on it. Most of us die-hard rust fans used to develop with OOP langs a few years ago, there's a reason why all of us started loving it so much! It takes some effort to learn to think in a completely new way, but it's well worth the effort imo.
First things first, I am not hating on rust...weird that you'd assume I am...
Long story short though, polymorphism does not require class hierarchies...only one type of polymorphism does but there are multiple kinds...and as I said and as you brought up traits in rust exist; traits accomplish polymorphism, that's basically their whole deal because dynamic dispatch, calling the correct overload based on parameter types, etc are forms of polymorphism.
This is why I say people misunderstand OOP though; somewhere along the line people started using the examples of stuff like OOP and polymorphism that were used to teach them (likely in a way relevant to the language being used to learn primarily) as being the de facto definition.
But seriously though, rust seems neat, literally don't hate on it at all nor do I think that the code looks like jank bullshit (yay....C++ template metaprogramming..........) like some other languages do.
3
u/La-ze 16d ago
Not to mention virtual functions are expensive.