【C++】深度解析C++的四种强制转换类型(小白一看就懂!!)

news2024/11/26 12:28:09

目录

一、前言

二、C风格的强制类型转换 

🥝隐式类型转换 

🍉显示类型转换 

三、为什么C++需要四种类型转换 

四、C++强制类型转换 

🍓 静态转换(static_cast)

✨用法 

✔️语法 

 🌱例子

🍋动态转换(dynamic_cast) 

✨用法 

✔️语法 

🌱例子 

🍇常量转换(const_cast) 

✨用法 

✔️语法 

🌳例子 

🍍重新解释转换(reinterpret_cast) 

✨用法 

✔️语法 

🌳例子 

五、总结 

 六、共勉


一、前言

在之前我们学过,变量的数据类型可以强制转换为其他数据类型。但由于这种C风格的类型转换可能会出现一些问题,即过于松散的情况,因此 C++ 提出了更加规范、严格的类型转换,添加了四个类型转换运算符,进而更好的控制类型转换过程

类型转换符:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast

因此,我们可以根据自身的目的选择合适的运算符,进行类型转换,也能让编译器能检查程序的行为是否和正常的逻辑相吻合。 

二、C风格的强制类型转换 

在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换显式类型转换

  1. 隐式类型转化(截断或提升):编译器在编译阶段自动进行,能转就转,不能转就编译失败
  2. 显式类型转化(强转):需要用户自己处理

🥝隐式类型转换 

隐式类型转换虽然简化了编程过程,但由于是编译器自动执行的,它会在某些情况下带来不可预期的问题,主要体现在以下方面: 

  • 隐式转换可能导致数据丢失在隐式类型转换中,可能会将一个较大范围的数据类型转换为一个较小范围的类型,导致数据丢失或精度损失。例如,double 转换为 int,会丢失小数部分。 
#include <stdio.h>

int main() {
    double pi = 3.14159;
    int truncatedPi = pi;  // 隐式转换,丢失小数部分
    printf("Truncated Pi: %d\n", truncatedPi);  // 输出:3
    return 0;
}

🍉显示类型转换 

显式类型转换可以给程序员更多的控制权,但也可能引发一些问题,尤其是在不恰当地使用时。 

  • 不安全的转换显式转换可以绕过编译器的类型检查,允许将完全不相关的类型相互转换。例如,将指针转换为整数类型,或将不同类型的指针相互转换,可能导致严重的运行时错误或未定义行为。 
#include <stdio.h>
#include <stdint.h>  // 包含 uintptr_t

int main() {
    int a = 100;
    int *ptr = &a;

    // 使用 uintptr_t 来存储指针的整数值
    uintptr_t address = (uintptr_t)ptr;

    // 将整数转换回指针
    int *newPtr = (int*)address;
    printf("Dereferenced value: %d\n", *newPtr); // 100

    return 0;
}

三、为什么C++需要四种类型转换 

C风格的转换格式很简单,但是有不少缺点的: 

  1. 隐式类型转换有些情况下可能会出问题:比如数据精度丢失
  2. 显式类型转换将所有情况混合在一起,代码不够清晰

因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。

四、C++强制类型转换 

 标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast

下面来分开来讨论。 


🍓 静态转换(static_cast)

✨用法 

 静态转换可以用两种用法:

  • 基本数据类型的转换(如将int类型转换为char类型)
  • 静态转换用于类层次结构中父类和子类之间指针或引用的转换(有继承关系的)

✔️语法 

static_cast <要转换的类型>(变量名或表达式)

 🌱例子

基本数据类型转换的例子

1. 整数类型之间的转换 

#include <iostream>

int main() {
    int num = 97;

    // 将int类型转换为char类型
    char ch = static_cast<char>(num);

    std::cout << "Integer: " << num << std::endl;
    std::cout << "Character: " << ch << std::endl;  // 输出对应的ASCII字符

    return 0;
}

说明

  • 在这个例子中,static_castint 类型的 num 转换为 char 类型,输出的结果是对应 ASCII 值 97 的字符 'a'

