C++中常用的四种类型转换方式

news2024/11/17 13:30:46

C++中常用的四种类型转换方式

  • 一、相关概念
  • 二、static_cast 转换
    • 2.1、说明
    • 2.2、返回值
    • 2.3、示例
  • 三、const_cast 转换
    • 3.1、说明
    • 3.2、返回值
    • 3.3、示例
  • 四、dynamic_cast 转换
    • 4.1、说明
    • 4.2、返回值
    • 4.3、示例
  • 五、reinterpret_cast 转换
    • 5.1、说明
    • 5.2、返回值
    • 5.3、示例
  • 总结

一、相关概念

C++中常用的四种类型转换方式如下:

  1. 静态转换(static_cast)。

静态转换可以在编译时期完成,适用于具有继承关系的类或者指针类型之间的转换。静态转换会进行一些类型检查,但是不会进行运行时检查。如果类型无法转换,则会编译报错。

示例代码:

double d = 3.14;
int i = static_cast<int>(d);
  1. 动态转换(dynamic_cast)。

动态转换可以在运行时期完成,适用于具有继承关系的类或指针类型之间的转换。动态转换会进行类型检查,如果类型无法转换,则返回空指针(对于指针类型)或抛出bad_cast异常(对于引用类型)。

示例代码:

class Base { virtual void foo() {} };
class Derived : public Base {};

Base* ptr = new Derived;
Derived* dptr = dynamic_cast<Derived*>(ptr);
if (dptr != nullptr) {
    // 转换成功
} else {
    // 转换失败
}
  1. 常量转换(const_cast)。

常量转换用于去掉表达式的const属性,使其变成非常量表达式。常量转换不能改变表达式的类型,只能改变const属性。

示例代码:

const int a = 10;
int b = const_cast<int&>(a);
  1. 重解释转换(reinterpret_cast)。

重解释转换用于进行各种类型之间的强制转换,包括指针、引用、整数之间的转换。它是一种非常危险的类型转换,因为它会改变数据的原本含义。

示例代码:

int a = 10;
double b = reinterpret_cast<double&>(a);

二、static_cast 转换

使用隐式转换和用户定义转换的组合在类型之间进行转换。

语法:

static_cast< new-type expression>(...)		

返回类型为“new-type”的值。

2.1、说明

只有以下转换可以使用static_cast完成,除非此类转换会抛弃恒定性或波动性。

(1)如果 new-type 是对某个类的引用,expression 是其非虚基的左值;或者 new-type 是指向某个完整类的指针,expression 是指向其非虚基的 prvalue 指针,static_cast执行向下转换。(如果此向下转换是不明确的、无法访问的或虚拟基础(或虚拟基础的基础结构)的,则格式不正确)。

这种向下转换不会进行运行时检查以确保对象的运行时类型是真实的,并且只有在通过其他方式保证此前提条件时才能安全使用,例如在实现静态多态性时。安全的下落可以通过dynamic_cast来完成。 如果对象表达式引用或指向的实际上是类型对象的基类子对象,则结果引用类型的封闭对象。否则,行为是未定义的。

struct B {};
struct D : B { B b; };
 
D d;
B& br1 = d;
B& br2 = d.b;
 
static_cast<D&>(br1); // OK: 左值表示原始的d对象
static_cast<D&>(br2); // UB: 子对象b不是基类子对象

(2)如果new-type是右值引用类型,则static_cast将glvalue、类prvalue或数组prvalue (c++ 17之前)的任何左值(c++ 17之后)表达式的值转换为指向与该表达式相同对象的xvalue,或者指向其基子对象(取决于new-type)。

  • 如果目标类型是表达式类型的不可访问或不明确的基,则程序是病态的。
  • 如果表达式是位域左值,则首先将其转换为基础类型的右值。这种类型的static_cast用于在std::move中实现move语义。

(3)如果存在从表达式到new-type的隐式转换序列,或者如果重载解析直接初始化对象或new-type类型引用的重载解析会找到至少一个可行的函数,则static_cast< new-typeexpression>()返回虚拟变量Temp,就像由new-type Temp(expression)初始化一样,这可能涉及隐式转换、调用new-type的构造函数或调用用户定义的转换操作符。对于非引用的new-type, static_cast右值表达式的结果对象是直接初始化的对象。

