「C++系列」一篇文章说透【存储类】

news2024/9/25 15:21:26

文章目录

  • 一、C++ 存储类
    • 1. 类的定义
    • 2. 对象的创建
    • 3. 对象在内存中的布局
    • 4. 对象的存储位置
  • 二、auto 存储类
    • 1. `auto`的基本用法
    • 2. `auto`与存储类的关系
      • 1) 自动存储类(最常见的)
      • 2) 静态存储类
      • 3) 动态存储类(通过new)
  • 三、register 存储类
    • 1. register 存储类的特点
    • 2. 案例
    • 3. 注意事项
  • 四、static 存储类
    • 1. 局部静态变量
    • 2. 全局静态变量
    • 3. 静态类成员变量
    • 4. 静态类成员函数
  • 五、extern 存储类
    • 1. 声明全局变量
    • 2. 声明函数
    • 3. extern "C"
    • 4. 注意事项
  • 六、mutable 存储类
    • 1. 基本用法
    • 2. 示例
    • 3. 注意事项
  • 七、thread_local 存储类
    • 1. 基本用法
    • 2. 注意事项
  • 八、相关链接

在这里插入图片描述

一、C++ 存储类

在C++中,类的存储方式主要涉及到类的定义、对象的创建以及这些对象在内存中的布局。C++是一种静态类型、编译型语言,支持面向对象的编程范式。下面详细解释这些概念:

1. 类的定义

类是C++中用户定义的类型(UDT),用于封装数据(称为成员变量或属性)和函数(称为成员函数或方法)。类的定义不会为对象分配任何内存,它只是描述了对象的结构。

class MyClass {
public:
    int x;
    void func() {
        // 成员函数实现
    }
};

2. 对象的创建

当你使用类来创建对象时,才会在内存中为对象分配空间。这个空间用于存储对象的成员变量。成员函数则不直接存储在对象中,而是存储在程序的代码段中,对象通过其成员函数指针(通常是通过隐式的this指针)来访问这些函数。

MyClass obj; // 创建一个MyClass类型的对象obj

3. 对象在内存中的布局

  • 成员变量:对象的成员变量(包括从基类继承的成员变量)按照它们在类中声明的顺序,在内存中连续存储。成员变量的对齐和填充(padding)可能会根据编译器和平台的不同而有所不同,以优化内存访问速度。
  • 成员函数:成员函数不直接存储在对象中,而是存储在程序的代码段(Code Segment)中。当对象调用成员函数时,该函数通过隐式的this指针来访问对象的成员变量。
  • 静态成员:静态成员变量在程序的全局数据段中分配一次内存,所有对象共享同一份静态成员变量的拷贝。静态成员函数则不直接访问任何对象的成员变量(除非通过对象或类名显式传递),因此它们没有this指针。

4. 对象的存储位置

对象的存储位置取决于其生命周期和作用域:

  • 自动存储期:在函数或代码块内声明的局部变量(包括对象)存储在栈(Stack)上,其生命周期仅限于定义它们的代码块。
  • 静态存储期:全局对象、静态局部变量以及静态成员变量存储在全局数据段(Global Data Segment)或静态数据段(Static Data Segment)中,它们在程序的整个执行期间都存在。
  • 动态存储期:通过new操作符动态分配的对象存储在堆(Heap)上,其生命周期由newdelete操作符控制。

二、auto 存储类

在C++中,auto 关键字不是用来直接声明存储类的,而是用来自动推导变量类型的。存储类(也称为存储期或生命周期)指的是变量在内存中保留的时间长度,常见的存储类有自动(automatic)、静态(static)、寄存器(register,但现代C++中很少使用,且其优化行为通常由编译器自动处理)、外部(extern)以及动态分配(通过new操作符)。

然而,当我们在C++中使用auto关键字时,我们是在告诉编译器自动根据初始化表达式的类型来推导变量的类型。这并不意味着auto变量就有特定的存储类;它的存储类取决于其声明的上下文。

1. auto的基本用法

下面是一个使用auto的简单案例,它演示了如何自动推导变量的类型,但并没有直接涉及到存储类的概念:

#include <iostream>
#include <vector>

int main() {
    // 自动推导为int类型
    auto a = 10;
    std::cout << "a is: " << a << std::endl;

    // 自动推导为std::vector<int>类型
    std::vector<int> v = {1, 2, 3, 4, 5};
    auto b = v; // 注意:这里b是v的一个拷贝,而不是引用
    std::cout << "b has " << b.size() << " elements." << std::endl;

    return 0;
}

