I haven't done C/C++ in a while, so I just randomly decided to get some confusion sorted out..Please look at the code and comments below. Maybe you can answer some of the questions in there. Or correct opinions.
Please, if you answer make your comments simple and concise. And less terminology, the usually confuse me. I appreciate this a lot thanks.

Code:
#include <iostream> 
using namespace std ; 
int main()
{
    //widening values ; 
    {
        char a2 = 'A'; 
        int a1 = a2; //Compiler widens implicitly.  Nothing is lost.  Here it knows the data type of a2 hence can look up ascii data
        
        //cout << a1 ; // should get 65
    }
    //narrowing values 
    {
        int a1 = 65 ;
        char a2 = (int)a1 ; //narrowing occurs implicitly in C++, but to show it explicitly anyways.       
        //cout << endl << a2 ; //should get 'A'
    }
    
    //widening addresses
    {
        char a = 'A';
        char *a1 = &a ;
        int **a2 = (int**)&a1 ; //all the compiler knows is it is storing an address a2 will point to an integer type address. 
        int b=**a2 ; //It doesn't know that the address in a2 belongs to a char so will just will return the binary representation..
        cout << endl <<(char) b; //have to cast it to tell compiler that b actually HAS or POINTS to a character. Not an integer.  (char)b should print the character
        //if i printed this way: cout << endl << b sometimes I get 65 or random crap
        //This could explain why in polymorphism the compiler needs pointers and vtables to look at the correct object and function to call. Otherwise it might just believe that the type of object and functinon 
        //to call belongs to the base type. Without vtables it implicitly believes that its type is of base due to upcasting.
    }
    
    //widening addresses part
    {
        //i could do it this way
         char a = 'A' ;
        int *a1 = (int*)&a ; 
        cout << endl << (char)*a1 ;
        // if I printed it this way: cout << endl << *a1 sometimes I get 65 or random crap
        // I get 65, so it still doesn't look up the ascii tables because it genuinely believes a1 POINTS TO AN INT. EEJIT
        //The compiler still doesn't know that the actual type is a character because due to the cast, it believes that it is pointing to an integer..hence we get 65 as expected.
        //(char)a1 will solve the problem.
    }
    //narrowing
    {
        int a1 = 65; 
            
        char *s = (char*)&a1; 
        cout << endl << *s;  //So far either way I print I get correct values
    }
    //So what happens with Narrowing? I put a larger content to box that can hold little content. 32bytes >> 8bytes since 8bytes can hold up to 255 
    //no data is truly lost however the  compiler complains.
    
    //But with widening, sometimes when I print as an integer I get what I should expect for the binary representation of the character it points to..
    //otherwise I get random crap. Because the compiler sees that the pointer type is integer but is only pointing to a memory location of only 8bytes..
    //the compiler will do what it thinks is right, at the moment of printing (I believe it wouldn't widen the memory location permanenty after the cast) 
    //it adds an extra 3bytes to the memory location to make 4bytes or whatever the case is depending on the system. 
    //This extra space might contain lots of stack garbage of might be filled with 0? Hence sometimes I get correct value otherwise ........?
    //I do not know.
    cin.get() ;
    return 0 ;
}