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;  
}

 

posted @ 2020-08-14 14:41  R1chie  阅读(171)  评论(0编辑  收藏  举报