2. auto与存储类的关系

尽管auto本身不直接声明存储类,但auto声明的变量可以根据其声明的上下文获得不同的存储类。以下是一些例子:

1) 自动存储类(最常见的)

void func() {
    auto x = 10; // x具有自动存储类,因为它是在函数内部声明的局部变量
    // ...
} // x的作用域结束,x的生命周期也结束

2) 静态存储类

static auto y = 20; // y具有静态存储类,尽管它是用auto声明的
// y的生命周期贯穿整个程序执行期间,但其作用域可能受限于声明它的块或文件

void func() {
    // 可以在这里访问y
}

注意:在全局或命名空间作用域中使用static会改变链接性(linkage),但在函数内部使用时,它主要影响生命周期和初始化时机(只初始化一次)。

3) 动态存储类(通过new)

虽然auto通常不直接与动态分配相关联,但你可以使用auto来接收new表达式的结果:

auto ptr = new int(30); // ptr是指向int的指针,具有自动存储类
// 但它指向的对象是通过new动态分配的,具有动态存储类
// 记得用delete ptr;来释放内存

在这个例子中,ptr本身是一个具有自动存储类的局部变量,但它指向的对象是通过new动态分配的,因此该对象具有动态存储类。

总结:auto用于自动推导变量类型,而变量的存储类取决于其声明的上下文(如是否在函数内部、是否使用了static关键字、是否通过new动态分配等)。

三、register 存储类

在C++中,register 存储类是一个特殊的说明符,用于向编译器建议将变量存储在CPU的寄存器中,以便快速访问。然而,需要注意的是,register 关键字仅仅是一个建议,并不保证变量一定会被存储在寄存器中。此外,从C++11开始,register 关键字被废弃,现代编译器通常会忽略它,因为它们有自己的优化机制来决定变量的存储位置。

1. register 存储类的特点

  1. 建议性register 关键字告诉编译器,程序员认为该变量应该被频繁访问,因此建议将其存储在寄存器中。但编译器可以忽略这个建议。
  2. 局部变量register 只能用于局部变量,不能用于全局变量或静态变量。
  3. 限制:使用 register 的变量不能被取地址(即不能应用一元 & 运算符),因为它可能没有内存地址。
  4. 尺寸限制register 变量的最大尺寸通常等于寄存器的大小(通常是一个词或几个词)。

2. 案例

尽管 register 关键字在现代C++编程中不再被推荐使用,但以下是一个简单的例子,展示了如果编译器支持 register 关键字时它的用法:

#include <iostream>

int main() {
    register int counter = 0; // 建议编译器将counter存储在寄存器中
    for (int i = 0; i < 1000000; i++) {
        counter++; // 假设counter被存储在寄存器中,这可能会提高循环的性能
    }
    std::cout << "Counter: " << counter << std::endl;
    return 0;
}

然而,在现代C++编译器中,即使你使用了 register 关键字,编译器也可能会忽略它,因为它有自己的优化策略来决定哪些变量应该被存储在寄存器中。

3. 注意事项

  • 废弃:由于 register 关键字在C++11中被废弃,因此在新的C++代码中应避免使用它。
  • 性能优化:如果你关心性能,并且想要优化特定变量的访问速度,最好让编译器自己处理这些优化,或者通过其他方式(如使用更高效的算法或数据结构)来提高程序性能。
  • 代码可读性:使用 register 关键字可能会降低代码的可读性,因为它引入了一个不再被现代编译器广泛支持的特性。

综上所述,虽然 register 存储类在理论上可以用于优化变量访问速度,但在现代C++编程中,它已经不再是一个有用的特性,应该被避免使用。

四、static 存储类

在C++中,static存储类是一个非常重要的特性,它用来控制变量的存储方式和可见性。以下是static存储类的详细解释:

1. 局部静态变量

static用于函数内的局部变量时,它会使该变量具有静态存储期,即变量在程序的整个执行期间都存在,但只在定义它的作用域内可见。这样的变量只初始化一次,每次函数调用时保留其最后一次使用的值。

特点

  • 存储在静态存储区。
  • 只在定义它的函数内可见。
  • 即使函数调用结束,也不会销毁。
  • 只初始化一次。

