The usual way, as suggested by assert(3) man page and habits (with <assert.h> in C or <cassert> in C++), is to define NDEBUG at the command line (e.g. to compile with gcc -Wall -DNDEBUG) for non-debug compilation. In your Makefile you could CPPFLAGS += -DNDEBUG in release mode (and compile with g++ -Wall -g in debug mode).
My own habit might be to have something like
#ifndef NDEBUG
#define dbgprintf(Fmt,...) do{fprintf(stderr,"%s:%d:" Fmt "\n", \
__FILE__, __LINE__, \
##__VA_ARGS__);}while(0)
#else
#define dbgprintf(Fmt,...) do{}while(0)
#endif
in a common header file, and to use dbgprintf("i=%d", i) elsewhere in the code.
Notice that I use constant string catenation on the Fmt macro argument, that I append a constant newline to it, and that my debugging output contains the source file name and line number (you might also use __func__ if you like). In pure C++ code, I might instead have
#ifndef NDEBUG
#define DBGOUT(Out) do{std::out << __FILE__ << ":" << __LINE__ \
<< " " << Out << std::endl;}while(0)
#else
#define DBGOUT(Out) do{}while(0)
#endif
and use DBGOUT("i=" << i) with the advantage of using specific definitions of operator << for my types.
\n