zoukankan      html  css  js  c++  java
  • C++ 归纳复习常规篇

    复习之前必须说一个关键点   C++这门语言是强类型语言,非常的强调类型

    1. 关键字const

       1.0 ) const 非指针

    int main()
    {
        const int a = 1;
        int *p = (int *)&a;
        *p = 10;
    
        return 0;
    }

      结果 a = 1    *p = 10

      原因:a是常量,处在常量符号表中。在编译阶段,编译器检查到使用a这个常量时,会从常量符号表中取出这个值,而不是从该地址内存中取值。

      当使用 &获取地址后,才会为这个地址申请内存。因此*p只是操作这个地址的值,不影响a的常量符号表中的值

      

      1.1 ) const 指针

    int main()
    {
        int a = 1;
        int b = 2;
    
        const int *p1 = (int *)&a;
        int *const p2 = (int *)&b;    
    
        *p1 = 10;   //错误 
         p1 = &b;    //正确
    
        *p2 = 20;   //正确
         p2 = &a;   //错误
    
        return 0;
    }

     当const在 * 左侧,即代码中的指针p1,表示修饰的是p1指向的地址的内存,因此,修改p1指向地址是可以的,但修改p1指向地址的内存编译器会报错。

     当const在 * 右侧,即代码中的指针p2,表示修饰的是p2指向的地址,因此,修改p2指向地址的内存是可以的,但是修改p2指向地址编译器会报错。

    2.  关键字const 与define的区别

      发生的时期不同。

           define发生在预处理阶段,进行简单的文本替换,但不会进行类型检查作用域检查。

           const在预处理之后的编译阶段,会对类型进行检查。

      因此用define的时候要格外小心,特别是宏定义一些函数的时候,要考虑符号的优先级问题。

    3. inline 内联函数

        一般调用一个普通较短的函数,就是跳转到这个函数名(地址),在一些实时性要求高的程序里面,跳转就要有系统开销(保存现场、恢复现场),会降低实时性。因此可以使用inlne关键字来修饰函数,类似于define,就是原地展开函数。

    内联是一种请求,如果函数过于复杂,编译器会拒绝内联,函数变回普通函数。

    4. 函数重载

       这是C++一个重要的特性。

      

    void r1chie(void)
    {
    
    }
    
    void r1chie(int a)
    {
    
    }
    
    void r1chie(int a,int b)
    {
    
    }
    
    void r1chie(char a)
    {
    
    }
    
    void r1chie(char a,int b)
    {
    
    }
    
    int main()
    {
      int a; 
      char c;
      r1chie();
      r1chie(a);
      r1chie(a,c);
      r1chie(c);
      r1chie(c,a);
    
      return 0;
    }

    我们反汇编上面这段函数

    00000000004006ef <main>:
      4006ef:    55                       push   %rbp
      4006f0:    48 89 e5                 mov    %rsp,%rbp
      4006f3:    48 83 ec 10              sub    $0x10,%rsp
      4006f7:    e8 ba ff ff ff           callq  4006b6 <_Z6r1chiev> //4006b6
      4006fc:    8b 45 fc                 mov    -0x4(%rbp),%eax
      4006ff:    89 c7                    mov    %eax,%edi
      400701:    e8 b7 ff ff ff           callq  4006bd <_Z6r1chiei> //4006bd
      400706:    0f be 55 fb              movsbl -0x5(%rbp),%edx
      40070a:    8b 45 fc                 mov    -0x4(%rbp),%eax
      40070d:    89 d6                    mov    %edx,%esi
      40070f:    89 c7                    mov    %eax,%edi
      400711:    e8 b1 ff ff ff           callq  4006c7 <_Z6r1chieii> //4006c7
      400716:    0f be 45 fb              movsbl -0x5(%rbp),%eax
      40071a:    89 c7                    mov    %eax,%edi
      40071c:    e8 b3 ff ff ff           callq  4006d4 <_Z6r1chiec>  //4006d4
      400721:    0f be 45 fb              movsbl -0x5(%rbp),%eax
      400725:    8b 55 fc                 mov    -0x4(%rbp),%edx
      400728:    89 d6                    mov    %edx,%esi
      40072a:    89 c7                    mov    %eax,%edi
      40072c:    e8 af ff ff ff           callq  4006e0 <_Z6r1chieci> // 4006e0
      400731:    b8 00 00 00 00           mov    $0x0,%eax
      400736:    c9                       leaveq 
      400737:    c3                       retq   

    可以看到,虽然函数名是一样的,但是地址却是不一样的。

    那么重载是根据什么呢?

       4.1 )参数的数量

       4.2 )参数的类型

       4.3) 参数的顺序

       4.4) 同一作用域

    5. 关键字new和delete

       malloc函数与关键字new。

        5.1)malloc是库函数,需要包含头文件。new需要编译器支持。

        5.2)malloc需要指定大小。new是编译器计算。

        5.3)malloc返回的是void *类型指针,还需要进行强转。new返回的是对象类型指针,因此不需要强转。

    使用new申请内存,如果内存不需要了就记得释放

    class r1chie
    {
    
    public:
          int a;
          int b;
    private:
          int c;
    
    };
    
    
    int main()
    {
        int *p1 = new int();
        int *p2 = new int(10);
        int *p3 = new int[10];
        char *p4 = new char();
        r1chie *p5 = new r1chie;
            int  **p6 = new int*[10];
    
        delete p1;
        delete p2;
            delete[] p3;
        delete p4;
        delete p5;
        delete[] p6[10];
    
    
        return 0;
    }

      6.  namespace 命名空间

      

    #include <iostream>
    
    namespace r1chie
    {
        int i = 1; 
    }
    
    namespace r1chie_2
    {
    
        int i = 10;
    }
    
    using namespace std;
    
    int main()
    {
        
        int i = 100;
        
        cout << r1chie::i << endl;
            cout << r1chie_2::i << endl;
        cout << i << endl;
    
        return 0;
    }

    输出结果:

    1

    10

    100

    7. C++的引用 &

      7.0)引用的本质就是指针。

         7.1)一个变量可以取多个别名

      

    int main()
    {
        int a = 1;
        int& b = a;    
        int& c = a;
        cout << a << endl;
        cout << b << endl;
        cout << c << endl;
    
        a = 2;
        cout << a << endl;
        cout << b << endl;
        cout << c << endl;
    
        return 0;
    }

    输出结果:

    1
    1
    1
    2
    2
    2

         7.2)引用必须初始化

    int main()
    {
        int& b;    // 编译器报错
     
        return 0;
    }

      7.3)引用只能初始化一次,不能改变为其它变量的引用

      7.4)不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名

      7.5)引用的类型要相同

    int main()
    {
            int a = 1;
            float& b = a; //报错
    
            return 0;
    }

     7.6)常引用

      7.6.1) 使用常引用,则不能使用引用对目标变量的值进行改变,从而使引用的目标成为了const。

    int main()
    {
            int a = 1;
            const int& b = a;
    
            a = 2;  //正确
            b = 3;  //报错
    
            return 0;
    }

         7.6.2)临时对象,临时对象都是const类型的

    string fun1();
    void fun2(string &s);
    
    fun2(fun1());  //报错
    fun2("Hello world"); //报错

      原因是将const类型转换成非const,C++是强类型的语言因此报错。

      7.7)C++中引用的内部实现

    int& a            --->      int* const a
    
    void f(int& a)    ----> void f(int* const a)
    {                       {
       a = 5;                   *a = 5;
    }                       }    

    关于引用这篇文章分析得很好  https://www.cnblogs.com/Mr-xu/archive/2012/08/07/2626973.html

    8. C++的强制类型转换

      这个又是一个比较重要的特性。

     四种转换:

         8.1)static_cast 

    变量和对象之间的转换 和 有继承关系的类对象指针转换,可通过父类对象去初始化子类对象(只会初始化父类部分)。    

    class Base
    {
    
    public:
        int a;    
        
        Base(int i)
        {
             a = i;
            cout << "This is Base"<< endl;
        }
    };
    
    class Child :public Base
    {
    
    public:
        int b;
        Child(int i) : Base(i)
        {
            b = i;
            cout << "This is Child" << endl;
        }
    };
    
    
    int main()
    {
        int a = 1;
        char b = 'a';
    
        a = static_cast<int>(b);
    
        Base *b1 = new Base(10);
        Child *c1 = static_cast<Child*>(b1);
        
        c1->b = 100;
        cout << "c1->a = " << c1->a << endl;
        cout << "c1->b = " << c1->b << endl;
        c1->a = 20;
        cout << "b1->a = " << b1->a << endl;
        return 0;
    }

    输出结果

    This is Base
    c1->a = 10
    c1->b = 100
    b1->a = 20

      8.2)const_cast

        去除类对象的属性,但必须要强制转换成引用或指针

    int main()
    {
        const int a = 2;
    
    //    int b  = const_cast<int>(a);  报错
        int& c = const_cast<int&>(a);
        int *p = const_cast<int*>(&a);
    
        cout << a << endl;
        cout << c <<endl;
        cout << *p << endl;
    
        c  = 6;
        cout << endl;
        cout << a << endl;
        cout << c <<endl;
        cout << *p << endl;
    
        return 0;
    }

    输出:

    2
    2
    2
    
    2  //从常量符号表取出,因此a的值依然是2
    6
    6

      8.3)dynamic_cast 

      用于有继承关系的类指针(引用)间的转换

      用于有交叉关系的类指针(引用)间的转换

      具有类型检查的功能,编译时会去检查使用的方法是否正确,转换是否成功只有程序运行过程才知道

      父类转子类时,父类中必须有虚函数支持(换句话说必须是多态)

      

    class Base
    {
    public:
        Base()
        {
            cout <<  "This is Base"<<endl;
        }
    /*    
        virtual ~Base()
        {
            cout << "This is ~Base" << endl;
        }
    */
    
    };
    
    class Child : public Base
    {
    
    };
    
    
    int main()
    {
        Base *p = new Base;
        Child *pc = dynamic_cast<Child*>(p); //报错,原因父类没有虚函数
        cout << p << endl;
    
        delete p;
        
    
    
        return 0;
    }

      8.4)reinterpret_cast

      用于指针之间的转换

    int main()
    {
      int a = 1;
      char * b = reinterpret_cast<char*>(&a);
      char c = reinterpret_cast<int>(a); //报错    
    
      return 0;  
    }
  • 相关阅读:
    “扫一扫”模型
    CenterNet算法介绍
    PyTorch搭载的CenterNet算法环境配置
    软件评测
    代码规范制定
    寒假作业 2/2
    软件工程实践总结&个人技术博客
    React 请求拦截与接口统一和模拟解决方案
    软件评测
    结对作业二
  • 原文地址:https://www.cnblogs.com/r1chie/p/13463729.html
Copyright © 2011-2022 走看看