示例

#include <iostream>
using namespace std;

void countFunction() {
    static int count = 0; // 局部静态变量
    count++;
    cout << "Count: " << count << endl;
}

int main() {
    countFunction(); // 输出 1
    countFunction(); // 输出 2
    countFunction(); // 输出 3
    return 0;
}

2. 全局静态变量

在函数外部使用static声明的全局变量或函数,会限制其链接性(可见性),使其只在定义它的文件中可见。这有助于避免不同源文件之间的命名冲突。

特点

  • 存储在全局数据段。
  • 只在其定义的文件内可见。
  • 在程序的整个执行期间都存在。

示例

// file1.cpp
#include <iostream>
static int globalValue = 10;
void display() {
    std::cout << "Global Value in file1: " << globalValue << std::endl;
}

// file2.cpp
// extern int globalValue; // 如果取消注释,则会导致编译错误,因为globalValue在file2中不可见

// 假设有main函数在file2.cpp或其他地方
// 调用display函数会正常显示file1中的globalValue值

3. 静态类成员变量

在类中声明static变量时,该变量不属于任何特定的对象实例,而是类的所有实例共享的。它们必须在类外部初始化。

特点

  • 存储在静态存储区。
  • 类的所有对象共享同一份拷贝。
  • 初始化时使用作用域运算符(::)来标明它所属类。

示例

#include <iostream>
using namespace std;

class MyClass {
public:
    static int staticValue;
};

int MyClass::staticValue = 0; // 初始化

int main() {
    MyClass obj1, obj2;
    obj1.staticValue = 5;
    cout << "Static Value from obj1: " << obj1.staticValue << endl; // 输出 5
    cout << "Static Value from obj2: " << obj2.staticValue << endl; // 输出 5
    return 0;
}

4. 静态类成员函数

静态成员函数与静态数据成员一样,都是类的内部实现,属于类定义的一部分。它们可以在没有对象实例的情况下调用,并且只能访问其类的静态成员。

特点

  • 没有this指针。
  • 只能访问类的静态成员变量和静态成员函数。

示例

#include <iostream>
using namespace std;

class MyClass {
public:
    static int staticValue;
    static void displayValue() {
        cout << "Static Value: " << staticValue << endl;
    }
};

int MyClass::staticValue = 10;

int main() {
    MyClass::displayValue(); // 输出 10
    return 0;
}

五、extern 存储类

在C++中,extern关键字是一个非常重要的存储类修饰符,它主要用于声明一个全局变量或函数,这些变量或函数是在其他文件中定义的。下面详细解释extern存储类的用法和特点:

1. 声明全局变量

当我们在多个文件中使用同一个全局变量时,extern可以帮助我们实现这一目标。通过extern,我们可以在一个文件中声明该全局变量(而不是定义它,即不分配内存),然后在其他文件中使用它。这样做的好处是避免了在多个文件中重复定义同一个全局变量可能导致的编译错误。

示例

假设我们有两个文件,main.cppsupport.cpp

  • support.cpp中定义一个全局变量:
int testVar = 0; // 定义一个全局变量
  • main.cpp中,我们通过extern来声明这个全局变量,以便使用它:
extern int testVar; // 声明外部变量

int main() {
    testVar = 10; // 使用外部变量
    // ...
}

2. 声明函数

除了变量之外,extern也可以用于声明在其他文件中定义的函数。这样,我们就可以在多个文件中调用同一个函数,而不需要在每个文件中都重复函数的定义。

示例

  • support.cpp中定义一个函数:
void testFunc() {
    // 函数体
}
  • main.cpp中,我们通过extern来声明这个函数,以便调用它:
extern void testFunc(); // 声明外部函数

int main() {
    testFunc(); // 调用外部函数
    // ...
}

然而,需要注意的是,在C++中,对于函数的声明,extern通常是隐式的,即我们不需要显式地写出extern来声明一个函数是在其他文件中定义的。但在某些特殊情况下,为了强调或明确函数的外部链接性,也可以显式地使用extern

3. extern “C”

在C++中,我们还会经常看到extern "C"的用法。这是因为C++支持函数重载,而C语言不支持。当C++代码需要被C语言代码调用时,为了避免链接错误(即C++编译器对函数名进行名称修饰,而C编译器不进行),我们需要用extern "C"来告诉C++编译器按照C语言的方式来链接这些函数。

示例