(4)如果new-type是void类型(可能是cv限定的),则static_cast在求值后丢弃expression的值。

(5)如果存在从new-type到表达式类型的标准转换序列,该序列不包括左值到右值、数组到指针、函数到指针、空指针、空成员指针、函数指针(自c++ 17起)或布尔转换,则static_cast可以执行该隐式转换的逆操作。

(6)如果表达式到new-type的转换涉及左值到右值、数组到指针或函数到指针的转换,则可以通过static_cast显式执行。

(7)有作用域的枚举类型可以转换为整数或浮点类型。到C++20,其结果与从枚举的基础类型到目标类型的隐式转换相同。当目标类型为bool(可能是cv限定的)时,如果原始值为零,结果为false,其他所有值为true。对于其余整型,如果枚举的值可以用目标类型表示,则结果为其值,否则未指定。

(8)整数或枚举类型的值可以转换为任何完整的枚举类型。

  • 如果基础类型不固定,则在表达式的值超出范围时,行为是未定义的(范围是足够大的最小位字段的所有可能值,以容纳目标枚举的所有枚举器)。
  • 如果基础类型是固定的,则结果与先将原始值转换为枚举的基础类型,然后再转换为枚举类型相同。

浮点类型的值也可以转换为任何完整的枚举类型。结果与先将原始值转换为枚举的基础类型,然后再转换为枚举类型相同。

(9)浮点类型的右值可以显式地转换为任何其他浮点类型。

(10)指向某个完整类的成员的指针可以向上转换为指向其明确的、可访问的基类的成员的指针。这个static_cast不进行检查,以确保该成员实际存在于指向object.DB的运行时类型中。

(11)指向 void 的指针类型的 prvalue(可能符合 cv 条件)可以转换为指向任何对象类型的指针。

2.2、返回值

(1)与所有强制转换表达式一样,结果是:

  • 如果new-type是左值引用类型或对函数类型的右值引用,则为左值(c++ 11起);
  • 如果new-type是对象类型的右值引用,则使用xvalue;(自c++ 11起)。
  • 否则,使用右值。

(2)两个对象一个和b在以下情况下是指针可相互转换的:

  • 它们是同一对象;
  • 或者一个是联合对象,另一个是该对象的非静态数据成员;
  • 或者一个是标准布局类对象,另一个是该对象或该对象的任何基类子对象的第一个非静态数据成员;
  • 或者存在一个对象c这样一个和c是指针可相互转换的,并且c和b是指针可相互转换的。
union U { int a; double b; } u;
void* x = &u;                        // x's value is "pointer to u"
double* y = static_cast<double*>(x); // y's value is "pointer to u.b"
char* z = static_cast<char*>(x);     // z's value is "pointer to u"

static_cast还可用于通过执行到特定类型的函数到指针转换来消除函数重载的歧义,比如:

std::for_each(files.begin(), files.end(),
              static_cast<std::ostream&(*)(std::ostream&)>(std::flush));

2.3、示例

#include <iostream>
#include <vector>
 
struct B
{
    int m = 42;
    const char* hello() const
    {
        return "Hello world, this is B!\n";
    }
};
 
struct D : B
{
    const char* hello() const
    {
        return "Hello world, this is D!\n";
    }
};
 
enum class E { ONE = 1, TWO, THREE };
enum EU { ONE = 1, TWO, THREE };
 
