目录

  • 一、什么是C++
  • 二、C++关键字
  • 三、命名空间
    • 3.1命名空间的定义
    • 3.2命名空间的使用
  • 四、C++输入和输出
  • 五、缺省参数
    • 5.1缺省参数的概念
    • 5.2缺省参数的分类
  • 六、函数重载
    • 6.1函数重载的概念
    • 6.2函数重载的原理—-名字修饰
  • 七、引用
    • 7.1引用概念
    • 7.2引用特性
    • 7.3常引用
    • 7.4引用的使用
    • 7.5传值、传引用效率比较
    • 7.6引用和指针的区别
    • 7.7引用与指针的不同点
  • 八、内联函数
    • 8.1内联函数概念
    • 8.2 特性
  • 九、auto关键字
    • 9.1类型别名思考
    • 9.2 auto简介
    • 9.3 auto的使用细则
    • 9.4 auto不能推导的场景
  • 十、C++范围的for循环(C++11)
    • 10.1范围for的语法
    • 10.2范围for的使用条件
  • 十一、nullptr指针空值(C++11)

一、什么是C++

二、C++关键字

相较于C语言,C语言有32个关键字,而C++则有63个关键字

三、命名空间

在C/C++中,变量、函数等的类都是大量存在的,这些变量、函数和类的名称将都存
在于全局作用域中,可能会导致很多冲突。
使用命名空间的目的是对标识符的名称进行本地化,
以避免命名冲突或名字污染,**namespace**关键字的出现就是针对这种问题的

3.1命名空间的定义

命名空间的普通定义

//命名空间的普通定义namespace bit{ int rand = 10; int scanf = 2; int Add(int x, int y) { return x + y; }}

命名空间的嵌套定义

//命名空间的嵌套定义、namespace bit1{ int rand = 10; int scanf = 2; int Add(int x, int y) { return x + y; } namespace bit2 { int c; int d; int Sub(int left, int right) { return left + right; } }}

同一个命名空间的合并
同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中

3.2命名空间的使用

作用域:

符号**“::”**在C++中叫做作用域限定符,我们通过“命名空间名称::命名空间成员”便可以访问到命名空间中相应的成员

加命名空间名称及作用域限定符,嵌套调用可以用"xxx::bbb::"

std::cin >> i;cout << "xxxx" << endl;cout << bit::rand << endl;

使用using将命名空间中某个成员引入

using std::cout;using std::endl;

使用using namespace 命名空间名称 引入

using namespace bit;int main(){int i = 0;std::cin >> i;cout << "xxxx" << endl;cout << bit::rand << endl;Add();}

四、C++输入和输出

std是C++标准库的命名空间名,C++将标准库的定义实现都放在这个命名空间中

使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含头文件
以及按命名空间使用方法使用std。

输出流:cout和endl,其中endl表示换行输出.他们都包含在的头文件中 输入流:cin
<>是流提取运算符

在C++的输入输出可以自动识别变量类型

五、缺省参数

5.1缺省参数的概念

缺省参数是声明或定义函数时为函数的一个参数指定一个缺省值,在调用函数的时候,如果没有传参(也就是没有指定的实参)则采用该形参的缺省值

5.2缺省参数的分类

全缺省参数

void myq(int x=5,int y=2,int z=5){cout << "x=" << x << endl;cout << "y=" << y << endl;cout << "z=" << z << endl; }

半缺省参数

void fun(int x, int y = 10){cout << x << y << endl;}
  1. 半缺省参数必须从右往左依次来给出,不能间隔着给
  2. 缺省参数不能在函数声明和定义中同时出现

六、函数重载

同一个函数名可以定义为多个不同类型的函数

6.1函数重载的概念

函数重载是指在同一个作用域内,可以定义多个具有相同名称但参数列表不同的函数。C++通过函数的参数个数、类型或顺序的不同来实现函数重载。

通过使用相同的函数名来实现不同的功能和操作

//1.参数类型不同int max(int x, int y){if (x > y)return x;elsereturn y;}double max(double x, double y){if (x - y > 0)return x;else return y;}//2.参数个数不同void max(){cout << "max()" << endl;}//3.参数类型的顺序void min(int x, char y){cout << x << y << endl;}void min(char x, int y){cout << x << y << endl;}int main(){max(1, 4);max(1.2, 4.2);max();min(6, 'm');min('m', 5);return 0;}

6.2函数重载的原理—-名字修饰