extern "C" void testFunc() {
    // 函数体
}

或者,在头文件中声明时:

#ifdef __cplusplus
extern "C" {
#endif

void testFunc();

#ifdef __cplusplus
}
#endif

4. 注意事项

  • extern只是声明,不是定义。它告诉编译器变量或函数的类型,但不会分配内存。
  • extern声明的变量或函数必须在某个文件中被定义,否则在链接阶段会报错。
  • extern只能用于全局变量和函数,不能用于函数内部的局部变量。
  • 在C++中,对于函数的声明,extern通常是隐式的,但对于全局变量或需要明确指出外部链接性的函数,extern是必需的。

六、mutable 存储类

在C++中,mutable关键字是一个特殊的存储类修饰符,它用于类的成员变量上,以允许这个成员变量即使在常量成员函数(即被const修饰的成员函数)中也能被修改。这个特性在需要缓存计算结果、跟踪对象状态变化等场景中非常有用,特别是当这些变化不应该影响对象的逻辑常量性时。

1. 基本用法

当你将mutable关键字应用于类的成员变量时,这个成员变量即使在常量成员函数中也可以被修改。这意呀着,常量成员函数(即被声明为const的成员函数)可以修改这个mutable成员变量的值,而不会违反const成员函数不应修改任何成员变量的常规规则。

2. 示例

#include <iostream>

class MyClass {
private:
    mutable int cache; // 使用mutable修饰,允许在const成员函数中被修改
    int value;

public:
    MyClass(int v) : value(v), cache(-1) {} // 初始化value,cache初始化为-1表示未计算

    // 常量成员函数,但可以修改mutable成员变量cache
    int getValue() const {
        if (cache == -1) { // 如果cache未被计算
            cache = computeValue(); // 计算并存储结果
        }
        return cache;}
    

    // 非const成员函数,可以修改所有成员变量
    void setValue(int v) {
        value = v;
        cache = -1; // 重置cache,因为value的改变可能影响计算结果
    }

private:
    // 假设这是一个耗时的计算过程
    int computeValue() const {
        // ... 进行计算 ...
        return value * 2; // 仅为示例
    }
};

int main() {
    MyClass obj(5);
    std::cout << "Value: " << obj.getValue() << std::endl; // 输出:Value: 10
    obj.setValue(10);
    std::cout << "New Value: " << obj.getValue() << std::endl; // 输出:New Value: 20
    return 0;
}

3. 注意事项

  • mutable只能用于类的成员变量上。
  • 使用mutable要谨慎,因为它可能会让代码的阅读者感到困惑,特别是当常量成员函数实际上修改了某些状态时。因此,建议只在确实需要时才使用mutable,并且确保在文档中清楚地说明这一点。
  • mutable成员变量通常用于缓存计算结果、状态跟踪等场景,这些场景中的变化不会影响对象的逻辑常量性。
  • mutable成员变量不会改变常量成员函数的const性质,即常量成员函数仍然不能修改类的非mutable成员变量。

七、thread_local 存储类

在C++11及以后的版本中,thread_local关键字被引入作为一个存储类修饰符,用于声明线程局部存储(Thread-Local Storage, TLS)的变量。这意味着每个线程都有该变量的一个独立实例,线程之间对这些变量的修改是互不影响的。

1. 基本用法

当你在变量声明前加上thread_local关键字时,这个变量就变成了线程局部的。每个线程对这个变量的访问都指向它自己的独立实例。

#include <iostream>
#include <thread>

thread_local int tls_var = 0; // 线程局部变量

void increment() {
    tls_var++; // 每个线程都会增加它自己的tls_var实例
    std::cout << "Thread " << std::this_thread::get_id() << " sees tls_var as " << tls_var << std::endl;
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    increment(); // 主线程也会执行

    t1.join();
    t2.join();

    return 0;
}

在这个例子中,每个线程(包括主线程)都会打印出它自己的tls_var值,这些值在每次调用increment函数时都会递增,但每个线程看到的都是它自己的独立副本。