2. 浮点数与整数之间的转换 

#include <iostream>

int main() {
    double pi = 3.14159;

    // 将double类型转换为int类型(截断小数部分)
    int truncatedPi = static_cast<int>(pi);

    std::cout << "Original value (double): " << pi << std::endl;
    std::cout << "Truncated value (int): " << truncatedPi << std::endl;

    return 0;
}

说明

  • pi 是一个 double 类型的浮点数,通过 static_cast 将其转换为 int,结果 truncatedPi3,小数部分被截断。

3. 指针与 void* 之间的转换

#include <iostream>

int main() {
    int value = 42;
    int* intPtr = &value;

    // 将int*转换为void*
    void* voidPtr = static_cast<void*>(intPtr);

    // 将void*转换回int*
    int* newIntPtr = static_cast<int*>(voidPtr);

    std::cout << "Original value: " << *intPtr << std::endl;
    std::cout << "New pointer value: " << *newIntPtr << std::endl;

    return 0;
}

说明

  • 我们首先将 int* 类型的指针转换为 void*,然后再将其转换回 int*。由于这只是类型的转换,并未改变指针的地址,因此 *intPtr *newIntPtr 的值相同。

4. 指针与空指针 (nullptr) 之间的转换

#include <iostream>

int main() {
    int* ptr = nullptr;

    // 将nullptr转换为void*,表示空指针
    void* voidPtr = static_cast<void*>(ptr);

    if (voidPtr == nullptr) {
        std::cout << "The pointer is nullptr." << std::endl;
    }

    return 0;
}
  • static_cast<void*>(ptr)nullptr 转换为 void* 类型的空指针,这是在处理泛型指针时的一种常见用法 

使用 static_cast 进行类层次结构中的类型转换

1. 从子类转换为父类(Upcasting) 

  • 这是安全的,因为子类包含了父类的所有成员,因此将子类的指针或引用转换为父类的指针或引用是安全的。 
#include <iostream>

class Animal {
public:
    virtual void sound() const {
        std::cout << "Animal makes a sound" << std::endl;
    }
};

class Dog : public Animal  // 继承
{
public:
    void sound() const override  // 虚函数重写
{
        std::cout << "Dog barks" << std::endl;
    }
};

int main() {
    Dog myDog;
    Animal* animalPtr = static_cast<Animal*>(&myDog);  // 安全的上行转换
    animalPtr->sound();  // 调用的是 Dog 的 sound,因为它是虚函数

    return 0;
}

说明

  • 这里我们将 Dog 类的对象 myDog 转换为其基类 Animal 的指针 animalPtr。这是安全的,因为 DogAnimal 的子类,并且继承了父类的所有属性和方法。
  • 使用虚函数的情况下,虽然我们将 Dog 转换为 Animal,但调用的是 Dogsound() 方法,这是由于多态性的作用。

2. 从父类转换为子类(Downcasting) 

  • 将父类转换为子类是不安全的,只有当我们确信父类指针实际上指向的是一个子类对象时,才能进行这种转换。否则,转换后的对象可能无法正常工作,甚至导致未定义行为。 
#include <iostream>

class Animal {
public:
    virtual void sound() const {
        std::cout << "Animal makes a sound" << std::endl;
    }
};

class Dog : public Animal {
public:
    void sound() const override {
        std::cout << "Dog barks" << std::endl;
    }
    void fetch() const {
        std::cout << "Dog fetches the ball" << std::endl;
    }
};

int main() {
    Animal* animalPtr = new Dog();  // Animal指针指向Dog对象
    Dog* dogPtr = static_cast<Dog*>(animalPtr);  // 下行转换

    dogPtr->sound();  // 调用 Dog 的 sound 方法
    dogPtr->fetch();  // 调用 Dog 的 fetch 方法

    delete animalPtr;
    return 0;
}

