The problem with using #define rather than typedef or using is that [as has been pointed out] #define is a macro, and macros are evaluated and expanded by the preprocessor, so the compiler knows nothing about the data type you're trying to create because the #define directive is simply substituted with whatever comes after it.
The reason for using macros in languages such as C and C++ is to allow for things that aren't specifically to do with source code logic but are to do with source code structure.
The #include directive, for instance, quite literally includes the entire content of a file in place of the derective.
So, if myfile.h contains:
void func_1(int t);
void func_2(int t);
then
#inlude "myfile.h"
would expand the content of myfile.h, replacing the #include preprocessor directive with
void func_1(int t);
void func_2(int t);
The compiler then comes along and compiles the expanded file with class definitions, and other expanded macros!
It's why the macro
#pragma once
or
#ifndef __MYFILE_INCLUDE__
#define __MYFILE_INCLUDE__
is used at the start of header files to prevent multiple definitions occurring.
When you use an expression like #define INT64 unsigned int the preprocessor does exactly the same thing. It evaluates the expression, then replaces all occurrences of INT64 with unsigned int.
When you use a typedef, on the other hand, the compiler makes the type substitution, which means the compiler can warn about incorrect use of your newly created type.
#define would simply warn you of an incorrect use of unsigned int which if you have a lot of type substitution can become confusing!