[C++学习笔记02]从C到C++

  1. bool类型
      取值:true,false。
      大小:VC占1个字节。

  2. const限定符
      常量(常变量)声明形式:
        const 数据类型 常量名=常量值;
        数据类型 const 常量名=常量值;
        注:上面两者等价。
      例子说明:
    #include <iostream>
    using namespace std;
    
    int main(void)
    {
        //const int a;            Error,常量必须初始化
        const int a = 100;        
        //a = 200;                Error,常量不能重新被赋值
    
        int b = 22;
        const int * p;             //const在*左边,表示*p为常量,经由*p不能更改指针所指向的内容
        p = &b;
        //*p = 200;                Error,常量不能重新被赋值
        //int * const p2;          Error,p2为常量,常量必须初始化
        int * const p2 = &b;       //const在*右边,表示p2为常量
        //int c =100;
        //p2 = &c;                 Error,常量不能重新被赋值
        *p2 = 200;
    
        cout<<b<<endl;
    
        return 0;
    }
  3. const与#define
      const定义的常量与#define定义的符号常量的区别:
        前者有类型,要进行类型检查,后者只是单纯的替换;
        前者在编译期分配内存,后者在预处理期替换,不分配内存;
        作用域不同,前者的作用域与一般的变量的作用域相同,后者为定义处到程序结束,可以使用#undef取消。
      #define容易产生副作用
    //Effective C++ 3rd的一个例子。
    #define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b))
    
    int a = 5;
    int b = 0;
    CALL_WITH_MAX(++a, b);    //a被累加二次
    CALL_WITH_MAX(++a, b+10);    //a被累加一次
    
    // 在这里,调用f之前,a的递增次数竟然取决于“它被拿来和谁比较”
      #define中#,##的使用
    #include <iostream>
    using namespace std;
    
    //#define STR(a) a  // 不使用#,使用STR(a),a未声明,会报错。
    #define STR(a) #a // 字符串化,当这里使用#,你使用STR(a),a未声明,会被转化成字符串"a"。
    #define CAT(a, b) a##b // 连接作用
    
    int main()
    {
        int ab = 100;
        const char *str = STR(a);
        cout <<str << endl; //"a"
        cout << CAT(a, b) << endl; // 100
    
        return 0;
    }
  4. 结构体对齐
    什么是结构体对齐?
        编译器为每个“数据单元”按排在某个合适的位置上。
        C、C++语言非常灵活,它允许你干涉“内存对齐”。
      为什么要对齐?
        性能原因:在对齐的地址上访问数据快。
      对齐方法
         1.第一个变量与结构体变量的偏移量为0;    
        2.计算每个变量的对齐数(编译器设置的对齐数与该成员大小的较小值);
        3.每个成员变量 的地址为:对齐数的整数倍;对其到对齐数整数倍的地址;
        4.结构体总大小为最大对齐数的整数倍。
      对齐案例
    // vc++默认对其整数为8,g++默认为4
    // 在属性->C/C++->代码生成可以修改
    // 或者 #pragma pack(8)修改
    // #pragma pack()取消修改
    /*
    struct MyStruct {
        char ch; // 0
        double db; // 8 
        int it; // 16-23
    }; // 24*/
    
    /*
    struct MyStruct {
        char ch; // 0
        int it; // 4
        double db; // 8-15
    }; // 16*/
    
    /*
    struct MyStruct {
        char ch1; // 0
        int it; // 4
        char ch2; // 8
        double db; // 16-23
    }; // 24*/
    
    struct MyStruct {
        double db1; //0
        char ch1; //8
        float ft; //12
        int it; // 16
        char ch2; // 20
        double db2; // 24-31
    }; // 32

     

  5. 域运算符
      作用:
        用于对与局部变量同名的全局变量进行访问;
        用于表示类的成员。

  6. new、delete运算符
      new语法:
        指针变量=new 数据类型(初始化参数-可选);
        指针变量=new 数据类型[长度n];
      delete语法:
        delete 指针变量;
        delete [] 指针变量;
      new一个新对象过程:
        内存分配(operator new)
        调用构造函数
      delete一个对象过程:
        调用析构函数
        释放内存(operator delete)
      operator new/new operator/placement new:
        operator new:只分配内存
        new operator:调用构造函数 + 分配内存
        placement new:不分配内存,调用拷贝构造函数

  7. 重载
      函数重载:又称为函数的多态(静态多态),编译时期指定函数入口地址,静态联编。
        成立条件:
          
    形参数量不同;
          形参类型不同;
          形参的顺序不同;
          形参数量和形参类型都不同。
           注:函数重载跟函数的返回值无关。
      派生类与基类通过虚函数实现的多态(动态多态),运行时期指定函数入口地址,动态联编。

  8. name managling与extern “C”
      name managling:就是将函数的名字修改,实现函数重载。
      extern "C"使用:
    #ifdef __cpluscplus
    extern “C”
    {
    #endif
    ...
    #ifdef __cpluscplus
    }
    #endif
  9. 带默认参数的函数
      几点说明:
        1.函数既有定义又有声明时,声明时指定后,定义后就不能再指定默认值;
        2.默认值的定义必须遵守从右到左的顺序,如果某个形参没有默认值,则它左边的参数就不能有默认值;
        3.函数调用时,实参与形参按从左到右的顺序进行匹配

  10. 引用
      几点说明:
        1.引用在定义的时候要进行初始化;
        2.引用一经初始化,不能重新引用其他变量;
        3.非const引用不能引用const常量;
        4.double val3 = 3.14;const int& ref4 = val3;只是报警告,等价于定义一个临时变量int tmp = 3,然后让ref4去引用tmp,当ref4不是常引用的时候,编译器直接报类型转换错误(强类型)。
        5.同理4,const int &ref = 10;常引用使用字面量初始化自然就是有效的。
        6.不能返回对局部变量的引用,通过返回引用,可以让函数调用作为左值,改变全局或者静态变量的值(通常在函数中返回静态或者全局变量的引用)。
        7.教课书上说的是引用不分配内存空间,但是不是与引用是使用指针实现的矛盾么?

  11. 内联函数
      几点说明:    
        1.不能有递归    
        2.不能包含静态数据    
        3.不能包含循环    
        4.不能包含switch和goto语句    
        5.不能包含数组    
        注:若一个内联函数定义不满足以上限制,则编译系统把它当作普通函数对待。
      
    内联函数与带参数宏区别
        一样是内联函数要进行类型检查,宏只是简单的替换。
  12. 类型转换
      旧式转换
        (T)expr
        T(expr)
      新式转换
        const_cast<T>(expr)
    void change(const int &r)
    {
        int &rr = const_cast<int &>(r);
        rr = 200;
    }
    
    void test(int &r)
    {
        r = 100;
    }
    
    int main(void)
    {
        int n = 100;
        const int *pn = &n;
        const int &rn = n;
    
        //const_cast一般用于指针或者引用
        // 个人觉得const_cast应该应用到该场景
        // 即变量本身是变量,但是被常量指针或者常引用引用了
        // 希望通过指针,或者引用去修改最初变量的值
        int *p = const_cast<int *>(pn);
        *p = 200;
        cout << n << endl; // 200 
        int &r = const_cast<int &>(rn);
        r = 300;
        cout << r << endl;
    
        const int val = 100; // 常量本身是不能更改的,最多你能更改它在内存中的值,当使用该常量时,它去常量表直接取值
        // int varl = const_cast<int>(val); //Error, 无法从“const int”转换为“int”
    
        int m = 10;
        change(m);
        cout << m << endl; // 200
    
        //使用const_cast去除const限定的目的不是为了修改它的内容
        //使用const_cast去除const限定,通常是为了函数能够接受这个实际参数
        const int a = 10;
        //test(a); // Error, 从“const int”转换为“int &”
        test(const_cast<int&>(a)); // 只是修改了a在内存中的值,a本身的值不会修改,在常量表中
    
        return 0;
    }
        static_cast<T>(expr)
          理解成一般的强制转换即可。
        reinterpret_cast<T>(expr)
          “通常为操作数的位模式提供较低层的重新解释”也就是说将数据以二进制存在形式的重新解释。

    int main(void)
    {
        int i;
        char *p = "This is a example.";
        i = reinterpret_cast<int>(p);
        //此时结果,i与p的值是完全相同的。
    
        //cout << p << endl; // This is a example.
        cout << i << endl; // 13032560,cout 见int型,那么直接相当于printf的%d输出了他的地址
        printf("%s\n", i); // This is a example.
        printf("%d\n", i); // 13032560
        printf("%s\n", p); // This is a example.
        printf("%d\n", p); // 13032560
    
        int m = 10;
        int *ip = &m;
        char *pc = reinterpret_cast<char*>(ip);
        // 程序员需要记得pc所指向的真实对象是int型,并非字符串。
        // 如果将pc当作字符指针进行操作,可能会造成运行时错误
        // 如int len = strlen(pc);
    
        printf("%d\n", *pc); // 10
    
        return 0;
    }
        dynamic_cast<T>(expr)
          
    执行“安全向下”转型操作,也就是说支持运行时识别指针或所指向的对象,这是唯一个无法用旧式语来进行的转型操作。

转载于:https://www.cnblogs.com/ifpelset/articles/4505648.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值