第5课 - C++新的关键字(类型转换/new/命名空间)

第5课 - 新的关键字

    一.动态内存分配和释放(C语言是通过标准C库里面的malloc,free函数分配,不只属于C语言)

        1.1 C++中通过new关键字进行动态内存申请

        1.2 C++中的动态内存申请时基于类型进行的

        1.3 delete关键字用于内存释放

            变量申请:

                Type* pointer = new Type;

                ......

                delete pointer;

            数组申请:

                Type* pointer = new Type[N];

                ......

                delete[] pointer;

      

  Source Example 1.3:
            #include <iostream>

            /* run this program using the console pauser or add your own getch, system("pause") or input loop */

            int main(int argc, char** argv) {
                
                int *p = new int;
                
                *p = 5;
                
                printf ("p = %p\n", p);
                printf ("*p = %d\n", *p);
                
                delete p;
                
                p = new int[10];
                
                for (int i = 0; i < 10 ; i++)
                {
                    p[i] = i + 1;
                    printf ("p[%d] = %d\n", i, p[i]);
                }
                
                delete[] p;
                
                return 0;
            }


            输出结果如下
            
            特别要注意如果申请的是一个数组,在释放的时候必须delete[] p,否则如果delete p,那么就只会释放一个字节,造成内存泄漏!
    

 

        1.4. new关键字和malloc关键字的区别

 

            1.4.1 new关键字是C++的一部分,malloc是C库提供的函数

            1.4.2 new以具体l类型为单位进行内存分配,malloc只能以字节为单位进行内存分配

            1.4.3 new在申请单个类型变量是可进行初始化,malloc不具备内存初始化的特性,具体初始化方式见如下代码

                  

 Source Example 1.4.3
                    #include <iostream>

                    /* run this program using the console pauser or add your own getch, system("pause") or input loop */

                    int main(int argc, char** argv) {
                        
                        int *pi = new int(1);
                        float *pf = new float(2.0);
                        char *pc = new char('c');
                        
                        printf ("*pi = %d\n", *pi);
                        printf ("*pf = %f\n", *pf);
                        printf ("*pc = %c\n", *pc);
                        
                        delete pi;
                        delete pf;
                        delete pc;
                        
                        return 0;
                    }

                    输出结果如下
                    

    二.C++中的命名空间

        2.1 在C语言中只有一个全局作用域(标识符之间可能发生冲突)

        2.2 C++中提出了命名空间的概念

            2.2.1 命名空间将全局作用域分成不同的部分

            2.2.2 不同命名空间的标识符可以同名而不会发生冲突

            2.2.3 命名空间可以相互嵌套

            2.2.4 全局作用域也叫默认命名空间

            2.2.5 C++命名空间的定义

                namespace name {/* */}
                