C/C++中,一个程序在运行中都需要经历这四个阶段:预处理、编译、汇编、链接

  1. 编译后链接前,a.o的目标 文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。那么 怎么办呢” />七、引用

    7.1引用概念

    引用不是新定义一个变量,而是给已有的变量去一个别名,本质上就是同一个变量的不同叫法。
    因此编译器不会为引用变量开辟内存空间,他和它引用的变量共用同一块内存空间

    类型& 引用变量名(对象名) = 引用实体;

    注意:引用类型必须和引用实体为同一种类型

    7.2引用特性

    1. 引用在定义是必须初始化
    2. 一个变量可以有多个引用
    3. 引用一旦引用一个实体,再不能引用其他实体

    7.3常引用

    int main(){const int a = 10;const int& ma = a;//int& fa = a;//编译出错double b = 3.14;double & fa = b;const int& c = 10;double d = 12.34;//int& rd = d; // 该语句编译时会出错,类型不同const int& rd = d;cout << b << endl << c << endl;return 0;}

    7.4引用的使用

    参数引用

    void Swap(int& x, int& y){int tmp = x;x = y;y = tmp;}

    返回值引用

    int& count(){static int x = 0;x++;return x;}

    引用函数返回值的作用
    避免不必要的对象拷贝开销,提高程序的性能;支持连续赋值和链式调用,使代码更加简洁和易读;同时允许函数返回修改后的对象,增加了程序的灵活性。因此,在设计函数接口时,如果函数返回的是一个非基本数据类型的对象,考虑返回值引用通常是一个不错的选择。

    7.5传值、传引用效率比较

    以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。

    struct A { int a[10000]; };void TestFunc1(A a) {}void TestFunc2(A& a) {}void TestRefAndValue(){A a;// 以值作为函数参数size_t begin1 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc1(a);size_t end1 = clock();// 以引用作为函数参数size_t begin2 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc2(a);size_t end2 = clock();// 分别计算两个函数运行结束后的时间cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;}


    传值和指针在作为传参以及返回值类型上效率相差很大

    7.6引用和指针的区别

    语法的概念上,引用就是一个别名,没有独立空间,同引用实体共用一块空间
    而在编译器实现的底层上,实际是有空间的,因为引用也是按照指针方式来实现的,只是较于c来说,把事情的重复性交给了底层,来提高效率

    7.7引用与指针的不同点

    1. 引用概念上定义一个变量的别名,指针存储一个**变量地址**。
    2. **引用在定义时必须初始化,而指针没有要求**
    3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
    4. 没有NULL引用,但有NULL指针
    5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32
    位平台下占4个字节)
    6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
    7. 有多级指针,但是没有多级引用
    8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
    9. 引用比指针使用起来相对更安全

    八、内联函数

    8.1内联函数概念

    以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率

    这里便涉及到汇编的一些知识

    内联函数实质是,将整个函数(也就是这一堆的指令) ,直接展开使用
    比较 宏: 宏虽然也能直接展开函,但宏的本质是将所写的指令从一个名字中替换

    8.2 特性

    1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会
      用函数体替换函数调用.
      缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
    2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。
    3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址
      了,链接就会找不到

    相较于的优缺点:
    优:

    1. 可以增强代码复用性
    2. 提高性能
      缺:
    3. 不方便调试
    4. 可读性差
    5. 没有类型的检查

    C++有哪些技术替代宏?

    1. 常量定义 换用const enum
    2. 短小函数定义 换用内联函数

    九、auto关键字

    9.1类型别名思考

    当程序过于复杂,类型过于繁多复杂,如

    1. 类型难以拼写
    2. 含义不明确
    #include #include int main(){ std::map<std::string, std::string> m{ { "apple", "苹果" }, { "orange", "橙子" },{"pear","梨"} }; std::map<std::string, std::string>::iterator it = m.begin(); while (it != m.end()) { //.... } return 0;}

    std::map::iterator 是一个类型,但是该类型太长了,特别容易写错。

    使用typedef给类型取别名确实可以简化代码,但是typedef有会遇到新的难题:

    typedef char* pstring;int main(){ const pstring p1;// 编译成功还是失败? const pstring* p2; // 编译成功还是失败? return 0;}

    在编程时,常常需要把表达式的值赋值给变量,这就要求在声明变量的时候清楚地知道表达式的
    类型。然而有时候要做到这点并非那么容易,因此C++11给auto赋予了新的含义。

    9.2 auto简介

    使用auto修饰的变量,是具有自动存储器的局部变量
    但实际应用中并不多见,这是为什么” />9.3 auto的使用细则

    1. auto与指针和引用结合起来使用
      用auto声明指针类型时,用auto和auto* 没有区别,但用auto声明引用类型则必须加 &
    2. 在同一行定义多个变量 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译 器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

    9.4 auto不能推导的场景

    1. auto不能作为函数的参数
    // 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导void TestAuto(auto a){}
    1. 不能直接用来声明数组

    十、C++范围的for循环(C++11)

    10.1范围for的语法

    C++是兼容C的,所以for循环中,C的语法依然是适用的

    int main(){for(int i=0;i<10;i++){printf("%d ",i);}return 0;}

    对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因
    此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范
    围内用于迭代的变量
    ,第二部分则表示被迭代的范围

    for(迭代变量 : 别迭代范围){}

    int main(){int array1[] = { 1,2,3,4,5,6 };int array2[] = { 1,2,3,4,5,6 };//仅仅只是变量的拷贝,不做修改for (int i : array1){i *= 2;}for (auto e : array1){cout << e << " ";}cout << endl;//for范围配合引用,可以做到指针的效果for (auto& i : array2){i *= 2;}for (auto i : array2){cout << i << " ";}return 0;}

    10.2范围for的使用条件

    for循环迭代的范围必须是确定的
    对于数组而言**,就是数组中第一个元素和最后一个元素的范围**;对于类而言,应该提供begin和end的办法,begin和end就是for循环迭代的范围

    十一、nullptr指针空值(C++11)

    声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下方式对其进行初始化:

    int* N = NULL;int* M = 0;

    在C的头文件中,(stddef.h)
    NULL实际是一个宏 ,所以在应用中,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦

    void fun(int){cout << "fun(int)" << endl;}void fun(int*){cout << "fun(int*)" << endl;}int main(){fun(0);fun(NULL);fun((int*)NULL);return 0;}


    本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的
    初衷相悖.
    字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0。

    注意:

    1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入 的。
    2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
    3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr