C++基本语法

发布于:2025-04-11 ⋅ 阅读:(34) ⋅ 点赞:(0)

常量

  • 整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。
  • 浮点常量由整数部分、小数点、小数部分和指数部分组成,如下所示。
    3.14159       // 合法的 
    314159E-5L    // 合法的 
    510E          // 非法的:不完整的指数
    210f          // 非法的:没有小数或指数
    .e55          // 非法的:缺少整数或分数
    
  • 布尔常量共有两个,我们不应把 true 的值看成 1,把 false 的值看成 0。
  • 请注意,把常量定义为大写字母形式,是一个很好的编程实践。

变量作用域

  • 局部作用域、全局作用域、块作用域、类作用域
  • 局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值,如下所示输出为10。
    #include <iostream>
    using namespace std;
    // 全局变量声明
    int g = 20;
    int main ()
    {
    // 局部变量声明
    int g = 10;
    cout << g;
    return 0;
    }
    

数据类型

类型转换

  • 类型转换是将一个数据类型的值转换为另一种数据类型的值。C++ 中有四种类型转换:静态转换、动态转换、常量转换和重新解释转换。
  • 静态转换是将一种数据类型的值强制转换为另一种数据类型的值。静态转换通常用于比较类型相似的对象之间的转换,例如将 int 类型转换为 float 类型。
    int i = 10;
    float f = static_cast<float>(i); // 静态将int类型转换为float类型
    
  • 动态转换(dynamic_cast)是 C++ 中用于在继承层次结构中进行向下转换(downcasting)的一种机制。动态转换通常用于将一个基类指针或引用转换为派生类指针或引用。dynamic_cast<目标类型>(表达式)
    指针类型动态转换:
    #include <iostream>
    class Base {
    public:
        virtual ~Base() = default; // 基类必须具有虚函数
    };
    class Derived : public Base {
    public:
        void show() {
            std::cout << "Derived class method" << std::endl;
        }
    };
    int main() {
        Base* ptr_base = new Derived; // 基类指针指向派生类对象
        // 将基类指针转换为派生类指针
        Derived* ptr_derived = dynamic_cast<Derived*>(ptr_base);
        if (ptr_derived) {
            ptr_derived->show(); // 成功转换,调用派生类方法
        } else {
            std::cout << "Dynamic cast failed!" << std::endl;
        }
        delete ptr_base;
        return 0;
    }
    
    引用类型动态转换
    #include <iostream>
    #include <typeinfo>
    
    class Base {
    public:
        virtual ~Base() = default; // 基类必须具有虚函数
    };
    class Derived : public Base {
    public:
        void show() {
            std::cout << "Derived class method" << std::endl;
        }
    };
    int main() {
        Derived derived_obj;
        Base& ref_base = derived_obj; // 基类引用绑定到派生类对象
        try {
            // 将基类引用转换为派生类引用
            Derived& ref_derived = dynamic_cast<Derived&>(ref_base);
            ref_derived.show(); // 成功转换,调用派生类方法
        } catch (const std::bad_cast& e) {
            std::cout << "Dynamic cast failed: " << e.what() << std::endl;
        }
        return 0;
    }
    
  • 常量转换用于将 const 类型的对象转换为非 const 类型的对象。常量转换只能用于转换掉 const 属性,不能改变对象的类型。
    const int i = 10;
    int& r = const_cast<int&>(i); // 常量转换,将const int转换为int
    
  • 重新解释转换将一个数据类型的值重新解释为另一个数据类型的值,通常用于在不同的数据类型之间进行转换。
    int i = 10;
    float f = reinterpret_cast<float&>(i); // 重新解释将int类型转换为float类型
    

修饰符类型

  • C++ 允许在 char、int 和 double 数据类型前放置修饰符,其他类型不能放修饰符。
  • signed、unsigned、long 和 short 可应用于整型,signed 和 unsigned 可应用于字符型,long 可应用于双精度型。
  • 类型限定符如下 :register用于建议编译器将变量存储在CPU寄存器中以提高访问速度。在 C++11 及以后的版本中,register 已经是一个废弃的特性,不再具有实际作用。在 C++17及以后的版本中,auto 已经是一个废弃的特性,不再具有实际作用。
    在这里插入图片描述

存储类

  • 存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。下面列出 C++ 程序中可用的存储类:
    在这里插入图片描述

循环

  • 基于范围的for循环
    int my_array[5] = {1, 2, 3, 4, 5};
    // 每个数组元素乘于 2
    for (int &x : my_array)
    {
        x *= 2;
        cout << x << endl;  
    }
    // auto 类型也是 C++11 新标准中的,用来自动获取变量的类型
    for (auto &x : my_array) {
        x *= 2;
        cout << x << endl;  
    }
    //示例3
    int main()  
    {  
        string str("some string");  
        // range for 语句  
        for(auto &c : str)  
        {  
            c = toupper(c);  
        }  
        cout << str << endl;  
        return 0;  
    }
    

函数参数