int main()
{
    // 1. static downcast
    D d;
    B& br = d; // upcast via implicit conversion
    std::cout << "1) " << br.hello();
    D& another_d = static_cast<D&>(br); // downcast
    std::cout << "1) " << another_d.hello();
 
    // 2. lvalue to xvalue
    std::vector<int> v0{1,2,3};
    std::vector<int> v2 = static_cast<std::vector<int>&&>(v0);
    std::cout << "2) after move, v0.size() = " << v0.size() << '\n';
 
    // 3. initializing conversion
    int n = static_cast<int>(3.14);
    std::cout << "3) n = " << n << '\n';
    std::vector<int> v = static_cast<std::vector<int>>(10);
    std::cout << "3) v.size() = " << v.size() << '\n';
 
    // 4. discarded-value expression
    static_cast<void>(v2.size());
 
    // 5. inverse of implicit conversion
    void* nv = &n;
    int* ni = static_cast<int*>(nv);
    std::cout << "5) *ni = " << *ni << '\n';
 
    // 6. array-to-pointer followed by upcast
    D a[10];
    [[maybe_unused]]
    B* dp = static_cast<B*>(a);
 
    // 7. scoped enum to int
    E e = E::TWO;
    int two = static_cast<int>(e);
    std::cout << "7) " << two << '\n';
 
    // 8. int to enum, enum to another enum
    E e2 = static_cast<E>(two);
    [[maybe_unused]]
    EU eu = static_cast<EU>(e2);
 
    // 9. pointer to member upcast
    int D::*pm = &D::m;
    std::cout << "9) " << br.*static_cast<int B::*>(pm) << '\n';
 
    // 10. void* to any type
    void* voidp = &e;
    //[[maybe_unused]]
    std::vector<int>* p = static_cast<std::vector<int>*>(voidp);
}

输出:

1) Hello world, this is B!
1) Hello world, this is D!
2) after move, v0.size() = 0
3) n = 3
3) v.size() = 10
5) *ni = 3
7) 2
9) 42

三、const_cast 转换

在具有不同限制资格的类型之间进行转换。
语法:

const_cast< new-type expression>(...)		

返回new-type类型的值。

3.1、说明

const_cast只能进行以下转换。特别是,只有const_cast可用于抛弃(去除)恒常性或波动性。

(1)指向同一类型的两个可能的多级指针可以相互转换,而不必考虑每个级别上的cv限定符。

(2)任何类型的左值都可以转换为相同类型的左值或右值引用,或多或少受cv限制。同样,类类型的右值或任何类型的右值都可以转换为或多或少限定cv的右值引用。如果表达式是一个全局值,则引用const_cast的结果指向原始对象,否则指向物化的临时对象。

(4)同样的规则也适用于可能指向数据成员的多级指针,以及可能指向具有已知和未知边界的数组的多级指针(指向限定cv的元素的数组本身也被认为是限定cv的)。

(5)空指针值可以转换为new-type的空指针值。

3.2、返回值

与所有强制转换表达式一样,结果是:

  • 如果new-type是左值引用类型或对函数类型的右值引用,则为左值(c++ 11起);
  • 如果new-type是对象类型的右值引用,则使用xvalue;
  • 否则使用右值。

指向函数的指针和指向成员函数的指针不受const_cast约束。
const_cast可以形成指向实际引用 const 对象的非 const 类型的引用或指针,或者形成指向实际引用 volatile 对象的非易失性类型的引用或指针。通过非常量访问路径修改 const 对象并通过非 const glvalue 引用易失性对象会导致未定义的行为。

3.3、示例

#include <iostream>
 
struct type
{
    int i;
 
    type(): i(3) {}
 
    void f(int v) const
    {
        // this->i = v;                 // compile error: this is a pointer to const
        const_cast<type*>(this)->i = v; // OK as long as the type object isn't const
    }
};
 
int main()
{
    int i = 3;                 // i is not declared const
    const int& rci = i;
    const_cast<int&>(rci) = 4; // OK: modifies i
    std::cout << "i = " << i << '\n';
 
    type t; // if this was const type t, then t.f(4) would be undefined behavior
    t.f(4);
    std::cout << "type::i = " << t.i << '\n';
 
    const int j = 3; // j is declared const
    [[maybe_unused]]
    int* pj = const_cast<int*>(&j);
    // *pj = 4;      // undefined behavior
 
    [[maybe_unused]]
    void (type::* pmf)(int) const = &type::f; // pointer to member function
    // const_cast<void(type::*)(int)>(pmf);   // compile error: const_cast does
                                              // not work on function pointers
}

结果输出:

i = 4
type::i = 4

四、dynamic_cast 转换

沿继承层次结构安全地向上、向下和横向转换对类的指针和引用。

语法:

dynamic_cast< target-type expression>(...)		

参数:

  • target-type:指向完整类类型的指针、对完整类类型的引用或指向(可选符合 CV 条件)void 的指针
  • expression:lvalue(直到 C++11)glvalue (自 C++11) 如果目标类型是引用,则指向完整类类型的指针的 PR值(如果目标类型是指针)。

如果强制转换成功,dynamic_cast返回目标类型的值。如果强制转换失败并且目标类型是指针类型,则返回该类型的空指针。如果强制转换失败并且目标类型是引用类型,则会引发与 std::bad_cast 类型的处理程序匹配的异常。

4.1、说明

为了便于描述,“表达式或结果是对”的引用“表示”它是类型的glvalue“,遵循decltype的约定。TT

只有以下转换可以用dynamic_cast完成,除非这种转换会抛弃恒定性或波动性。

(1)如果表达式的类型恰好是目标类型或目标类型的 cv 限定度较低的版本,则结果是表达式的值,类型为 target-type。(换句话说,dynamic_cast可以用来增加恒定性。隐式转换和static_cast也可以执行此转换。

(2)如果表达式的值是空指针值,则结果是目标类型的空指针值。

(3)如果 target-type 是指向 的指针或引用,而表达式的类型是对 的指针或引用,其中 是 的唯一、可访问的基类,则结果是指向表达式指向或标识的对象内的类子对象的指针或引用。(注意:隐式转换和static_cast也可以执行此转换。

(4)如果表达式是指向多态类型的指针,而目标类型是指向 void 的指针,则结果是指向表达式指向或引用的最派生对象的指针。

(5)如果表达式是指向多态类型的指针或引用,而目标类型是指向该类型的指针或引用,则执行运行时检查:

  1. 检查通过表达式指向/识别的最派生对象。如果在该对象中,表达式指向/引用 的公共基,并且如果只有一个类型的对象派生自表达式指向/标识的子对象,则强制转换点的结果/引用该对象。(这被称为“向下投掷”。DerivedDerivedDerived
  2. 否则,如果表达式点/引用最派生对象的公共基,同时,派生最多的对象具有明确的公共基类类型,则强制转换点的结果/引用该(这称为“侧播”)。DerivedDerived
  3. 否则,运行时检查将失败。如果在指针上使用dynamic_cast,则返回目标类型类型的空指针值。如果它用于引用,则会抛出异常 std::bad_cast。

(6)当 dynamic_cast 用于构造函数或析构函数(直接或间接)并且表达式引用当前正在构造/销毁的对象时,该对象被视为派生最多的对象。如果 target-type 不是指向构造函数/析构函数自己的类或其基之一的指针或引用,则行为是未定义的。

4.2、返回值

与其他强制转换表达式类似,结果为:

  • 向下强制转换也可以用static_cast来执行,这样可以避免运行时检查的开销,但是只有当程序能够保证(通过其他逻辑)表达式所指向的对象是确定的时候,向下强制转换才是安全的。
  • 某些形式的dynamic_cast依赖于运行时类型标识(RTTI),即关于编译程序中每个多态类的信息。编译器通常具有禁用包含此信息的选项。

4.3、示例

#include <iostream>
 
struct V
{
    virtual void f() {} // must be polymorphic to use runtime-checked dynamic_cast
};
 
struct A : virtual V {};
 
struct B : virtual V
{
    B(V* v, A* a)
    {
        // casts during construction (see the call in the constructor of D below)
        dynamic_cast<B*>(v); // well-defined: v of type V*, V base of B, results in B*
        dynamic_cast<B*>(a); // undefined behavior: a has type A*, A not a base of B
    }
};
 
struct D : A, B
{
    D() : B(static_cast<A*>(this), this) {}
};
 
struct Base
{
    virtual ~Base() {}
};
 
struct Derived: Base
{
    virtual void name() {}
};
 
int main()
{
    D d; // the most derived object
    A& a = d; // upcast, dynamic_cast may be used, but unnecessary
 
    [[maybe_unused]]
    D& new_d = dynamic_cast<D&>(a); // downcast
    [[maybe_unused]]
    B& new_b = dynamic_cast<B&>(a); // sidecast
 
    Base* b1 = new Base;
    if (Derived* d = dynamic_cast<Derived*>(b1); d != nullptr)
    {
        std::cout << "downcast from b1 to d successful\n";
        d->name(); // safe to call
    }
 
    Base* b2 = new Derived;
    if (Derived* d = dynamic_cast<Derived*>(b2); d != nullptr)
    {
        std::cout << "downcast from b2 to d successful\n";
        d->name(); // safe to call
    }
 
    delete b1;
    delete b2;
}

输出:

downcast from b2 to d successful

五、reinterpret_cast 转换

通过重新解释基础位模式在类型之间进行转换。
语法:


reinterpret_cast< new-type expression>(...)		

返回new-type类型的值。

5.1、说明

与static_cast不同,但与const_cast一样,reinterpret_cast表达式不会编译为任何 CPU 指令(除非在整数和指针之间进行转换,或者在指针表示取决于其类型的模糊体系结构上)。它纯粹是一个编译时指令,它指示编译器将表达式视为具有 new-type 类型。

只有以下转换可以使用reinterpret_cast完成,除非此类转换会抛弃恒定性或波动性。

(1)积分、枚举、指针或指向成员的指针类型的表达式可以转换为其自己的类型。结果值与表达式的值相同。

(2)指针可以转换为任何足够大的整数类型,以容纳其类型的所有值(例如,转换为 std::uintptr_t)

(3)任何整型或枚举类型的值都可以转换为指针类型。转换为足够大小的整数并返回到相同指针类型的指针保证具有其原始值,否则无法安全地取消引用生成的指针(不保证相反方向的往返转换;同一指针可能有多个整数表示形式)空指针常量 NULL 或整数零不保证产生目标类型的空指针值;为此,应使用static_cast或隐式转换。

(4)std::nullptr_t 类型的任何值,包括空PTR可以转换为任何整数类型,就好像它是(无效*)0,但没有价值,甚至没有空PTR可以转换为 std::nullptr_t"static_cast 应该用于此目的。

(5)任何对象指针类型都可以转换为另一个对象指针类型。这完全等同于T1cv T2static_cast<cv T2*>(static_cast<cv void*>(表达式))(这意味着如果 的对齐要求不比 '更严格,则指针的值不会更改,并且将生成的指针转换回其原始类型将生成原始值)。在任何情况下,只有在类型别名规则允许的情况下,才能安全地取消引用生成的指针。

(6)类型的左值(直到C++11)glvalue(自C++11)表达式可以转换为对另一种类型的引用。结果是T1T2reinterpret_cast<T2>(p)哪里p是指向表达式指定的对象的“指针指向”类型的指针。不创建临时函数,不创建副本,不调用构造函数或转换函数。只有在类型别名规则允许的情况下,才能安全地访问生成的引用(见下文)T1

(7)任何指向函数的指针都可以转换为指向不同函数类型的指针。通过指向其他函数类型的指针调用函数是未定义的,但将此类指针转换回指向原始函数类型的指针会生成指向原始函数的指针。

(8)在某些实现上(特别是在 dlsym 要求的任何 POSIX 兼容系统上),函数指针可以转换为 void* 或任何其他对象指针,反之亦然。如果实现支持双向转换,则转换为原始类型将生成原始值,否则无法取消引用或安全地调用生成的指针。

(9)任何指针类型的空指针值都可以转换为任何其他指针类型,从而生成该类型的空指针值。请注意,空指针常量空PTR或者 std::nullptr_t 类型的任何其他值不能转换为具有 reinterpret_cast: 隐式转换的指针,或者应为此目的使用static_cast。

(10)指向成员函数的指针可以转换为指向不同类型的不同成员函数的指针。转换回原始类型将生成原始值,否则无法安全地使用生成的指针。

(11)指向某个类的成员对象的指针可以转换为指向另一个类的另一个成员对象的指针。如果 的对齐方式不比 '严格,则转换回原始类型将生成原始值,否则无法安全地使用生成的指针。

5.2、返回值

与所有强制转换表达式一样,结果为:

  • 如果 new-type 是左值引用类型或对函数类型的右值引用,则为 lvalue(自 C++11 起);
  • 如果新类型是对对象类型的右值引用,则为 x值;
  • 否则为PR值。

假设满足对齐要求,则除了处理指针可相互转换对象的少数有限情况外,reinterpret_cast不会更改指针的值:

struct S1 { int a; } s1;
struct S2 { int a; private: int b; } s2; // not standard-layout
union U { int a; double b; } u = {0};
int arr[2];
 
int* p1 = reinterpret_cast<int*>(&s1); // value of p1 is "pointer to s1.a" because
                                       // s1.a and s1 are pointer-interconvertible
 
int* p2 = reinterpret_cast<int*>(&s2); // value of p2 is unchanged by reinterpret_cast
                                       // and is "pointer to s2". 
 
int* p3 = reinterpret_cast<int*>(&u);  // value of p3 is "pointer to u.a":
                                       // u.a and u are pointer-interconvertible
 
double* p4 = reinterpret_cast<double*>(p3); // value of p4 is "pointer to u.b": u.a and
                                            // u.b are pointer-interconvertible because
                                            // both are pointer-interconvertible with u
 
int* p5 = reinterpret_cast<int*>(&arr); // value of p5 is unchanged by reinterpret_cast
                                        // and is "pointer to arr"

5.3、示例

#include <cstdint>
#include <cassert>
#include <iostream>
 
int f() { return 42; }
 
int main()
{
    int i = 7;
 
    // pointer to integer and back
    std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // static_cast is an error
    std::cout << "The value of &i is " << std::showbase << std::hex << v1 << '\n';
    int* p1 = reinterpret_cast<int*>(v1);
    assert(p1 == &i);
 
    // pointer to function to another and back
    void(*fp1)() = reinterpret_cast<void(*)()>(f);
    // fp1(); undefined behavior
    int(*fp2)() = reinterpret_cast<int(*)()>(fp1);
    std::cout << std::dec << fp2() << '\n'; // safe
 
    // type aliasing through pointer
    char* p2 = reinterpret_cast<char*>(&i);
    std::cout << (p2[0] == '\x7' ? "This system is little-endian\n"
                                 : "This system is big-endian\n");
 
    // type aliasing through reference
    reinterpret_cast<unsigned int&>(i) = 42;
    std::cout << i << '\n';
 
    [[maybe_unused]] const int &const_iref = i;
    // int &iref = reinterpret_cast<int&>(
    //     const_iref); // compiler error - can't get rid of const
    // Must use const_cast instead: int &iref = const_cast<int&>(const_iref);
}

可能的输出:

The value of &i is 0x7fff352c3580
42
This system is little-endian
42

总结

  1. 静态类型转换

静态类型转换是最常用的类型转换方式,它可以将一种类型的数据强制转换为另一种类型,但需要注意的是,这种转换可能会损失一些信息,因此在进行此类转换时应当谨慎。

例如:

int a = 10;
double b = static_cast<double>(a);
  1. 动态类型转换

动态类型转换主要用于多态类型之间的转换,它可以将基类指针或引用转换为派生类指针或引用。如果进行非法的类型转换,动态类型转换会返回一个空指针。

例如:

class Base {};
class Derived : public Base {};
Base* base = new Derived;
Derived* derived = dynamic_cast<Derived*>(base);
  1. 重新解释类型转换

重新解释类型转换可以将一个对象的二进制表示重新解释为另一种类型的对象,这种转换通常用于底层编程和特殊的系统级程序中。

例如:

int a = 100;
float b = reinterpret_cast<float&>(a);
  1. const_cast转换

const_cast转换可以将const限定符添加或移除,以便在需要更改底层值时使用,但要注意的是,const_cast转换可能会导致未定义行为。

例如:

const int a = 100;
int& b = const_cast<int&>(a);

在这里插入图片描述

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

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

相关文章

【大数据之Hadoop】十八、MapReduce之压缩

1 概述 优点&#xff1a;减少磁盘IO、减少磁盘存储空间。 缺点&#xff1a;因为压缩解压缩都需要cpu处理&#xff0c;所以增加CPU开销。 原则&#xff1a;运算密集型的Job&#xff0c;少用压缩&#xff1b;IO密集型的Job&#xff0c;多用压缩。 2 压缩算法对比 压缩方式选择时…

深入浅出DPDK-1.1主流包处理硬件平台

DPDK用软件的方式在通用多核处理器上演绎着数据包处理的新篇章&#xff0c;而对于数据包处理&#xff0c;多核处理器显然不是唯一的平台。支撑包处理的主流硬件平台大致可分为三个方向&#xff1a;硬件加速器、网络处理器、多核处理器。 根据处理内容、复杂度、成本、量产规模…

【数据结构】- 链表之单链表(上)

文章目录 前言一、链表1.1链表的概念及结构1.2链表的分类 二、单链表(上)2.1单链表的实现2.2单链表实现的两种结构解析2.3单链表的接口实现2.3.1头插2.3.2温馨提醒 宝子~2.3.3头插完整版代码2.3.4尾插2.3.5温馨提醒 宝子~2.3.6总而言之 总结 前言 “偶尔失意 是为了压住翘起的…

web事件循环

事件循环的应用&#xff1a;计时器 promise ajax node 单线程是异步产生的原因&#xff0c;事件循环时异步的实现方式 1.浏览器进程模型 进程&#xff1a;程序运行需要自己专属的内存空间&#xff0c;可以把这块内存空间简单的理解为进程。 每个应用至少又一个进程&#xff…

头文件stdafx.h的作用(常见错误),以及如何在空项目中添加该头文件

头文件stdafx.h的作用&#xff08;常见错误&#xff09;&#xff0c;以及如何在空项目中添加该头文件 预编译头文件 stdafx.hfatal error C1083: 无法打开包括文件:“stdafx.h”: No such file or directoryfatal error C1083: 无法打开预编译头文件:“Debug\Win32RegistryClas…

【LeetCode】72. 编辑距离

72. 编辑距离&#xff08;困难&#xff09; 思路 状态定义&#xff1a;「dp[i][j] 表示第一个字符串到 i &#xff0c;第二个字符串到 j&#xff0c;要想使得 word1 word2 &#xff0c;最少的修改次数」。状态转移方程&#xff1a; 当第 i 位和第 j 位对应的字符相同时&#x…

【youcans 的 OpenCV 学习课】21. Haar 小波变换与 Haar 特征检测(上)

专栏地址&#xff1a;『youcans 的图像处理学习课』 文章目录&#xff1a;『youcans 的图像处理学习课 - 总目录』 【youcans 的 OpenCV 学习课】21. Haar 小波变换与 Haar 特征检测&#xff08;上&#xff09; 1. 小波变换1.1 小波变换基本概念例程 17_1&#xff1a;常用小波族…

学会这几个Word技巧,让你办公省时又省力(一)

如果在学习&#xff0c;或者工作上&#xff0c;你经常需要用到Word文档&#xff0c;那下面这几个Word技巧一定要学习收藏&#xff0c;提升工作效率不是一点点。 ​1. 快速定位对象 有时候文档的内容很长&#xff0c;涉及很多表格、图片、图表&#xff0c;想要检查一下所有的图…

linux从入门到精通 第一章centos7里tomcat,jdk,httpd,mysql57,mysql80的安装

配置centos运行环境 一 安装httpd,tomcat,jdk,mysql1 安装httpd2 安装tomcat3 安装jdk 三 MySql的安装1 克隆出来两台虚拟机2 配置虚拟机3 链接xhsell4 链接xftp5 mysql8的安装6 mysql5.7的安装 一 安装httpd,tomcat,jdk,mysql 1 安装httpd 下载httpd yum -y install httpd关…

微信小程序php+vue校园达达互助平台快递代取系统

校园快递互助平台所要实现的功能分析&#xff0c;对于现在网络方便&#xff0c;校园快递互助平台要实现管理员、学生、兼职者可以直接在平台上进行查看自己所需数据信息&#xff0c;这样既能节省管理的时间&#xff0c;不用再像传统的方式&#xff0c;如果用户想要进行交流信息…

精读《利用 GPT 解读 PDF》

hatPDF 最近比较火&#xff0c;上传 PDF 文件后&#xff0c;即可通过问答的方式让他帮你总结内容&#xff0c;比如让它帮你概括核心观点、询问问题&#xff0c;或者做观点判断。 背后用到了几个比较时髦的技术&#xff0c;还好有 ChatGPT for YOUR OWN PDF files with LangCha…

【Java实战篇】Day11.在线教育网课平台--RBAC

文章目录 一、用户授权1、RBAC2、资源服务授权流程3、授权相关的数据模型4、查询用户权限5、细粒度授权 二、找回密码与注册1、找回密码2、注册 三、需求&#xff1a;学生选课1、添加选课需求分析2、数据模型设计2、查询课程信息接口3、添加选课接口4、完善controller 一、用户…

每日一个小技巧:1招教你手机消除笔怎么用

在日常生活中&#xff0c;我们经常需要在手机上进行编辑和涂改&#xff0c;但是由于各种原因&#xff0c;我们可能会做出错误或者不满意的修改。这时候&#xff0c;消除笔就派上用场了。消除笔可以帮助我们在不影响其他内容的前提下&#xff0c;对错误或者不满意的修改进行撤销…

java实现大气无风环境污染物扩散模拟

一、扩散公式整理 二、编写java代码实现 String strJson InterpolationUtils.calGaussPlumePoints0(z,height,q,lon,lat, size,scale,airStable); return strJson.replaceAll("NaN","0").replaceAll("Infinity",String.valueOf(q)); String st…

【机器学习】P23 决策树、熵和信息增益

决策树、熵与信息增益 决策树熵信息增益Python 与 决策树 决策树 决策树&#xff08;Decision Tree&#xff09; 是一种基于树形结构的分类算法&#xff0c;它通过一系列的询问&#xff08;也称为测试或判定条件&#xff09;来判断一个数据实例属于哪个类别。 以一个案例贯穿…

Go | 一分钟掌握Go | 2 - 集成开发工具

作者&#xff1a;Mars酱 声明&#xff1a;本文章由Mars酱编写&#xff0c;部分内容来源于网络&#xff0c;如有疑问请联系本人。 转载&#xff1a;欢迎转载&#xff0c;转载前先请联系我&#xff01; 工具介绍 编码是一门传统手艺活&#xff0c;手艺好不好很重要&#xff0c;器…

高精度气象模拟软件WRF实践技术

【原文链接】&#xff1a;高精度气象模拟软件WRF(Weather Research Forecasting)实践技术及案例应用https://mp.weixin.qq.com/s?__bizMzU5NTkyMzcxNw&mid2247538149&idx3&sn3890c3b29f34bcb07678a9dd4b9947b2&chksmfe68938fc91f1a99bbced2113b09cad822711e7f…

开发者必读!常用的二维码生成器 API 推荐

引言 二维码是一种能够存储信息的图形码&#xff0c;它在现代社会中扮演着越来越重要的角色。生成二维码的过程通常需要使用二维码生成器&#xff0c;而现在有很多二维码生成器 API 可以供开发者使用。 在本文中&#xff0c;我们将讨论二维码生成器 API 的工作原理、应用场景…

CHAPTER 6: 《DESIGN A KEY-VALUE STORE》 第6章 《设计一个键值存储》

CHAPTER 6: DESIGN A KEY-VALUE STORE 键值存储(也称为键值数据库)是一种非关系数据库。每一个唯一标识符存储为与其关联值的键。这种数据配对称为“键-值”对。 在一个键-值对中&#xff0c;键必须是唯一的&#xff0c;与该键相关联的值可以是通过密钥访问。键可以是纯文本或…

编译原理个人作业--第五章——基于 编译原理 国防工业出版社 第三版

1 文法 G 1 G_1 G1​为 E → E T ∣ T T → T ∗ F ∣ F F → ( E ) ∣ i E\rightarrow ET|T\\ T\rightarrow T*F|F\\ F\rightarrow(E)|i E→ET∣TT→T∗F∣FF→(E)∣i 请证明 E T ∗ F ET*F ET∗F是他的一个句型(课本写的是ET*T感觉是印错了)&#xff0c;指出它的所有短语…