Source Example 2.2.5:
                    #include <iostream>
                    #include <stdio.h>

                    /* run this program using the console pauser or add your own getch, system("pause") or input loop */
                    namespace First
                    {
                        int i = 0;
                    }

                    namespace Second
                    {
                        int i = 1;
                        namespace Internal
                        {
                            struct P
                            {
                                int x;
                                int y;
                            };
                        }
                    }

                    int main(int argc, char** argv) {
                        
                        
                        return 0;
                    }

                编译通过,两个同名变量i位于不同的命名空间内,因此可以编译通过。

            2.2.6 C++命名空间的使用

                a.使用整个命名空间 using namespace name;

                b.使用命名空间中的变量 using name :: variable

                c.使用默认命名空间的变量 ::vaiable

                    (默认情况下可以使用默认命名空间的所有标识符)

                    
          

  Source Example 2.2.6:
                #include <iostream>
                #include <stdio.h>

                /* run this program using the console pauser or add your own getch, system("pause") or input loop */
                /* 定义First命名空间 */
                namespace First
                {
                    int i = 0;
                }
                /* Second */
                namespace Second
                {
                    int i = 1;
                    namespace Internal /* 嵌套定义Internal命名空间 */
                    {
                        struct P
                        {
                            int x;
                            int y;
                        };
                    }
                }

                int main(int argc, char** argv) {
                    
                    /* 使用整个First命名空间的标识符 */
                    using namespace First;
                    /* 使用Second命名空间里面的Internal命名空间的P */
                    using Second::Internal::P;
                    
                    printf ("i = %d\n", i);
                    printf ("i = %d\n", Second::i);
                    
                    P p = {2,3};
                    
                    printf ("p.x = %d\n", p.x);
                    printf ("p.y = %d\n", p.y);
                    
                    return 0;
                }

                输出结果如下

      

    三.强制类型转换

        3.1 C语言中的强制类型转化

            (Type)(Expression) or Type(Expression)
          

 Source Example 3.1:
                #include <iostream>
                #include <stdio.h>

                /* run this program using the console pauser or add your own getch, system("pause") or input loop */

                typedef void(PF)(int);

                struct Point {
                    int x;
                    int y;
                };

                /* 下面的强制类型转换完全是乱来,但是编译通过! */
                int main(int argc, char** argv) {

                    int v = 0x12345;
                    PF* pf = (PF *)v;
                    char c = char(v); /* 等同于char c = (char)v; */
                    
                    pf(v);
                    
                    Point* p = (Point*)v;
                    printf ("p->x = %d\n", p->x);
                    printf ("p->y = %d\n", p->y);
                    
                    return 0;
                }

               

            结论: C方式的强制类型转换存在问题,强制类型转换是不被推荐的,与goto一样,应尽量避免。

                a.过于粗暴

                    任意类型之间都可以进行转换,编译器很难判断其正确性

                b.难于定位

                    在源码中无法快速定位所有使用强制类型转换的语句

                现代软件三大难找的BUG:

                    1. 运算符优先级,位运算,逻辑运算,数学运算混合时

                    2. 多线程编程时各个线程之间的交互

                    3. 强制类型转换

        

        3.2 C++将强制类型转换分为4种不同的类型

                static_cast,const_cast,dynamic_cast,reinterpret_cast

            用法:

                xxx_cast<Type>(Expression)

                Type是最终要转换目标的类型

                Expression是要转换的表达式

            3.2.1 static_cast强制类型转换

                a.用于基本类型间的转换,但不能用于基本类型指针间的转换

                b.用于有继承关系类对象之间的转换和类指针之间的转换

              

  Source Example 3.2.1:
                    #include <iostream>
                    #include <stdio.h>

                    /* run this program using the console pauser or add your own getch, system("pause") or input loop */



                    int main(int argc, char** argv) {

                        int i = 0;
                        char c = 'c';
                        int *pi = &i;
                        char *pc = &c;
                        
                        c = static_cast<char>(i);/* It is Ok! */
                        pc = static_cast<char *>(pi);/* Error! */
                        
                        return 0;
                    

                上面的例子表明static_cast不能用于基本类型指针间的转换。

                注意"static_cast是编译器进行转换的,无法在运行时检测类型,所以类类型之间的转换可能存在风险。

            3.2.2 const_cast强制类型转换

                    用于去除变量的const属性                

  Source Example 3.2.2:
                      #include <iostream>
                    #include <stdio.h>

                    /* run this program using the console pauser or add your own getch, system("pause") or input loop */

                    int main(int argc, char** argv) {

                        const int& j = 1;                 /* 定义一个只读变量j */
                        int &k = const_cast<int &>(j);  /* 去掉只读变量的const属性,j的只读变量属性就没有了 */
                        const int x = 2;                 /* 定义一个真正的常量 */ 
                        
                        /* 去掉常量的const属性,即将常量强制转换为引用 */ 
                        /* 引用的本质就是指针,需要取址,因此会给x常量分配空间*/
                        /* 同时y这个引用就是x空间的别名 */
                        int &y = const_cast<int &>(x);  / 
                        
                        k = 5;
                        
                        printf ("k = %d\n", k);
                        printf ("j = %d\n", j);

                        y = 8;
                        
                        /* x为常量,被放入了符号表里面,因此x的值不会改变,但是x所在内存空间的值变为了8 */
                        printf ("x = %d\n", x);     
                        printf ("y = %d\n", y);            
                        /* x,y处于同一块内存空间,地址相同 */ 
                        printf ("&x = %p\n", &x);        
                        printf ("&y = %p\n", &y); 
                        
                        return 0;
                    }

                    输出结果如下
                    
                    

            3.2.3 reinterpret_cast强制类型转换

                a.用于指针类型间的强制类型转换

                b.用于整数和指针类型间的强制类型转换

             
                  

                    #include <iostream>
                    #include <stdio.h>

                    /* run this program using the console pauser or add your own getch, system("pause") or input loop */

                    typedef void(PF)(int);

                    int main(int argc, char** argv) {
                        
                        int i = 0;
                        char c = 'c';
                        int *pi = reinterpret_cast<int *>(&c);
                        char *pc = reinterpret_cast<char *>(&i);
                        PF* pf = reinterpret_cast<PF*>(0x123456);
                        
                        c = reinterpret_cast<char>(i); /* Error, static_cast should be used here */    
                        return 0;
                        
                    }

                注意:

                    reinterpret_cast直接从二进制位进行复制,是一种极其不安全的行为。

            3.2.4 dynamic_cast强制类型转换

                a.主要用于类层次间的转换,还可以用于类之间的交叉转换

                b.dynamic_cast具有类型检查功能,比static_cast更安全

                
    

    四.小结

        4.1 C++中内置了动态内存分配的专用关键字

        4.2 C++中的动态内存分配是基于类型进行的

        4.3 C++中命名空间概念结局了名称冲突的问题

        4.4 C++细化了C语言的强制类型转换

             4.4.1 C++不推荐在程序中使用强制类型转换

             4.4.2 C++建议在强制类型转换时考虑一下究竟希望什么样的转换

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值