C++关键字总结

C++关键字总结

思维导图链接

一、const

该用const的位置就应该带上const,一出手就是高手风范

const 在函数不同位置代表不同的作用:https://blog.youkuaiyun.com/weixin_44039187/article/details/105182211

const 在变量不同位置代表不同的作用:https://www.cnblogs.com/scyq/p/12419267.html

1.1 修饰对象

  • 变量:不可以被改变

  • 指针:主要区别在于指针本身是否可以修改指针指向的内容是否可以修改

    • 指向常量的指针(pointer to const):指向的内容是常量,const char* p2
      • 无法通过指针修改内存数据,但是可以改变指针的指向。
    • 指针常量(const pointer):指针的地址是常量,char* const p3
      • 无法改变指针的指向,但是可以修改指向的值
  • 引用:指向常量的引用(reference to const)const A &q

  • 成员函数:说明该成员函数内不能修改成员变量。

1.2 #define 和 const 常量
宏定义 #defineconst 常量
宏定义,相当于字符替换常量声明
预处理器处理编译器处理
无类型安全检查有类型安全检查
不分配内存要分配内存
存储在代码段存储在数据段
可通过 #undef 取消不可取消

二、static

2.1 非static成员 与 static成员对比

staic data member:只保存一份在内存中

static member func:无this指针,只能用于处理static数据

non-static data member:每个对象都保存一份各自的数据

不同对象调用成员函数 等价于 通过传入不同对象的this指针来作为成员函数的参数,去访问各自对象的数据

image-20240623150237029

2.2 静态数据必须在类外部定义

静态成员变量的声明在类内部,但是其定义必须在类外部定义来获得内存类类型::变量名

image-20240623150651443

2.3 修饰对象

  1. 修饰普通变量,修改变量的存储区域和生命周期,使变量存储在静态存储区,在 main 函数运行前就分配了空间,如果有初始值就用初始值初始化它。
  2. 修饰普通函数,表明函数的作用范围,仅在定义该函数的文件内才能使用防止与他人命名空间里的函数重名,可以将函数定位为 static。
  3. 修饰成员变量,修饰成员变量使所有的对象只保存一个该变量静态存储区,而非对象实例的内存中。。
  4. 修饰成员函数,不需要生成对象就可以访问该函数,存储在程序的代码段中,但是在 static 函数内不能访问非静态成员。

2.4 总结对比

使用位置存储位置作用域生命周期
静态全局变量静态存储区当前文件程序开始到程序结束
全局变量静态存储区全局作用域,跨文件可访问程序开始到程序结束
静态局部变量静态存储区定义它的块或函数内程序开始到程序结束
局部变量栈区函数或块内有效函数或块开始到结束
静态类成员变量静态存储区类内或通过类名访问程序开始到程序结束
类成员变量对象内存(堆区或栈区,取决于对象的存储位置)对象作用域随对象生命周期而定
静态类成员函数代码段类内或通过类名访问程序开始到程序结束
静态函数(普通)代码段当前文件程序开始到程序结束
普通函数代码段全局作用域程序开始到程序结束

三、inline内联函数

3.1 虚函数可以是内联函数嘛

可以,但没必要。

虚函数通过虚函数表(vtable)机制实现动态绑定(运行期)

内联函数要求函数地址在编译时已知(编译器),而动态绑定的虚函数无法满足这一要求。

而当虚函数**静态绑定时(非多态调用)**可以内联——(但是这样虚函数的意义在哪?)

3.2 内联函数和宏定义区别