在这里插入图片描述

  • 传值调用就是最普通的调用形式,不做讲解。
  • 指针调用,向函数传递参数的指针调用方法,把参数的地址复制给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。
    // 函数定义
    void swap(int *x, int *y)
    {
       int temp;
       temp = *x;    /* 保存地址 x 的值 */
       *x = *y;        /* 把 y 赋值给 x */
       *y = temp;    /* 把 x 赋值给 y */
      
       return;
    }
    
  • 向函数传递参数的引用调用方法,把引用的地址复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。
    1、引用定义:通过使用&符号来定义引用类型,例如int &ref。
    2、传递引用:在函数参数列表中使用引用类型,这样函数内部的修改会影响到实际传递的变量。
    3、避免拷贝开销:引用传递避免了传递大对象时的拷贝开销,提高了效率。
    // 函数定义
    void swap(int &x, int &y)
    {
       int temp;
       temp = x; /* 保存地址 x 的值 */
       x = y;    /* 把 y 赋值给 x */
       y = temp; /* 把 x 赋值给 y  */
       return;
    }
    

Lambda函数

暂未深入了解

数组

  • C++ 传数组给一个函数,数组类型自动转换为指针类型,因而传的实际是地址。
  • C++ 不允许返回一个完整的数组作为函数的参数。但是可以通过指定不带索引的数组名来返回一个指向数组的指针。不能简单地返回指向局部数组的指针,因为当函数结束时,局部数组将被销毁,指向它的指针将变得无效,C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。为了避免以上情况,可以使用静态数组或者动态分配数组。

指针

  • NULL指针:在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。
  • 指针的算术运算:指针算术运算会根据指针的类型和大小来决定移动的距离。由于是一个 32 位整数指针,每个整数占据 4 个字节,因此 ptr++ 会将指针 ptr 向前移动 4 个字节,指向下一个整型元素的地址。
  • 指针的比较操作可以用于确定两个指针是否指向相同的位置、一个指针是否指向的位置在另一个指针之前或之后等。指针的比较主要包括以下几种:相等性比较 (== 和 !=),关系比较 (<, <=, >, >=)。
  • 指针数组,如下所示,把 ptr 声明为一个数组,由 MAX 个整数指针组成。因此,ptr 中的每个元素,都是一个指向 int 值的指针。
    int *ptr[MAX];//
    cout << *ptr[i] << endl;
    const char *names[MAX] = {
                   "Zara Ali",
                   "Hina Ali",
                   "Nuha Ali",
                   "Sara Ali",
    };
    cout << names[i] << endl;
    
    上述两种表达引发了一个问题:为啥char* 是字符串,int* 是指针,int *需要解引用,而char *不需要?
    原因:C++ 的 iostream(特别是 cout)对 char* 类型有专门的输出规则,而 int* 和其他指针类型没有这种特殊待遇。int* 只是一个普通指针,cout 不知道它指向的是单个 int 还是数组。char* 在 C/C++ 中默认被视为 C 风格字符串的起始地址。
  • 指向指针的指针:一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。
  • 传递指针给函数:能接受指针作为参数,也能接受数组作为参数。
    #include <iostream>
    #include <ctime>
    using namespace std;
    // 在写函数时应习惯性的先声明函数,然后在定义函数
    void getSeconds(unsigned long *par);
    int main ()
    {
       unsigned long sec;
       getSeconds( &sec );
       // 输出实际值
       cout << "Number of seconds :" << sec << endl;
       return 0;
    }
    void getSeconds(unsigned long *par)
    {
       // 获取当前的秒数
       *par = time( NULL );
       return;
    }
    
  • 从函数返回指针
    int * getRandom( )
    {
      static int  r[10];
      // 设置种子
      srand( (unsigned)time( NULL ) );
      for (int i = 0; i < 10; ++i)
      {
        r[i] = rand();
        cout << r[i] << endl;
      }
      return r;
    }
    

引用

  • 引用与指针的区别: 1、不存在空引用,引用必须连接到一块合法的内存。2、一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。3、引用必须在创建时被初始化。指针可以在任何时间被初始化。4、引用的对象必须是一个变量,而指针必须是一个地址。
  • 引用作为参数:
    // 函数定义
    void swap(int& x, int& y)
    {
       int temp;
       temp = x; /* 保存地址 x 的值 */
       x = y;    /* 把 y 赋值给 x */
       y = temp; /* 把 x 赋值给 y  */
       return;
    }
    
  • 引用作为返回值:当返回一个引用时,要注意被引用的对象不能超出作用域。所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用。以下是两种引用方式,第一种不需要static进行修饰,是因为ref 是一个引用变量,它直接绑定到全局数组 vals[i],并不是局部变量。
    double& setValues(int i) {  
       double& ref = vals[i];    
       return ref;   // 返回第 i 个元素的引用,ref 是一个引用变量,ref 引用 vals[i]
    }
    int& func() {
       int q;
       //! return q; // 在编译时发生错误
       static int x;
       return x;     // 安全,x 在函数作用域外依然是有效的
    }
    

面向对象

C++面向对象基本语法