In very simple terms, what makes a program cross-platform is your ability to take the sources from one environment, compile them in another and have the finished product work as expected.
In less simple terms, it's having full overlap between what the program expects to be available and what your target environments provide. Once you do anything that makes the overlap less than 100%, such as using an environment-specific library or a language feature with undefined behavior, you've tied your program to the environments(s) that can supply the non-overlapping features.
("Platform" is a bit of a squishy word. People can talk about Windows and Unix as platforms or Linux, OS X, BSD and Solaris as platforms even though they're all nominally Unix. Stir in running any of the above on different hardware architectures and things become even more nebulous. Having said that, I'll start using the word.)
Fortunately, there are standards to ease this problem:
Languages. You're writing C++, which was first standardized by the ISO in 1998. Any program you write that conforms to that standard can be compiled and run with the expected results on any platform with a conforming compiler and runtime. There's no limit to the size or sophistication of the program as long as it doesn't deviate from the standard. If a program that provably meets the standard doesn't run as expected on a given platform, the implementation of the language on that platform becomes suspect. Many languages have carefully-designed test suites that can be used to verify conformance.
Java gets a special mention because not only does it standardize a language, it also standardizes object code, which makes its programs runnable anywhere without recompilation. This is accomplished by pushing the point of conformance down an additional layer into a platform-customized virtual machine (or even hardware) which is capable of running the object code.
APIs. The calls you make to have your program do certain things can be standardized as well. Like languages, these APIs and the libraries that implement them can be set up to behave as the callers expect using an underlying implementation suitable for a particular platform. One such API is the IEEE's POSIX standards, which arose as a way to stem the fragmentation that was happening in Unix during the 1980s. (I was around then; that aspect of it wasn't fun.) By defining standard calls and behavior, system vendors could give their customers confidence that migrating to their OS wasn't going to entail huge amounts of work as it had in the past. POSIX was widely adopted and is still in wide use almost 30 years later.
I've done numerous projects which slavishly adhered to the standards because I knew they were going to have to run on multiple platforms. What I got for my troubles was code that worked everywhere I'd planned to run it and pleasantly surprised me on a few where I hadn't.