说明

  • 这里 animalPtr 是一个指向基类 Animal 的指针,但它实际上指向一个 Dog 对象。因此,我们可以安全地使用 static_castanimalPtr 转换为 Dog* 类型的指针 dogPtr,然后调用 Dog 类中特有的 fetch() 方法。
  • 注意:这种转换前提是 animalPtr 实际上指向一个 Dog 对象。如果它指向的是其他类型的对象,比如另一个继承自 Animal 的子类对象,那么这种转换会导致未定义行为。

🍋动态转换(dynamic_cast) 

动态转换dynamic_cast)是 C++ 中的一种类型转换操作,专门用于处理多态类型(即包含虚函数的类层次结构)。dynamic_cast 可以在运行时对类的类型进行检查,确保类型转换的安全性。 

✨用法 

相比于静态类型转换(如 static_cast),dynamic_cast 会在运行时进行检查,并且只有在以下两种情况下使用:

  1. 上行转换(Upcasting):从子类转换为父类。
  2. 下行转换(Downcasting):从父类转换为子类,且要求父类中至少有一个虚函数(即是多态类)。

dynamic_cast 在多态类型中非常有用,尤其是在进行 下行转换 时,它可以在运行时检查父类指针或引用是否实际指向一个子类对象。如果转换失败,dynamic_cast 会返回 nullptr(对于指针类型)或抛出异常(对于引用类型),从而避免不安全的类型转换。 

✔️语法 

dynamic_cast<要转换的类型>(变量名或表达式)

🌱例子 

1:安全的上行转换(Upcasting) 

  • 上行转换是指将子类对象的指针或引用转换为父类的指针或引用。这种转换是安全的,因为子类总是包含父类的所有成员。 
#include <iostream>

class Animal {
public:
    virtual void makeSound() const {
        std::cout << "Animal sound" << std::endl;
    }
};

class Dog : public Animal {
public:
    void makeSound() const override {
        std::cout << "Dog barks" << std::endl;
    }
};

int main() {
    Dog myDog;
    Animal* animalPtr = dynamic_cast<Animal*>(&myDog);  // 上行转换(子类到父类)

    animalPtr->makeSound();  // 输出: Dog barks,调用的是子类的重载方法

    return 0;
}

说明

  • 在这个例子中,myDog Dog 类的对象,我们将 Dog 类的指针转换为其基类 Animal 的指针。这是安全的,因为 DogAnimal 的派生类。
  • 虽然我们使用的是父类的指针,但由于 makeSound() 是一个虚函数,调用的是 Dog 类中的重载方法。

2:安全的下行转换(Downcasting) 

  • 下行转换是指将父类对象的指针或引用转换为子类的指针或引用。在这种情况下,必须确保父类的指针实际上指向的是子类对象,否则转换可能会失败。 
#include <iostream>

class Animal {
public:
    virtual ~Animal() {}  // 基类需要至少有一个虚函数
    virtual void makeSound() const {
        std::cout << "Animal sound" << std::endl;
    }
};

class Dog : public Animal {
public:
    void makeSound() const override {
        std::cout << "Dog barks" << std::endl;
    }
    void fetch() const {
        std::cout << "Dog fetches the ball" << std::endl;
    }
};

class Cat : public Animal {
public:
    void makeSound() const override {
        std::cout << "Cat meows" << std::endl;
    }
};

int main() {
    Animal* animalPtr = new Dog();  // 父类指针指向一个Dog对象

    // 使用 dynamic_cast 进行下行转换
    Dog* dogPtr = dynamic_cast<Dog*>(animalPtr);

    if (dogPtr) {  // 如果转换成功
        dogPtr->makeSound();  // 输出: Dog barks
        dogPtr->fetch();      // 输出: Dog fetches the ball
    } else {
        std::cout << "Conversion failed!" << std::endl;
    }

    delete animalPtr;
    return 0;
}

说明

  • animalPtr 是一个指向基类 Animal 的指针,但它实际上指向一个 Dog 对象。我们使用 dynamic_cast 尝试将 Animal* 转换为 Dog*
  • dynamic_cast 会在运行时检查,如果 animalPtr 指向的是 Dog,则转换成功,返回有效的指针;如果指向的是其他派生类(例如 Cat),则转换失败,dogPtrnullptr
  • 在下行转换中,dynamic_cast 可以防止不安全的类型转换,因为它会在运行时确保转换的正确性。