2. 注意事项

  • thread_local变量在其所属线程开始时被构造,在线程结束时被销毁。这意味着你不能在全局作用域中直接初始化一个需要构造函数和析构函数的thread_local变量,因为全局变量的构造和析构发生在程序开始和结束时,而这时线程可能还没有开始或已经结束。
  • thread_local变量不会跨线程共享,这意呀着每个线程对其的修改都不会影响到其他线程。
  • 在某些平台上,thread_local变量的实现可能依赖于动态内存分配,这可能会引入额外的性能开销。此外,如果线程数量非常多,可能会消耗大量的内存。
  • 在使用thread_local时,要特别注意内存泄漏和异常安全问题。如果thread_local变量在析构时抛出异常,并且这个异常没有被捕获,那么程序可能会以未定义的方式终止。
  • 并不是所有的C++实现都支持thread_local,但它被C++11标准所要求,因此大多数现代编译器都支持它。

八、相关链接

  1. Visual Studio Code下载地址
  2. Sublime Text下载地址
  3. 「C++系列」C++简介、应用领域
  4. 「C++系列」C++ 基本语法
  5. 「C++系列」C++ 数据类型
  6. 「C++系列」C++ 变量类型
  7. 「C++系列」C++ 变量作用域
  8. 「C++系列」C++ 常量知识点-细致讲解
  9. 「C++系列」C++ 修饰符类型

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

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

相关文章

自学第十五天----深入理解函数上

1. 函数是什么&#xff1f; 维基百科中对函数的定义&#xff1a; 子程序 在计算机科学中&#xff0c;子程序&#xff08;英语&#xff1a;Subroutine, procedure, function, routine, method, subprogram, callable unit&#xff09;&#xff0c;是一个大型程序中的某部分代码…

【2-1:RPC设计】

RPC 1. 基础1.1 定义&特点1.2 具体实现框架1.3 应用场景2. RPC的关键技术点&一次调用rpc流程2.1 RPC流程流程两个网络模块如何连接的呢?其它特性RPC优势2.2 序列化技术序列化方式PRC如何选择序列化框架考虑因素2.3 应用层的通信协议-http2.3.1 基础概念大多数RPC大多自…

windows上修改redis端口号

概况 redis是一个开源的内存数据结构存储系统&#xff0c;常用做数据库、缓存和消息代理。默认的端口号为6379 更改redis端口号步骤如下 先停止redis服务 redis-cli shutdowm 打开redis配置文件 在redis安装目录下&#xff0c;即redis.windows.conf文件。 port 6396 然后…

插片式远程 I/O模块:热电阻温度采集模块与PLC配置案例

XD系列成套系统主要由耦合器、各种功能I/O模块、电源辅助模块以及终端模块组成。有多种通讯协议总线的耦合器&#xff0c;例如Profinet、EtherCAT、Ethernet/IP、Cclink IE以及modbus/TCP等。I/O 模块可分为多通道数字量输入模块、数字量输出模块、模拟量输入模块、模拟量输出模…

js前端隐藏列 并且获取值,列表复选框

列表框 <div class"block" id"psi_wh_allocation_m"><table id"result" class"list auto hover fixed" style"width:100%;border-collapse:collapse"><thead><tr><%--<th></th>--%&…

人类大脑的计算与机器的类脑计算

人类大脑的计算基本原理涉及到神经元的基本工作方式、神经网络的结构和连接模式、信息传递的方式、学习和记忆的机制等多个层面的复杂互动&#xff0c;这些原理的深入理解不仅有助于神经科学的发展&#xff0c;还为人工智能领域的发展提供了重要的启示和指导。人类大脑计算基本…

【JavaScript 报错】未捕获的加载错误:Uncaught LoadError

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、错误原因分析1. 资源路径错误2. 资源不存在3. 网络问题 二、解决方案1. 检查资源路径2. 确保资源存在3. 处理网络问题 三、实例讲解四、总结 在JavaScript应用程序中&#xff0c;未捕获的加载错误&#xff08;Uncaught …

电脑录音如何操作?电脑麦克风声音一起录制,分享7款录音软件

电脑录音已经成为我们日常生活和工作中不可或缺的一部分。无论是录制会议、教学、音乐、网络直播、音源采集还是其他声音&#xff0c;电脑录音软件都为我们提供了极大的便利。本文将为大家介绍如何操作电脑录音&#xff0c;并分享七款录音软件&#xff0c;包括是否收费、具体操…

OpenCV中的浅拷贝和深拷贝

文章目录 前言一、浅拷贝二、深拷贝三、比较总结 前言 在数字图像处理中&#xff0c;针对读取到的一张图像&#xff0c;需要反复利用这张图像做各种的变换&#xff0c;以满足我们项目的需求。在这之前&#xff0c;最容易忽略的一点就是图像之间的拷贝问题&#xff0c;其中的浅…

