Please see my last comment to the question, the one saying "I think I understand the confusion".
To understand things, you need to understand all the operation from CPU to the application. You don't, so it's hard to explain, needs time and patience.
How do you think the applications work with OS? C++ and all languages addressing native platforms, compile to native code, writing native CPI instructions. This code is not exactly the same as the code when it is executed by the process when an application is started. The code is created by a
compiler and uses relative addressing and referenced to other parts of code, which are prescribed in the form of names (strings) in the binary obj or lib file. Those files are put together in one executable code (DLL or EXE, it does not matter, this is just the PE file) by the
linker. The linker takes the symbolic names and links them by addresses. Those addresses are still relative, only the symbols are eliminated. Then the third player comes up, the
loader, the least known and most sophisticated part. It actually creates code in memory. Each process gets its separate virtual address space using virtual memory. The code blocks, all calls, objects are shifted in sync with addresses referencing them, to fit in the memory the OS provides, code and data descriptors are set up. The problem here: where are the OS calls? Get back to the stage between compilation and linkage: they are the symbolic names of OS DLLs (shared objects, or something else) and function names. This is all resolved in the PE file.
Now, all OS are different, so the compiled code is different. Not just in names: OS calls do not have one-to-one correspondence. Nothing is cross-platform, except common standard library things, such as console and file I/O. It's not "cross-platform", it's portable. It means that there are libraries that bind OS with those standard portable calls. The source code is the same, but the libraries are different on implementation side. Other aspects of code, notably, UI, is not portable. It's a lot more than UI: serial and parallel I/O, synchronization primitives, system-specific facilities, a lot of things. Note that the same library has separate version of every platform and every CPU
instruction set architecture. Say, Windows for x86 is one thing, Windows for x86-64 is another one, and Windows for IE-64 is different, so same goes for the C++ libraries for these platforms — they are all different version. And there are a lot more platforms, non-Microsoft once.
How more complex C++ application can be made portable? With UI and other features? There is only one way: only if they come with the libraries playing the role of
OS and hardware abstraction layers. One example is QT:
http://en.wikipedia.org/wiki/Qt_%28software%29[
^],
https://qt-project.org/[
^]. On this example, how the same QT application can work on different platforms? First of all, it cannot: first, it needs to be re-build for different platform. Each platform supposedly has its one version of QT, which should be installed on development machine, only them you can build it. But when your solution is built, you need to deploy it with the application to the platform supported by one or another QT version. If such version does not exist, your application won't work. If the user, when asked for confirmation, says "What is that QT? Who knows? I don't want to install it", your application won't work. Is it portable? Yes, potentially? Is it "cross-platform"? Ha-ha, only in the sense I just explained.
With Java, things are different. The role of such OS and hardware abstraction is played by its
virtual machine, JVM. This is not a library. This is a whole platform working on top of native OS. There are JVM implementations for different OS. They have to surfaces: the backs ends face native OS, so they are all different, in the same sense as the C++ libraries I mentioned above. On the top, front-end size, it faces Java application, and this interface is unified. Java does not compile in CPU instructions, it compiles to the "fictional" instructions of the virtual machine,
byte-code. The byte code is compiled on the fly using Just-In-Time compilation (JIT). The virtual machine translates byte-code instruction into native code and executes it.
With .NET, the OS and hardware abstraction part is the .NET Framework implementing
CLR. .NET applications are also compiled kind of byte-code, CIL code, JIT compilation is used, but the compiled CIL code is packed into regular PE files, but the files are special, then can only work if CLR is installed. There are CLR implementation for different OS, such as Mono. The same PE file can be used for different CPU architectures and different platforms, no recompilation is done, but there are exclusions: application can target concrete CPU instruction-set architectures, usually for compatibility with some native-code modules (in .NET it can be done with C++/CLI or P/Invoke, Java also can use native code via JNI). Such applications looses part of its portability, they can work only with the CPIs compatible with the target CPU.
Please see:
http://en.wikipedia.org/wiki/Java_virtual_machine[
^],
http://en.wikipedia.org/wiki/Bytecode[
^],
http://en.wikipedia.org/wiki/Virtual_machine[
^],
http://en.wikipedia.org/wiki/Portable_Executable[
^],
http://en.wikipedia.org/wiki/Just-in-time_compilation[
^],
http://en.wikipedia.org/wiki/Compiler[
^],
http://en.wikipedia.org/wiki/Linker_%28computing%29[
^],
http://en.wikipedia.org/wiki/Loader_%28computing%29[
^],
http://en.wikipedia.org/wiki/Instruction_set[
^],
http://en.wikipedia.org/wiki/Common_Language_Runtime[
^],
http://en.wikipedia.org/wiki/Platform_Invocation_Services[
^],
http://en.wikipedia.org/wiki/C%2B%2B/CLI[
^],
http://en.wikipedia.org/wiki/Cross-platform[
^],
http://en.wikipedia.org/wiki/Operating_system_abstraction_layer[
^],
http://en.wikipedia.org/wiki/Hardware_abstraction[
^].
—SA