🍇常量转换(const_cast) 

const_cast 是 C++ 中专门用于 常量属性 的类型转换操作符。它的主要作用是 移除添加 对象的 constvolatile 修饰符。与其他类型转换操作符不同,const_cast 只能用于修改对象的常量性,而不能用于在不同类型之间进行转换。 

✨用法 

  • 移除 const 限定符:允许将指向常量的指针或引用转换为指向非常量的指针或引用,以便修改常量对象(注意:修改真正的常量对象会导致未定义行为)。
  • 添加 const 限定符:也可以用于给指针或引用添加 const 限定符。

✔️语法 

const_cast <要转换的类型>(变量名或表达式)

🌳例子 

 移除 const 属性

#include <iostream>

void modify(int* p) 
{
    *p = 100;  // 修改指针指向的值
}

int main() 
{
    const int a = 10;  // a 是常量
    const int* p = &a;  // p 是指向常量的指针

    // 使用 const_cast 去除 const 属性,试图修改常量 a
    modify(const_cast<int*>(p));  // 不安全,可能导致未定义行为

    std::cout << "a = " << a << std::endl;  // 输出未定义结果

    return 0;
}

说明

  • 这里我们尝试通过 const_castconst int* 转换为 int*,然后修改指针指向的值 a
  • 问题:由于 a 是一个常量,试图修改它的行为是不安全的,可能导致 未定义行为(UB)。一些编译器可能不会检测出错误,程序可能会继续运行,但结果无法预测。

2:移除 const,修改非常量对象 

#include <iostream>

void modify(int* p) {
    *p = 200;  // 修改指针指向的值
}

int main() {
    int b = 20;  // b 是非常量
    const int* p = &b;  // p 是指向常量的指针,但指向非常量

    // 使用 const_cast 去除 const 属性,安全修改 b
    modify(const_cast<int*>(p));

    std::cout << "b = " << b << std::endl;  // 输出: b = 200

    return 0;
}

说明

  • 在这个例子中,p 是指向 b 的常量指针,虽然 p 被修饰为 const,但 b 本身是非常量。因此,通过 const_cast 移除 pconst 修饰符是安全的,可以修改 b 的值。
  • 结果 b 被安全地修改为 200

3:添加 const 限定符 

  • 有时你需要将非常量对象强制转换为常量对象来保证代码的安全性,防止某些函数无意中修改对象。 
#include <iostream>

void print(const int* p) {
    std::cout << "Value: " << *p << std::endl;
}

int main() {
    int c = 50;
    int* p = &c;

    // 将非常量指针转换为常量指针,保证不会修改对象
    print(const_cast<const int*>(p));  // 安全地将 p 转换为 const int*

    return 0;
}

说明

  • 这里使用 const_cast 将非常量指针 p 转换为常量指针,并将其传递给 print 函数,保证函数不会修改指针指向的对象。
  • 这种做法确保了 print 函数只能读取数据,而不能修改 c

🍍重新解释转换(reinterpret_cast) 

 reinterpret_cast 是 C++ 中最强大、但也最危险的类型转换操作符之一。它允许在不同类型之间进行低级别的类型转换。与其他类型转换操作符不同,reinterpret_cast 并不会进行任何类型检查,它仅仅是重新解释二进制位的含义,直接将一个类型的位模式重新解释为另一个类型。

✨用法 

  • 指针类型之间的转换:将一个指针类型转换为另一个指针类型,甚至是将指针转换为整数类型(或反之)。
  • 指针和整数之间的转换:允许将指针转换为整数类型,或将整数转换为指针类型。
  • 非相关类型之间的转换:允许在不相关的类型之间进行转换,比如将 float* 转换为 int*,或者将对象类型转换为字节序列。

✔️语法 

reinterpret_cast<要转换的类型>(变量名或表达式)

🌳例子 

 1:指针类型之间的转换

#include <iostream>

class A {
public:
    void show() {
        std::cout << "This is class A" << std::endl;
    }
};

class B {
public:
    void display() {
        std::cout << "This is class B" << std::endl;
    }
};

int main() {
    A objA;
    B* bPtr = reinterpret_cast<B*>(&objA);  // 将 A* 转换为 B*

    // 这里的 bPtr 指向的其实是 A 的对象,但我们把它当作 B 来操作
    bPtr->display();  // 可能导致未定义行为

    return 0;
}

说明

  • 这里我们将 A 类型的指针转换为 B 类型的指针,并调用了 B 的方法。这种转换是危险的,因为 AB 并不相关,强行转换会导致内存错误或未定义行为。
  • 风险:指针的二进制位未改变,但内存布局不同,因此调用 bPtr->display() 时可能会崩溃或输出不正确的结果。

2:指针和整数之间的转换 

  • reinterpret_cast 可以将指针转换为整数类型,或将整数转换为指针。通常用于系统编程或底层操作,如操作内存地址。 
#include <iostream>

int main() {
    int x = 42;
    int* intPtr = &x;

    // 将指针转换为整数类型 uintptr_t (C++标准中定义的足够大以容纳指针的类型)
    uintptr_t address = reinterpret_cast<uintptr_t>(intPtr);

    std::cout << "Address as integer: " << address << std::endl;

    // 将整数转换回指针
    int* newPtr = reinterpret_cast<int*>(address);

    std::cout << "Dereferenced value: " << *newPtr << std::endl;

    return 0;
}

说明

  • uintptr_t 是一个无符号整数类型,它可以存储指针的数值。通过 reinterpret_cast,我们可以将指针转换为整数,然后再转换回来。
  • 这种转换通常在底层操作中使用,例如在操作系统内核中管理内存地址或处理硬件寄存器。
  • 风险:如果不小心修改了 address,然后再将其转换回指针,可能会导致非法的内存访问。

3:类型之间的强制转换(例如 float* 转换为 int*) 

#include <iostream>

int main() {
    float f = 3.14f;
    int* intPtr = reinterpret_cast<int*>(&f);  // 将 float* 转换为 int*

    std::cout << "Float value: " << f << std::endl;
    std::cout << "Reinterpreted as int: " << *intPtr << std::endl;  // 输出 f 的位模式所代表的整数

    return 0;
}

说明

  • reinterpret_castfloat 类型的指针转换为 int*,并通过 int* 解引用来读取数据。这会输出 float 的位模式所对应的整数值,而不是 float 的数值本身。
  • 这种操作会重新解释数据的位模式,因此结果可能非常难以理解。如果需要处理底层内存或二进制文件,这种操作可能有用,但在普通应用程序中应避免。

五、总结 

每种转换工具都有其特定的用途,在实际开发中选择合适的转换方式可以避免潜在的错误并提高代码的可读性和安全性。

  • static_cast用于基本数据类型和类层次中的安全转换,通常用于类型之间的已知转换,不涉及运行时检查。
  • dynamic_cast主要用于类层次的下行转换,提供运行时类型检查,确保转换安全性,常用于多态环境。
  • const_cast用于移除或添加 const/volatile 修饰符,必须谨慎使用,不能修改真正的常量。
  • reinterpret_cast用于底层的、低级别的强制转换,不进行类型检查,应避免不必要的使用,因其可能导致未定义行为。

 

 六、共勉

以下就是我对 【C++】强制转换 的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新【C++】请持续关注我哦!!!   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2128708.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

oracle数据库安装和配置:详细教程

安装和配置Oracle数据库是一个较为复杂的过程&#xff0c;需要按照一定的步骤来操作。以下是基于Oracle Database 19c的安装和配置教程&#xff0c;适用于Windows环境。请根据你的具体环境和版本进行适当的调整。 1. 下载Oracle数据库软件 访问Oracle官方网站下载Oracle Data…

2.C++入门2(函数重载与引用)