【JavaScript 报错】未捕获的类型错误:Uncaught TypeError

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、错误原因分析1. 调用不存在的方法2. 访问未定义的属性3. 数据类型不匹配4. 函数参数类型不匹配 二、解决方案1. 检查方法和属性是否存在2. 使用可选链操作符3. 数据类型验证4. 函数参数类型检查 三、实例讲解四、总结 在…

Java高级重点知识点-25-Stream流、方法引用

文章目录 Stream流流式思想概述获取流常用方法 方法引用方法引用符通过对象名引用成员方法通过类名称引用静态方法通过super引用成员方法通过this引用成员方法类的构造器引用数组的构造器引用 Stream流 通过循环遍历来讲解流的优势&#xff1b; 要求&#xff1a;筛选所有姓张的…

游戏如何应对黑灰产工作室

游戏黑灰产工作室&#xff0c;是指以非法渠道、非法手段通过游戏进行牟利的团伙。使用脚本、外挂是黑灰产工作室的显著特征&#xff0c;其常见的牟利方式有&#xff1a;打金工作室、资源囤积号、初始号、自抽号、代练工作室以及营销欺诈等。 ▲ 常见的游戏黑灰产工作室牟利路径…

正向传播和反向传播

正向传播&#xff08;Forward Propagation&#xff09; 正向传播是指将输入数据通过神经网络&#xff0c;计算出预测值的过程。具体步骤如下&#xff1a; 输入层&#xff1a;接受输入数据。隐藏层&#xff1a;每个隐藏层中的神经元接收上一层的输出&#xff0c;进行加权求和&…

ENSP防火墙综合配置

综合拓扑&#xff1a; 实验要求&#xff1a; 要求一 生产区的安全策略配置 办公区的安全策略 要求二 生产区的安全策略 游客和办公区的安全策略 因为ISP返回的数据包会被防火墙最后的默认安全策略给拒绝&#xff0c;所以&#xff0c;把要ISP返回的数据给允许通过 要求三 增加…

Lingo学习(三)——工厂合并、运算符、内置函数

一、工厂合并 &#xff08;一&#xff09; 工厂合并——生产二维矩阵 【引入】 sets: factory /1..6/ : a; plant /1..8/ : d; Cooperation(factory,p lant) : c, x; endsets 以上程序可…

Go语言入门之Map详解

Go语言入门之Map详解 1.基础定义 map是一个无序的&#xff0c;k-v键值对格式的集合 &#xff08;1&#xff09;特点 类型特点&#xff1a;map为引用类型&#xff0c;所以在函数中更新value值会永久改变顺序特点&#xff1a;map的遍历是无序的&#xff0c;因为底层是哈希表&am…

按下快门前的算法——对焦

对焦算法可以分为测距式&#xff0c;相位式&#xff0c;反差式。 其中测距式是通过激光&#xff0c;&#xff08;TOF&#xff0c;Time of Flight&#xff09;等主动式地得知物距&#xff0c;然后对焦。更常用的是后两者。 反差式CDAF&#xff08;Contrast Detection Auto Foc…

微调及代码

一、微调&#xff1a;迁移学习&#xff08;transfer learning&#xff09;将从源数据集学到的知识迁移到目标数据集。 二、步骤 1、在源数据集&#xff08;例如ImageNet数据集&#xff09;上预训练神经网络模型&#xff0c;即源模型。 2、创建一个新的神经网络模型&#xff…

【Python实战因果推断】31_双重差分2

目录 Canonical Difference-in-Differences Diff-in-Diff with Outcome Growth Canonical Difference-in-Differences 差分法的基本思想是&#xff0c;通过使用受治疗单位的基线&#xff0c;但应用对照单位的结果&#xff08;增长&#xff09;演变&#xff0c;来估算缺失的潜…

PostgreSQL 中如何处理数据的批量更新和事务日志管理?

文章目录 PostgreSQL 中数据的批量更新和事务日志管理 PostgreSQL 中数据的批量更新和事务日志管理 在数据库的世界里&#xff0c;数据的批量更新和事务日志管理就像是一场精心编排的舞蹈&#xff0c;需要精准的步伐和协调的动作。对于 PostgreSQL 而言&#xff0c;这两个方面…