特性内联函数(inline宏定义(#define
替换时机编译时预处理时(编译之前)
类型安全类型安全,参数具有明确类型无类型检查,可能导致隐式类型转换问题
作用域按照函数的作用域来解析,支持作用域控制没有作用域,可能引发意外的命名冲突
返回值可以返回值不能直接返回值,宏展开时只做文本替换
性能性能与宏相当,实际优化取决于编译器性能上通常与内联函数相似,但存在额外风险

四、volatile

用来修饰变量(指针),它告诉编译器该变量的值可能会在程序执行的过程中被外部因素(如硬件、操作系统、其他线程等)改变,从而影响编译器的优化行为。

因此在每次访问该变量时,必须从内存中重新读取它,而不是使用缓存。

4.1 什么时候使用volatile

硬件寄存器:在嵌入式编程中,通常会使用 volatile 来表示硬件寄存器的值(硬件可能会直接修改这些寄存器的值)

多线程编程:在多线程编程中,多个线程可能会同时访问同一个共享变量。此时,如果没有使用 volatile,编译器可能会对该变量进行优化

特性volatileconstatomic
用途防止编译器优化,确保每次访问内存值确保值不可修改确保线程间的原子性操作
作用保证每次访问变量时都从内存读取值不能修改变量的值保证操作的原子性,防止竞争条件
线程安全不提供线程安全支持不涉及线程安全提供线程安全支持,适用于并发编程
常见用途嵌入式编程、多线程间共享变量用于常量数据多线程共享数据的原子操作

五、explicit

主要作用:用于修饰构造函数和转换运算符,防止它们在隐式类型转换过程中被错误调用

5.1 隐式调用构造函数

class MyClass {
public:
    MyClass(int x) {
        std::cout << "Constructor called with value " << x << std::endl;
    }
};

void function(MyClass obj) {
    std::cout << "Function called" << std::endl;
}

int main() {
    function(10);  // 隐式调用 MyClass(10),会调用构造函数
    return 0;
}

//explicit
class MyClass {
public:
    explicit MyClass(int x) {  // `explicit` 防止隐式转换
        std::cout << "Constructor called with value " << x << std::endl;
    }
};

void function(MyClass obj) {
    std::cout << "Function called" << std::endl;
}

int main() {
    // function(10);  // 编译错误,因为 `MyClass(10)` 需要显式调用
    function(MyClass(10));  // 显式调用构造函数
    return 0;
}

5.2 隐式调用操作符

class MyClass {
public:
    /*explicit*/operator int() const {  // 转换为 int 类型
        return 42;
    }
};

void function(int x) {
    std::cout << "Function called with value " << x << std::endl;
}

int main() {
    MyClass obj;
    function(obj);  // 隐式调用 `operator int()`,会转换为 int 类型
  //function(static_cast<int>(obj));  // 显式调用转换
    return 0;
}
特性explicit 修饰构造函数explicit 修饰转换运算符
功能防止构造函数的隐式类型转换防止类型转换运算符的隐式转换
典型用途避免自动调用构造函数进行隐式转换避免自动调用转换运算符进行隐式转换
要求显式调用必须显式地创建对象必须显式地进行类型转换
避免的情况误用构造函数进行隐式转换误用类型转换进行隐式转换

六、friend

指定某些类或函数访问另一个类的私有(private)和保护(protected)成员,即使它们并不是该类的成员(破坏封装性)

6.1 友元函数

友元函数是一个被声明为类的友元的函数,虽然它不是该类的成员函数,但它能够访问该类的私有和保护成员

class MyClass {
private:
    int x;
public:
    MyClass(int val) : x(val) {}
    // 声明友元函数
    friend void display(const MyClass& obj);
};
// 友元函数定义
void display(const MyClass& obj) {
    std::cout << "Value of x: " << obj.x << std::endl; //可访问MyClass的私有变量 x
}

注意点:声明友元时必须把函数的声明放在类定义内部,但其定义可以在类外部。

6.2 友元类

友元类是一个被声明为另一个类的友元的类,允许该类访问目标类的私有和保护成员

class MyClass {
private:
    int x;
public:
    MyClass(int val) : x(val) {}
    // 声明 FriendClass 为友元类
    friend class FriendClass;
};
class FriendClass {
public:
    void display(const MyClass& obj) {
        std::cout << "Value of x: " << obj.x << std::endl; //可直接访问MyClass的私有变量 x
    }
};
特性友元函数友元类
定义位置声明在目标类内部,定义可以在类外部声明在目标类内部
访问范围允许单个函数访问类的私有成员允许整个类及其成员访问目标类的私有成员
作用范围只限于声明为友元的函数允许友元类的所有成员访问目标类的私有成员
类型可以是全局函数或类的成员函数等只能是类

七、using

7.1 using声明

一次只引入命名空间的一个成员。它使得我们可以清楚知道程序中所引用的到底是哪个名字

using namespace_name::name;

7.2 using指示

using 指示 使得某个特定命名空间中所有名字都可见,这样我们就无需再为它们添加任何前缀限定符了。

using namespace_name name;

7.3 尽量少使用using指示

使用 using 命令比使用 using 编译命令更安全,这是由于它只导入了指定的名称。如果该名称与局部名称发生冲突,编译器将发出指示

using编译命令导入所有的名称,包括可能并不需要的名称。如果与局部名称发生冲突,则局部名称将覆盖名称空间版本,而编译器并不会发出警告

八、::范围解析运算符

帮助开发者区分全局变量、类成员、命名空间成员等,避免命名冲突或不明确的问题。

8.1 全局作用域符(::name

用于在全局命名空间中查找标识符(变量、函数、类型等)。它常用于解决命名冲突或者在嵌套作用域中访问全局变量或全局函数

  • 访问全局变量或全局函数:即使局部作用域中有与全局命名相同的变量或函数,也可以通过 :: 来显式访问全局的标识符

    • #include <iostream>
      
      int value = 10;  // 全局变量
      
      int main() {
          int value = 20;  // 局部变量
      
          std::cout << "Local value: " << value << std::endl;       // 输出局部变量
          std::cout << "Global value: " << ::value << std::endl;   // 使用全局作用域符访问全局变量
      
          return 0;
      }
      

8.2 命名空间作用域符(namespace::name

用于表示指定类型的作用域范围是具体某个命名空间

  • 访问全局命名空间中的标识符:当命名空间或类中定义的成员名称与全局名称冲突时,:: 可用来明确区分。

    • namespace MyNamespace {
          int value = 100;
      }
      int value = 200;  // 全局变量
      int main() {
          std::cout << "Global value: " << ::value << std::endl;          // 访问全局变量
          std::cout << "Namespace value: " << MyNamespace::value << std::endl;  // 访问命名空间的变量
          return 0;
      }
      

8.3 类作用域符(class::name

用于表示指定类型的作用域范围是具体某个类的

  • 定义类的静态成员变量

    • class MyClass {
      public:
          static int value;  // 静态成员变量声明
      };
      
      // 静态成员变量的定义
      int MyClass::value = 100;
      
  • 定义类成员函数

    • class MyClass {
      public:
          void display();  // 成员函数声明
      };
      
      // 成员函数定义
      void MyClass::display() {
          std::cout << "Member function defined outside the class" << std::endl;
      }
      

九、enum

9.1 不限定作用域的枚举类型(传统枚举)

枚举常量在全局作用域中。容易引发命名冲突。

enum Color {
    Red, Green, Blue
};
enum TrafficLight {
    Red, Yellow, Green  // 与 Color 枚举冲突
};

int main() {
    std::cout << "Red: " << Red << std::endl;  // 冲突:不清楚是 Color::Red 还是 TrafficLight::Red
    return 0;
}

9.2 限定作用域的枚举类型(强类型枚举(C++11 引入))

使用 enum class 定义枚举类型,可以避免命名冲突,并增加类型安全性。

优点:

  • 枚举常量受限于枚举类型作用域,避免命名冲突。

  • 类型安全:不能将不同枚举类型的值混用。

  • 需要显式类型转换才能输出数值。

#include <iostream>

enum class Color {
    Red, Green, Blue
};

enum class TrafficLight {
    Red, Yellow, Green
};

int main() {
    Color color = Color::Red;
    TrafficLight light = TrafficLight::Red;

    // std::cout << "Red: " << Red << std::endl; // 错误,Red 不在全局作用域
    std::cout << "Color::Red: " << static_cast<int>(color) << std::endl;  // 显式转换
    std::cout << "TrafficLight::Red: " << static_cast<int>(light) << std::endl;
    return 0;
}

9.3 总结

特点传统枚举 (enum)强类型枚举 (enum class)
作用域全局作用域定义在类或命名空间作用域内
类型安全不安全,可以隐式转换为整数或其他枚举类型安全,不能隐式转换
命名冲突易发生冲突避免冲突
底层类型隐式为 int可以显式指定底层类型
使用方式EnumNameEnumName::Constant

十、decltype

用于检查实体的声明类型或表达式的类型;语法decltype ( expression )

C++11 引入,与 auto 一起极大地增强了类型推导的能力。

总结:

  • decltype 是一种编译时类型推导工具,可以获取表达式的类型。

  • auto 不同,decltype 不需要初始化变量,并且直接基于表达式的类型推导。

  • 常用于泛型编程、函数返回类型推导和复杂类型推导。

  • 通过 decltype,开发者可以在模板和复杂代码中减少显式声明类型的负担,提升代码的灵活性和可读性。

10.1 使用场景

泛型编程:在模板中,通过 decltype 推导函数或表达式的返回类型。

template <typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {  // 使用 decltype 推导返回值类型
    return a + b;
}

int main() {
    std::cout << add(1, 2.5) << std::endl;  // 返回 double 类型
    return 0;
}

未完待续…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值