⭐本章主要介绍c中的函数重载&#xff0c;重载的原理&#xff0c;和引用&#xff08;参数引用&#xff0c;返回值引用&#xff09; ⭐本人c代码的Gitee仓库&#xff1a;c学习 橘子真甜/yzc的c学习 - 码云 - 开源中国 (gitee.com) 一.函数重载 1.1 什么是函数重载 函数重载是函…

使用OpenCore Auxiliary Tools(OCAT)为黑苹果生成三码

文章目录 基础知识apple三码常用工具OCAT 操作方法配置正确的open core版本一键生成 验证 基础知识 apple三码 苹果的生态是一套完整的闭环&#xff0c;每一个苹果硬件产品都有独一无二的 SMBIOS ID&#xff08;机型 ID&#xff09;&#xff0c;机型 ID 决定序列号、主板序列号…

遥感技术在生态系统碳储量、碳收支、碳排放、碳循环以及人为源排放反演等领域的技术发展,实践角度解决遥感技术在生态、能源、大气等领域的碳排放监测及模拟问题

卫星遥感具有客观、连续、稳定、大范围、重复观测的优点&#xff0c;已成为监测全球碳盘查不可或缺的技术手段&#xff0c;卫星遥感也正在成为新一代 、国际认可的全球碳核查方法。本教程的目的就是梳理碳中和与碳达峰对卫星遥感的现实需求&#xff0c;系统总结遥感技术在生态系…

I 农产品如何实现全链路追踪?区块链、非中心化数据库的应用

大家好啊&#xff0c;我是豆小匠。 本期作为区块链技术分享的第一期&#xff0c;和大家分享下区块链在现实生活中的应用。 区块链的场景 说起区块链&#xff0c;大家可能比较陌生&#xff0c;但是说比特币估计都有耳闻。 但是作为一项技术&#xff0c;区块链的应用远不止于此…

mysql Field ‘ssl_cipher‘ doesn‘t have a default value的解决

1、执行sql的时候报错&#xff1a; 16:48:00 INSERT INTO mysql.user (Host,User,authentication_string) VALUES(%,root, PASSWORD(12323)) Error Code: 1364. Field ssl_cipher doesnt have a default value 0.000 sec 1、解决&#xff0c;执行命令&#xff1a; my…

Linux 操作系统 进程(2)

上篇文章我们说过的操作系统进程的概念等&#xff08;Linux 操作系统 进程&#xff08;1&#xff09;-CSDN博客&#xff09;&#xff0c;这篇我们就来了解进程最重要最直观的两个状态 &#xff1a; 进程状态 进程优先级 进程状态 kill命令 我们在查询进程的时候就可以看到当前…

港股通“大洗牌”,这是一场“双赢”还是一次“交易”?

最近的资本市场&#xff0c;可谓是“一石激起千层浪”。先有阿里完成香港双重主要上市&#xff0c;随后港股通进行“大洗牌”。 9月9日&#xff0c;上交所、深交所发布公告&#xff0c;阿里巴巴-W&#xff08;09988.HK&#xff09;、茶百道(02555.HK)、云音乐(09899.HK)、顺丰…

YB5090是一款 5V USB 输入,支持双节串联锂电池或锂离子电池的升压充电管理 IC

适用于TYPE-C接口,集成30V OVP功能 , 最大1.8A充电电流,带NTC及使能功能,双节锂电升压充电芯片 概述&#xff1a; 是一款 5V USB 输入&#xff0c;支持双节串联锂电池或锂离子电池的升压充电管理 IC。集成有 NTC 功能&#xff0c;其最大的充电电流可达 1.8A。 集成功率MOS,采用…

架构师备考的一些思考(二)

前言 以我的视野来看&#xff0c;部长或技术总监这种岗位还是比较难竞争的&#xff0c;换言之&#xff0c;程序员的上升空间比较窄&#xff0c;如果想要拿到高级岗位&#xff0c;最好的是工作三五年后就转项目经理&#xff0c;然后再往上爬。 架构师倒是也能晋升高级岗位&#…

git的快速合并fast-forward merge详解

文章目录 1. 什么是快进合并&#xff1f;2. 快进合并的前提条件3. 快进合并的工作原理3.1 示例场景&#xff1a;3.2 使用命令&#xff1a;3.3 快进合并的视觉效果&#xff1a; 4. 快进合并的优点5. 快进合并的缺点6. 快进合并 vs 非快进合并6.1 非快进合并&#xff1a;6.2 非快…

Promise详解、自定义

这里写目录标题 一、.Promise对象详解1. Promise是什么2. Promise的优点3. 异步编程4. then(onFulfilled, onRejected)&#xff1a;5. util.promisify 方法&#xff1a;6. promise的状态改变7.Promise 对象的值8. Promise的基本执行流程9. Promise的API10. Promise中的关键问题…

怎样训练一个自己的大语言模型?这可能是全网最简单易懂的教程!

Llama 2 是来自 Meta 的第二代开源LLM的集合&#xff0c;旨在处理各种自然语言处理任务&#xff0c;模型的规模从 7B&#xff08;70亿&#xff09;到 70B&#xff08;700亿&#xff09;个参数不等&#xff0c;可以商用。 Llama-2-Chat 针对对话进行了优化&#xff0c;显示出与…

你们准备好了吗?Python 入行 AI 的基础技术栈及学习路线

人工智能&#xff08;AI&#xff09;是当今技术发展的重要领域之一&#xff0c;而 Python 已成为 AI 领域的首选编程语言之一。Python 简单易学&#xff0c;具有丰富的生态系统和社区支持&#xff0c;特别是在 AI 和机器学习&#xff08;ML&#xff09;领域有大量强大的库和框架…

电商系统源码开发中的卷轴模式系统:当前技术面临的问题

随着互联网技术的飞速发展&#xff0c;电商系统已成为数字经济的重要组成部分。为了提升用户体验和平台活跃度&#xff0c;卷轴模式作为一种创新的用户参与机制&#xff0c;逐渐在电商系统中崭露头角。然而&#xff0c;在电商系统源码开发卷轴模式系统的过程中&#xff0c;仍面…

‌汽车的舒适进入功能是什么意思?

移动管家汽车的舒适进入系统是指无钥匙进入功能&#xff0c;它允许驾驶者在距离车辆一定范围内自动感应解锁车辆&#xff0c;并具备无钥匙启动功能‌。舒适进入系统的核心优势包括&#xff1a; ‌智能化操作‌&#xff1a;无需传统钥匙&#xff0c;通过智能感应实现车门解锁和…

@Transactional和@Synchronized的冲突

Transactional和Synchronized的冲突 场景 方法是先进行检查&#xff0c;然后新增&#xff0c;添加了事务注解&#xff0c;为了保证检查&#xff08;要求业务上唯一&#xff09;&#xff0c;添加了Synchronized注解 想法很简单&#xff0c;事务注解保证方法原子性&#xff0c…

基于Kithara实时套件的EtherCAT主站

第1章 Kithara实时套件概述 1.1 概述 Kithara Software是一家德国的软件公司&#xff0c;专注于实时技术和嵌入式解决方案。 他们为Windows操作系统提供了Kithara RealTime Suite&#xff0c;这是一套实时扩展模块&#xff0c;使Windows能够实现硬实时任务和控制。 Kithara R…

C++实现宏编译不同版本程序

1. #define的概念 #define命令是C语言中的一个宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。该命令有两种格式:一种是简单的宏定义,另一种是带参数的宏定义。 (1)简单的宏定义: #define <宏名>  <字符串…

JavaScript web API part2

web API 全选反选案例 需求&#xff1a; 勾选大复选框&#xff0c;勾选全部小复选框取消勾选大复选框&#xff0c;则取消勾选全部小复选框若有小复选框没有被勾选&#xff0c;则大复选框不被勾选若所有小复选框都被勾选&#xff0c;则大复选框被勾选 <!DOCTYPE html>…