C is used in Linux because of its versatility. It can do everything C++ can do, but also gives you fined-tuned control over every aspect of program at the cost of expertise.
Brainfuck can do everything C can do, but also gives you fine-tuned control over every atomic instruction of your program at the cost of expertise. So why isn't Linux written in Brainfuck?Because your argument is fallacious. C does not do everything C++ does, in the sense that C does not provide black-box abstractions that C++ provides. This means that C is fundamentally a less productive language when comparing the efficiency of experts in either language.
In C++ it has built-in OO functionality via classes. This makes things such as polymorphism much easier. BUT, by using classes you loose control of how object pointers are handled in the member functions, you loose control of how constructors/destructors are called and when, how member-function pointers are stored within the structure, etc etc.
Object pointers are always passed using thiscall, i.e. in the first (hidden) parameter to the function. This is not magic and does not remove anything. In fact, you can write a shim that translates from arbitrary C-style pointer passing to thiscall in three lines of code:
int InvokeFoo(int argument1, int argument2, Foo* ptr)
Construction and destruction are performed in very, very well defined ways and in well defined orders. I suspect you don't know C++ very well if you aren't aware of how to reason about construction and destruction order in a program. For stack-allocated objects it's bloody trivial; for free-store-allocated objects, it's only slightly more complex due to the presence of placement new and explicit destructor invocation. But those things give you perfect control over construction and destruction patterns, not remove control.
Your last "argument" is nonsensical. My best guess is that this is some oblique reference to the vtable, which is hardly something you need to tamper with... well, ever, really, in a proper C++ program. And guess what? If you're psycho and really want to write your own vtable, C++ has member function pointer syntax which lets you do so, while retaining thiscall semantics.
So there is absolutely no loss of control here - you just personally don't know how to obtain that control, apparently.
In C you can still do OO programming just like C++ classes by using structures and callbacks via function pointers. But, you get the ultimate control of how everything happens. YOU decide when constructors/destructors are called, YOU decide how object pointers are passed to member-functions, and YOU decide how member function pointers are stored in the structure and how they are alligned.
I control all of that in C++ too. And, what's better, I don't have to think about it 99% of the time, because the language makes certain default guarantees that accommodate the vast
majority of use cases with no issue.
And, guess what, I can still control all of that by hand if I really want to.
I could choose a special alignment of the function pointers that could produce huge performance boosts on certain systems.
AFAIK any decent C++ compiler will native-align the vtable entries; in fact, they'd almost have to bend over backwards not
to, because vtable entries are by definition machine pointer size to begin with. So I don't think you really know what you're comparing with here.
I could choose to pass the object pointer to the member function via the stack instead of a register, thus saving the register for a different parameter--which when used right, could provide huge performance boosts.
You can implement your own calling convention on top of thiscall trivially in C++, if you know what you're doing. (Compiler specific, of course.)
In C++ I am writing a bubble-sorting algorithm to sort a list of strings alphabetically. In C++ the quick'n'easy solution would be to store the strings as a std::string, and store the group of strings as a std::vector. Once sorting, each time I swap two strings a total of 10 things happen: A) a extra std::string constructor is called, and B) 3 memory-free operations, and C) 3 memory-alloc operations, and 3 memory-copy operations. If each string is 10,000 characters in size, and there are 40,000 strings your looking at a huge overhead implementing it this way.
Or you could use std::string::swap like any first-year C++ student would know to do, and your C++ program boils down to the C program with extra type safety and memory management safety.
The vast majority of people who argue against C++ are not arguing against the language itself, but rather against people who don't know how to use the language. Granted, crappy C++ "programmers" are a dime a dozen, so caveat emptor - but there's no really good argument for C over C++ that actually has to do with the languages themselves. It's all down to comparing tools, relying on history, selecting for certain types of programmers, or bigotry.