• C++ 中常用关键字及其用法


    0.1 C++与C的对比

    1. C++有三种编程方式:过程性,面向对象,泛型编程。
    2. C++函数符号由 函数名+参数类型 组成,C只有函数名。所以,C没有函数重载的概念。
    3. C++ 在 C的基础上增加了封装、继承、多态的概念
    4. C++增加了泛型编程
    5. C++增加了异常处理,C没有异常处理
    6. C++增加了bool型
    7. C++允许无名的函数形参(如果这个形参没有被用到的话)
    8. C允许main函数调用自己
    9. C++支持默认参数,C不支持
    10. C语言中,局部变量必须在函数开头定义,不允许类似for(int a = 0; ;;)这种定义方法。
    11. C++增加了引用
    12. C允许变长数组,C++不允许
    13. C中函数原型可选,C++中在调用之前必须声明函数原型
    14. C++增加了STL标准模板库来支持数据结构和算法

     一、常用的关键字极其用法

    1.1 const 

    主要用法

    C++ 的const关键字的作用有很多,几乎无处不在,面试中往往会问“说一说const有哪些用法”。下面是一些常见的const用法的总结:

     

    除此以外,const的用法还有:

    • const引用可以引用右值,如const int& a = 1; 

    注:

    1. const 成员方法本质上是使得this指针是指向const对象的指针,所以在const方法内,
    2. const 成员函数可以被非const和const对象调用,而const对象只能调用const 成员函数。原因得从C++底层找,C++方法调用时,会传一个隐形的this参数(本质上是对象的地址,形参名为this)进去,所有成员方法的第一个参数是this隐形指针。const成员函数的this指针是指向const对象的const指针,当非const对象调用const方法时,实参this指针的类型是非const对象的const指针,赋给const对象的const指针没有问题;但是如果const对象调用非const方法,此时实参this指针是指向const对象的const指针,无法赋给非const对象的const指针,所以无法调用。注意this实参是放在ecx寄存器中,而不是压入栈中,这是this的特殊之处。在类的非成员函数中如果要用到类的成员变量,就可以通过访问ecx寄存器来得到指向对象的this指针,然后再通过this指针加上成员变量的偏移量来找到相应的成员变量。文章来源http://blog.csdn.net/starlee/article/details/2062586/
    3. const 指针、指向const的指针和指向const的const指针,涉及到const的特性“const左效、最左右效”
    4. const 全局变量有内部链接性,即不同的文件可以定义不同的同名const全局变量,使用extern定义可以消除内部链接性,称为类似全局变量,如extern const int a = 10.另一个文件使用extern const int a; 来引用。而且编译器会在编译时,将const变量替换为它的值,类似define那样。

    const 常量和define 的区别

    1. const常量有数据类型,而宏定义没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换中可能会产生意想不到的错误(边际效应)。
    2. 有些集成化的调试工具可以对const常量进行调试,但是不能对宏定义进行调试。
    3. 在C++程序中只使用const常量而不使用宏常量,即const常量完全取代宏常量。
    4. 内存空间的分配上。define进行宏定义的时候,不会分配内存空间,编译时会在main函数里进行替换,只是单纯的替换,不会进行任何检查,比如类型,语句结构等,即宏定义常量只是纯粹的置放关系,如#define null 0;编译器在遇到null时总是用0代替null它没有数据类型.而const定义的常量具有数据类型,定义数据类型的常量便于编译器进行数据检查,使程序可能出现错误进行排查,所以const与define之间的区别在于const定义常量排除了程序之间的不安全性.
    5. const常量存在于程序的数据段,#define常量存在于程序的代码段
    6. const常量存在“常量折叠”,在编译器进行语法分析的时候,将常量表达式计算求值,并用求得的值来替换表达式,放入常量表,可以算作一种编译优化。因为编译器在优化的过程中,会把碰见的const全部以内容替换掉,类似宏。

    1.2 sizeof

    1. sizeof关键字不会计算表达式的值,而只会根据类型推断大小。
    2. sizeof() 的括号可以省略, 如 sizeof a ; 
    3. 类A的大小是 所有非静态成员变量大小之和+虚函数指针大小

    1.3 static 

    static的用法有:

    (1)声明静态全局变量,如static int a; 静态全局变量的特点:

    • 该变量在全局数据区分配内存; 
    • 未经初始化的静态全局变量会被程序自动初始化为0(自动变量的值是随机的,除非它被显式初始化); 
    • 静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的; 

    (2)声明静态局部变量,即在函数内部声明的,静态局部变量的特点:

    • 该变量在全局数据区分配内存; 
    • 静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化; 
    • 静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0; 
    • 它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;

    (3)声明静态函数,限定函数的局部访问性,仅在文件内部可见

    (4)类的静态数据成员,与全局变量相比,静态数据成员的好处有:

    • 静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性; 
    • 可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能;

    (5)类的静态方法

    1.4 typedef 

    typedef 用来定义新的类型,类似的还有#define 和 using (C++11) (应该尽可能用using ,比如 using AAA = int64_t; )

    与宏定义的对比

    1. #define 在预处理阶段进行简单替换,不做类型检查; typedef在编译阶段处理,在作用域内给类型一个别名。
    2. typedef 是一个语句,结尾有分号;#define是一个宏指令,结尾没有分号
    3. typedef int* pInt; 和 #define pInt int* 不等价,前者定义 pInt a, b;会定义两个指针,后者是一个指针,一个int。

    1.5 inline

    inline用来向编译器请求声明为内联函数,编译器有权拒绝。

    与宏函数的对比

    1. 内联函数在运行时可调试,而宏定义不可以;
    2. 编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会;
    3. 内联函数可以访问类的成员变量,宏定义则不能;
    4. 在类中声明同时定义的成员函数,自动转化为内联函数
    5. 宏只是预定义的函数,在编译阶段不进行类型安全性检查,在编译的时候将对应函数用宏命令替换。对程序性能无影响

    不能声明为inline的函数

    1. 包含了递归、循环等结构的函数一般不会被内联。
    2. 虚拟函数一般不会内联,但是如果编译器能在编译时确定具体的调用函数,那么仍然会就地展开该函数。
    3. 如果通过函数指针调用内联函数,那么该函数将不会内联而是通过call进行调用。
    4. 构造和析构函数一般会生成大量代码,因此一般也不适合内联。
    5. 如果内联函数调用了其他函数也不会被内联。

    1.6 static const const static 

    1. static const 
    static const 数据成员可以在类内初始化 也可以在类外,不能在构造函数中初始化,也不能在构造函数的初始化列表中初始化
    2. static
    static数据成员只能在类外,即类的实现文件中初始化,也不能在构造函数中初始化,不能在构造函数的初始化列表中初始化;
    3. const
    const数据成员只能在构造函数的初始化列表中初始化;

    1.7 explicit 

    explicit禁止了隐式转换类型,用来修饰构造函数。原则上应该在所有的构造函数前加explicit关键字,当你有心利用隐式转换的时候再去解除explicit,这样可以大大减少错误的发生。如果一个构造函数 Foo(int) ;则下面的语句是合法的:

    Foo f; 

    f = 12; // 发生了隐式转换,先调用Foo(int)用12构建了一个临时对象,然后调用赋值运算符复制到 f 中

    如果给构造函数加了explicit,即 explicit Foo(int);就只能进行显示转换,无法进行隐式转换了:

    f = 12; // 非法,隐式转换

    f = Foo(12); // 合法,显示转换

    f = (Foo)12;//合法,显示转换,C风格

    1.8 extern 

    extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定

    typedef 数据类型 数据类型的别名

    typedef的使用非常简单,在typedef关键字之后,分别跟上数据类型和相应的绰号就定义了这个数据类型的别名,在接下来的程序中就可以使用这个别名来指代这个数据类型了。

    在武林中,是给大名鼎鼎的人物取外号,为的是唬人;而在C++世界中,是为那些比较复杂难以书写的数据类型取外号,为的是偷懒。例如,觉得unsigned char无符号字符类型的书写比较烦琐时,可以使用typedef为它定义一个简单易记的别名,然后使用这个别名作为数据类型来定义无符号字符类型的变量:

    // 为无符号字符类型unsigned char定义一个别名uchar
    
    typedef unsigned char uchar;
    

    有了这个简单的别名,就可以用它来指代无符号字符类型,用作数据类型定义变量:

    // 定义一个uchar类型的变量,实际上就是unsigned char类型的变量
    
    uchar a;

    使用别名之后的程序代码,是不是书写起来更加简单,同时也更加简洁易懂呢?所以,在以后遇到类似情况的时候,使用typedef为复杂类型取一个简单的别名,不仅自己写起来很方便,别人读起来也很轻松。这样两全其美的事情,何乐而不为呢?

    这里大家可能有个疑问,利用前面学习的宏,将复杂类型定义成一个宏,不也同样可以达到化繁为简的目的吗?它们两个有什么区别呢?typedef是为复杂数据类型定义一个别名,而不只是像宏一样简单的替换。这一点在同时定义指针类型的多个变量时特别有用。例如,想定义两个int*指针类型的变量,自然地,使用宏我们可能会这样写:

    // 定义指针类型的宏
    #define PINT int*
    // 使用宏定义两个变量
    PINT pInt1,pInt2;
    

    然而,这段看起来再正确不过的代码,实际的效果却与我们的预想大相径庭。经过宏替换后,上面定义指针变量的代码变为:

    // 宏替换后的实际代码
    
    int* pInt1, pInt2;
    

    这不是在定义两个指针变量,而是在定义一个int指针类型变量pInt1和另一个int类型变量pInt2。想使用宏在同一行内方便地定义多个指针变量是行不通的,解决问题的办法就是用typedef为指针类型定义一个别名,然后使用这个别名作为数据类型,就可以在一行内定义多个指针类型的变量了:

    // 为指针类型int*定义一个别名PINT
    
    typedef int* PINT;
    
    // 同时定义多个指针类型变量
    
    PINT pInt1, pInt2;
    

    这时,PINT已经成为一种新的数据类型,自然它可以在同一行内同时定义多个PINT类型的变量,而这种新类型本质上还是int指针类型,也就相当于同时定义了多个int指针类型的变量。

    typedef的另外一个重要用途是为复杂的类型定义简单的别名。请看下面这行代码:

    int* (*pFunc)(int, char*);

    实际上,这行代码所定义的是一个函数指针pFunc,它所能够指向的函数的返回值类型是int*,两个参数分别是int类型和char*类型。如果只是定义一个这种类型的函数指针,那还可以勉强接受;如果要定义多个,那么多次书写这么复杂的既难写又难懂的语句,恐怕只有“撞墙”了。好在使用typedef可以为这种复杂的类型定义一个简单的别名,使用这个别名就可以轻松定义多个这种类型的变量。

    // 定义函数指针类型为PFUNC
    
    typedef int* (*PFUNC)(int, char*);
    
     
    // 使用PFUNC定义多个函数指针变量
    
    PFUNC pFunc1, pFunc2;
  • 相关阅读:
    C++ 多线程 (4) 互斥量(mutex)与锁(lock)
    C++ 多线程(3)std::thread 详解
    c++ 多线程(2)创建线程对象的方法
    CMake解决c++11的phread库问题:undefined reference to `pthread_create’
    生成对抗网络--Generative Adversarial Networks (GAN)
    语义分割(semantic segmentation)——U-Net
    目标检测SSD: Single Shot MultiBox Detector
    基于内容的图像检索(CBIR) ——以图搜图
    去噪自动编码器
    利用Chrome开发者工具功能进行网页整页截图的方法
  • 原文地址:https://www.cnblogs.com/klb561/p/13844804.html
Copyright © 2020-2023  润新知