• 【菜鸟教程】 C++学习笔记


    本文记录在菜鸟教程上C++的学习笔记

    教程地址

    数据类型

    枚举

    创建枚举

    enum 枚举名{
    	 标识符[=整型常数],
    	 标识符[=整型常数],
    	 ...
    	 标识符[=整型常数]
    } 枚举变量
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    若枚举没有初始化,即没有 “[=整型常数]” ,则从第一个标识符开始。
    例如,下面的代码定义了一个颜色枚举,变量 c 的类型为 color。最后,c 被赋值为 “blue”。

    enum color { red, green, blue } c;
    c = blue;
    
    • 1
    • 2

    默认情况下,第一个名称的值为 0,第二个名称的值为 1,第三个名称的值为 2,以此类推。但是,您也可以给名称赋予一个特殊的值,只需要添加一个初始值即可。例如,在下面的枚举中,green 的值为 5。

    enum color { red, green=5, blue };
    
    • 1

    在这里,blue 的值为 6,因为默认情况下,每个名称都会比它前面一个名称大 1,但 red 的值依然为 0。

    变量类型

    float:占4字节,32位
    double:占8字节,64位

    在这里插入图片描述

    变量声明

    变量声明向编译器保证变量以给定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明。

    当您使用多个文件且只在其中一个文件中定义变量时(定义变量的文件在程序连接时是可用的),变量声明就显得非常有用。您可以使用 extern 关键字在任何地方声明一个变量。虽然您可以在 C++ 程序中多次声明一个变量,但变量只能在某个文件、函数或代码块中被定义一次。

    实例:

    #include 
    using namespace std;
     
    // 变量声明
    extern int a, b;
    extern int c;
    extern float f;
      
    int main ()
    {
      // 变量定义
      int a, b;
      int c;
      float f;
     
      // 实际初始化
      a = 10;
      b = 20;
      c = a + b;
     
      cout << c << endl ;
     
      f = 70.0/3.0;
      cout << f << endl ;
     
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    函数声明实例:

    // 函数声明
    int func();
     
    int main()
    {
        // 函数调用
        int i = func();
    }
     
    // 函数定义
    int func()
    {
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    上述代码中的 extern 为一个存储类,提供了一个全局变量的引用,该全局变量对所有的程序文件都是可见的。

    面向对象:extern 存储类

    当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。

    extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候,如下所示:
    在这里插入图片描述
    然后编译这两个文件,并生成可执行文件write

    $ g++ main.cpp support.cpp -o write
    
    • 1

    执行write,得到如下结果:

    Count is 5
    
    • 1

    数字

    数学运算

    在C++标准库中包含了许多内置的数学函数,引用时需要包含头文件。
    在这里插入图片描述

    数组

    字符串常量末尾的"\0"占内存,但不计入串长。
    实例:

    #include 
    #include 
     
    using namespace std;
     
    int main ()
    {
       char str1[13] = "runoob";
       char str2[13] = "google";
       char str3[13];
       int  len ;
     
       // 复制 str1 到 str3
       strcpy( str3, str1);
       cout << "strcpy( str3, str1) : " << str3 << endl;
     
       // 连接 str1 和 str2
       strcat( str1, str2);
       cout << "strcat( str1, str2): " << str1 << endl;
     
       // 连接后,str1 的总长度
       len = strlen(str1);
       cout << "strlen(str1) : " << len << endl;
     
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    输出:

    strcpy( str3, str1) : runoob
    strcat( str1, str2): runoobgoogle
    strlen(str1) : 12
    
    • 1
    • 2
    • 3

    引用 vs 指针

    引用很容易与指针混淆,它们之间有三个主要的不同:

    • 不存在空引用。引用必须连接到一块合法的内存。
    • 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
    • 引用必须在创建时被初始化。指针可以在任何时间被初始化。

    继承

    继承代表 is a 关系,例如,哺乳动物是动物,狗是哺乳动物,因此,狗是动物,等等。
    在这里插入图片描述
    派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。

    根据访问权限总结出不同的访问类型,如下所示:
    在这里插入图片描述

    继承类型

    当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。继承类型是通过上面讲解的访问修饰符 access-specifier 来指定的。
    我们几乎不使用 protected 或 private 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:

    • 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有保护成员来访问。
    • 保护继承(protected): 当一个类派生自保护基类时,基类的公有保护成员将成为派生类的保护成员。
    • 私有继承(private):当一个类派生自私有基类时,基类的公有保护成员将成为派生类的私有成员。

    重载

    C++ 允许在同一作用域中的某个函数运算符指定多个定义,分别称为函数重载运算符重载

    重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表定义实现)不相同。

    当您调用一个重载函数重载运算符时,编译器通过把您所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程,称为重载决策

    实例:

    #include 
    using namespace std;
     
    class printData
    {
       public:
          void print(int i) {
            cout << "整数为: " << i << endl;
          }
     
          void print(double  f) {
            cout << "浮点数为: " << f << endl;
          }
     
          void print(char c[]) {
            cout << "字符串为: " << c << endl;
          }
    };
     
    int main(void)
    {
       printData pd;
     
       // 输出整数
       pd.print(5);
       // 输出浮点数
       pd.print(500.263);
       // 输出字符串
       char c[] = "Hello C++";
       pd.print(c);
     
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    输出:

    整数为: 5
    浮点数为: 500.263
    字符串为: Hello C++
    
    • 1
    • 2
    • 3

    数据抽象

    数据抽象是指,只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节。

    数据抽象是一种依赖于接口和实现分离的编程(设计)技术。

    就 C++ 编程而言,C++ 类为数据抽象提供了可能。它们向外界提供了大量用于操作对象数据的公共方法,也就是说,外界实际上并不清楚类的内部实现。

    访问标签强制抽象

    在 C++ 中,我们使用访问标签来定义类的抽象接口。一个类可以包含零个或多个访问标签:

    • 使用公共标签定义的成员都可以访问该程序的所有部分。一个类型的数据抽象视图是由它的公共成员来定义的。
    • 使用私有标签定义的成员无法访问到使用类的代码。私有部分对使用类型的代码隐藏了实现细节。

    访问标签出现的频率没有限制。每个访问标签指定了紧随其后的成员定义的访问级别。指定的访问级别会一直有效,直到遇到下一个访问标签或者遇到类主体的关闭右括号为止。

    数据抽象的好处

    数据抽象有两个重要的优势:

    • 类的内部受到保护,不会因无意的用户级错误导致对象状态受损。
    • 类实现可能随着时间的推移而发生变化,以便应对不断变化的需求,或者应对那些要求不改变用户级代码的错误报告。

    如果只在类的私有部分定义数据成员,编写该类的作者就可以随意更改数据。如果实现发生改变,则只需要检查类的代码,看看这个改变会导致哪些影响。如果数据是公有的,则任何直接访问旧表示形式的数据成员的函数都可能受到影响。

    实例:

    #include 
    using namespace std;
     
    class Adder{
       public:
          // 构造函数
          Adder(int i = 0)
          {
            total = i;
          }
          // 对外的接口
          void addNum(int number)
          {
              total += number;
          }
          // 对外的接口
          int getTotal()
          {
              return total;
          };
       private:
          // 对外隐藏的数据
          int total;
    };
    int main( )
    {
       Adder a;
       
       a.addNum(10);
       a.addNum(20);
       a.addNum(30);
     
       cout << "Total " << a.getTotal() <<endl;
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    输出:

    Total 60
    
    • 1

    上面的类把数字相加,并返回总和。公有成员 addNum 和 getTotal 是对外的接口,用户需要知道它们以便使用类。私有成员 total 是用户不需要了解的,但又是类能正常工作所必需的。

    接口(抽象类)

    接口描述了类的行为和功能,而不需要完成类的特定实现。

    C++ 接口是使用抽象类来实现的,抽象类与数据抽象互不混淆,数据抽象是一个把实现细节与相关的数据分离开的概念。

    如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。纯虚函数是通过在声明中使用 “= 0” 来指定的,如下所示:、

    class Box
    {
       public:
          // 纯虚函数
          virtual double getVolume() = 0;
       private:
          double length;      // 长度
          double breadth;     // 宽度
          double height;      // 高度
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    设计抽象类(通常称为 ABC)的目的,是为了给其他类提供一个可以继承的适当的基类。抽象类不能被用于实例化对象,它只能作为接口使用。如果试图实例化一个抽象类的对象,会导致编译错误。

    因此,如果一个 ABC 的子类需要被实例化,则必须实现每个虚函数,这也意味着 C++ 支持使用 ABC 声明接口。如果没有在派生类中重写纯虚函数,就尝试实例化该类的对象,会导致编译错误。

    可用于实例化对象的类被称为具体类。(真TMD抽象😳)

    实例:

    #include 
     
    using namespace std;
     
    // 基类
    class Shape 
    {
    public:
       // 提供接口框架的纯虚函数
       virtual int getArea() = 0;
       void setWidth(int w)
       {
          width = w;
       }
       void setHeight(int h)
       {
          height = h;
       }
    protected:
       int width;
       int height;
    };
     
    // 派生类
    class Rectangle: public Shape
    {
    public:
       int getArea()
       { 
          return (width * height); 
       }
    };
    class Triangle: public Shape
    {
    public:
       int getArea()
       { 
          return (width * height)/2; 
       }
    };
     
    int main(void)
    {
       Rectangle Rect;
       Triangle  Tri;
     
       Rect.setWidth(5);
       Rect.setHeight(7);
       // 输出对象的面积
       cout << "Total Rectangle area: " << Rect.getArea() << endl;
     
       Tri.setWidth(5);
       Tri.setHeight(7);
       // 输出对象的面积
       cout << "Total Triangle area: " << Tri.getArea() << endl; 
     
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    输出:

    Total Rectangle area: 35
    Total Triangle area: 17
    
    • 1
    • 2

    设计策略

    面向对象的系统可能会使用一个抽象基类为所有的外部应用程序提供一个适当的、通用的、标准化的接口。然后,派生类通过继承抽象基类,就把所有类似的操作都继承下来。

    外部应用程序提供的功能(即公有函数)在抽象基类中是以纯虚函数的形式存在的。这些纯虚函数在相应的派生类中被实现。

    这个架构也使得新的应用程序可以很容易地被添加到系统中,即使是在系统被定义之后依然可以如此。

    异常处理

    异常提供了一种转移程序控制权的方式。C++ 异常处理涉及到三个关键字:try、catch、throw

    • throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
    • catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
    • try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。

    如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。try 块中放置可能抛出异常的代码,try 块中的代码被称为保护代码。使用 try/catch 语句的语法如下所示:

    try
    {
       // 保护代码
    }catch( ExceptionName e1 )
    {
       // catch 块
    }catch( ExceptionName e2 )
    {
       // catch 块
    }catch( ExceptionName eN )
    {
       // catch 块
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    如果 try 块在不同的情境下会抛出不同的异常,这个时候可以尝试罗列多个 catch 语句,用于捕获不同类型的异常。

    抛出异常

    使用 throw 语句在代码块中的任何地方抛出异常。throw 语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常的类型。

    以下是尝试除以零时抛出异常的实例:

    double division(int a, int b)
    {
       if( b == 0 )
       {
          throw "Division by zero condition!";
       }
       return (a/b);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    捕获异常

    catch 块跟在 try 块后面,用于捕获异常。您可以指定想要捕捉的异常类型,这是由 catch 关键字后的括号内的异常声明决定的。

    try
    {
       // 保护代码
    }catch( ExceptionName e )
    {
      // 处理 ExceptionName 异常的代码
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上面的代码会捕获一个类型为 ExceptionName 的异常。如果您想让 catch 块能够处理 try 块抛出的任何类型的异常,则必须在异常声明的括号内使用省略号 …,如下所示:

    try
    {
       // 保护代码
    }catch(...)
    {
      // 能处理任何异常的代码
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    实例:

    #include 
    using namespace std;
     
    double division(int a, int b)
    {
       if( b == 0 )
       {
          throw "Division by zero condition!";
       }
       return (a/b);
    }
     
    int main ()
    {
       int x = 50;
       int y = 0;
       double z = 0;
     
       try {
         z = division(x, y);
         cout << z << endl;
       }catch (const char* msg) {
         cerr << msg << endl;
       }
     
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    由于我们抛出了一个类型为 const char* 的异常,因此,当捕获该异常时,我们必须在 catch 块中使用 const char*。当上面的代码被编译和执行时,它会产生下列结果:

    Division by zero condition!
    
    • 1

    标准异常

    在这里插入图片描述

    文件和流

    用fstream即可。

    在这里插入图片描述

    预处理器

    预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理。

    所有的预处理器指令都是以井号(#)开头,只有空格字符可以出现在预处理指令之前。预处理指令不是 C++ 语句,所以它们不会以分号(;)结尾。

    条件编译

    举例如下:
    1.定义了某个宏,如NULL

    #ifdef NULL
       #define NULL 0
    #endif
    
    • 1
    • 2
    • 3

    2.只在调试阶段编译

    #ifdef DEBUG
       cerr <<"Variable x = " << x << endl;
    #endif
    
    • 1
    • 2
    • 3

    注释掉程序一部分

    #if 0
       不进行编译的代码
    #endif
    
    • 1
    • 2
    • 3

    实例:

    #include 
    using namespace std;
    #define DEBUG
     
    #define MIN(a,b) (((a)<(b)) ? a : b)
     
    int main ()
    {
       int i, j;
       i = 100;
       j = 30;
    #ifdef DEBUG
       cerr <<"Trace: Inside main function" << endl;
    #endif
     
    #if 0
       /* 这是注释部分 */
       cout << MKSTR(HELLO C++) << endl;
    #endif
     
       cout <<"The minimum is " << MIN(i, j) << endl;
     
    #ifdef DEBUG
       cerr <<"Trace: Coming out of main function" << endl;
    #endif
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    输出:

    Trace: Inside main function
    The minimum is 30
    Trace: Coming out of main function
    
    • 1
    • 2
    • 3

    STL

    C++ STL(标准模板库)是一套功能强大的 C++ 模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈。
    在这里插入图片描述

    实例:

    #include 
    #include 
    using namespace std;
     
    int main()
    {
       // 创建一个向量存储 int
       vector<int> vec; 
       int i;
     
       // 显示 vec 的原始大小
       cout << "vector size = " << vec.size() << endl;
     
       // 推入 5 个值到向量中
       for(i = 0; i < 5; i++){
          vec.push_back(i);
       }
     
       // 显示 vec 扩展后的大小
       cout << "extended vector size = " << vec.size() << endl;
     
       // 访问向量中的 5 个值
       for(i = 0; i < 5; i++){
          cout << "value of vec [" << i << "] = " << vec[i] << endl;
       }
     
       // 使用迭代器 iterator 访问值
       vector<int>::iterator v = vec.begin();
       while( v != vec.end()) {
          cout << "value of v = " << *v << endl;
          v++;
       }
     
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    输出:

    vector size = 0
    extended vector size = 5
    value of vec [0] = 0
    value of vec [1] = 1
    value of vec [2] = 2
    value of vec [3] = 3
    value of vec [4] = 4
    value of v = 0
    value of v = 1
    value of v = 2
    value of v = 3
    value of v = 4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    标准库

    C++ 标准库可以分为两部分:

    • 标准函数库: 这个库是由通用的、独立的、不属于任何类的函数组成的。函数库继承自 C 语言。
    • 面向对象类库: 这个库是类及其相关函数的集合。

    C++ 标准库包含了所有的 C 标准库,为了支持类型安全,做了一定的添加和修改。

    标准函数库

    • 输入/输出 I/O
    • 字符串和字符处理
    • 数学
    • 时间、日期和本地化
    • 动态分配
    • 其他
    • 宽字符函数

    面向对象类库

    标准的 C++ 面向对象类库定义了大量支持一些常见操作的类,比如输入/输出 I/O、字符串处理、数值处理。面向对象类库包含以下内容:

    • 标准的 C++ I/O 类
    • String 类
    • 数值类
    • STL 容器类
    • STL 算法
    • STL 函数对象
    • STL 迭代器
    • STL 分配器
    • 本地化库
    • 异常处理类
    • 杂项支持库
  • 相关阅读:
    redis 中的八大问题
    6.0、C语言数据结构——链式存储结构 (1)
    vue cli 打包、生产环境http-proxy-middleware代理
    去除照片中多余人物方法分享-这些方法快收藏起来
    Matlab图形绘制
    虚幻4学习笔记(11) 蓝图实现AI移动、AI树实现移动、看见后寻找玩家
    cookiecutter工具
    【深度学习相关知识】
    冰冰学习笔记:泛型编程---模板简介
    【JavaScript 】DOM操作快速入门
  • 原文地址:https://blog.csdn.net/no_pain_no_gain_/article/details/125945964