Reasons for 'large' objects, libraries and executables
The most common reason why executables are larger than expected is that they include debugging information, which is generated when source files are compiled with the "-g" option to GCC. Even when your own source files are compiled without debugging information, libraries linked with your executable may have been compiled with "-g" (possibly including system libraries that are distributed with MinGW).
To exclude debugging information from your executable, simply use the "strip" command to remove it, or alternatively use the "-s" option when linking.
Templates and the C++ Standard Library
When you use template classes such as the Standard Template Library, the compiler generates code separately for each instantiation (e.g. vector<int> and vector<string>) so the total code size can increase significantly. Other elements of the standard library such as iostreams, exception handling and std::string can seem large compared to small "Hello World" type programs but this overhead is generally constant so it will not be significant in more realistic applications.
C++ Exception Handling / Run-Time-Type-Information
When you compile C++ code gcc's default is to include RTTI. If you don't use RTTI then you can vastly reduce the size of the created code by compiling with "-fno-rtti".
And the same with C++ exceptions, use "-fno-exceptions" if you don't use C++ exceptions.
UPX (Ultimate Packer for eXecutables)
UPX is a tool that can decrease the size of executables, using very fast and very efficient in-place compression/decompression. It works for Windows and Linux executables and is free for use with all applications (including commercial apps).