Effective C++ | 读书笔记 (二)

3、 资源管理

文章目录

      • 3、 资源管理
        • 条款 13:以对象管理资源
        • 条款 14: 在资源管理类中小心coping行为
        • 条款 15 :在资源管理类中提供对原始资源的访问
        • 条款 16 : 成对使用new和delete时要采取相同形式
        • 条款 17 :以独立语句将newed对象置入智能指针
      • 4、设计与声明
        • 条款18:让接口容易被正确使用,不容易被误用
        • 条款19 :设计class犹如设计type
        • 条款20: 宁以pass-by-reference-to-const替换pass-by-value(本条第六点要常看)
        • 条款21: 必须返回对象时,别妄想返回其reference
        • 条款22:将成员变量声明为private
        • 条款23:宁以non-member、non-friend替换member函数
        • 条款24:若所有参数皆需要类型转换,请为此采用non-member函数
        • 条款25:考虑写出一个不抛出异常的swap函数

条款 13:以对象管理资源
  1. 在一个作用域内,在delete 之前就return了,会造成内存泄漏,所以delete管理内存远远不够

    void fun()
    {
    	Investment* pInv=CreateInvestment();
    	……//这里提前 return
    	delete pInv;//释放资源
    }
    
  2. 用对象控制对象,离开了作用域自然会调用析构函数析构,比如使用智能指针auto_ptr(唯一资源使用权,对它的拷贝动作为让旧指针变为nullptr)

    1. RAII:资源获取时机即是初始化时机(resource acquisition is initialization)。获取资源后立即放进对象内进行管理。
    2. 管理对象运用析构函数确保资源释放。管理对象是开辟在栈上面的,离开作用域系统会自动释放管理对象,自然会调用管理对象的析构函数。
    3. 还有一种指针是引用计数器型指针,会记录多少个对象在使用资源,计数器为0,就释放,如share_ptr
    4. auto_ptr和shared_ptr释放资源用的都是delete,而不是delete[],对于数组指针,shared_array来对应。类似的还有scope_array

请记住:

为防止资源泄漏,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源。

两个常被使用的RAII classes 分别是tr1 :: shared_ptr和auto_ptr。前者通常是较佳选择,因为其copy行为比较直观。若选择auto_ptr,复制动作会使它(被复制物)指向null。

条款 14: 在资源管理类中小心coping行为
  1. 但是并不是所有资源都是开辟在堆上,有时候我们需要自己建立资源管理类

    class Lock{
    public:
    	explicit Lock(Mutex* mu):mutexPtr(mu)
    	{
    		lock(mutexPtr);
    	}
    	~Lock()
    	{
    		unlock(mutexPtr);
    	}
    private:
    	Mutex* mutexPtr;
    };
    

    这样客户对Lock的使用方法符合RAII方式:

    Mutex m;//定义互斥器
    ……
    {//建立区块来定义critical section
    	Lock(&m);
    	……//执行critical section 内的操作
    }//在区块末尾,自动解除互斥器的锁
    
    

    当一个RAII对象被复制,会发生什么?有以下做法

    1. 禁止复制,将coping函数设置为私有,条款6

    2. 对管理资源使用引用计数法,复制的时候就加1 。mutexPrt变为类型从Mutex*变为shared即可

      class Lockprivate Uncopyable{
      public:
      	explicit Lock(Mutex* mu):mutexPtr(mu,unlock)//以某个Mutex初始化,unlock作为删除其
      	{
      		lock(mutexPtr);
      	}
      private:
      	shared_prt<Mutex> mutexPtr;
      };
      

      注意的是在这个类中并没有自己编写析构函数。因为mutexPtr是类中的普通成员变量,编译器会自动生成析构函数类析构这样的变量。这个在条款5中有说明。

    3. 拷贝底部资源(深浅拷贝)

      使用资源管理类的目的是保证不需要这个资源时能正确释放。如果这种资源可以任意复制,我们只需编写好适当的copying函数即可。确保拷贝时是深拷贝。
      比如:C++中的string类,内部是指向heap的指针。当string复制时,底层的指针指向的内容都会多出一份拷贝。

    4. 转移底层资源的拥有权。

      有时候资源的拥有权只能给一个对象,这时候当资源复制时,就需要剥夺原RAII类对该资源的拥有权。像auto_ptr。在C++11新标准中的std::move便是这个功能。可以把一个左值转换为一个右值。

    copying函数如果你不编写,编译器会帮你合成,其合成版本行为可参考条款5。要记住的是不论是自己编写还是编译器合成,都要符合自己资源管理类的需要。

请记住:

复制RALL对象必须一并复制它所管理的资源,所以资源的copying行为决定RALL对象的copying行为

普通而常见的RALL class copying行为是:抑制copying、实行引用计数等。

条款 15 :在资源管理类中提供对原始资源的访问
  1. 原始资源,没有经过封装的指针(可以这样理解)

    //用智能指针来保存返回值
    shared_prt<Investment> pInv=(createInvestment());
    //有这样一个函数,显然是无法将只能指针对象的,这时就需要一个函数将管理的原始资源暴露出来
    int dayHeld(const Investment* pi);
    //shared_ptr和auto_ptr都提供一个get函数,用于执行这样的显示转换
    dayHeld(pInv.get());
    
  2. 为了使智能指针使用起来像普通指针一样,它们要重载指针取值(pointerdereferencing)操作符(operator->和operator*),它们允许转换至底部原始指针

  3. RAII class内的返回资源的函数和封装资源之间有矛盾。的确是这样,但这样不是什么灾难。RAII class不是为了封装资源,而是为确保资源释放。

请记住:

APIs往往要求访问原始资源(raw resources),所以每一个RAII class应该提供一个“取得其所管理之资源”的办法。

对原始资源的访问可能经由显式转换或隐式转换。一般而言显式转换比较安全,但隐式转换对客户比较方便。

条款 16 : 成对使用new和delete时要采取相同形式
  1. 如果使用new开辟内存,就使用delete释放。如果使用new[]开辟内存,就使用delete[]释放。
  2. 尽量不使用对数组做typedef动作。在C++的STL中有string、vector等templates(条款54),可以将数组需求降至几乎为零
条款 17 :以独立语句将newed对象置入智能指针
//这样写是不行的,因为shared_ptr用普通指针构造的构造函数是explict的,不允许隐式转换
int processWidget(shared_ptr<Widget> pw, int priority);
processWidget(new Widget,priority());
//这样写可以过编译但是会有资源泄露的问题
int processWidget(shared_ptr<Widget>(new Widget), int priority);
  1. 在使用智能指针时,应该用独立的语句把新创建的对象指针放入智能指针,否则可能会造成内存泄露

    //对于这个的传参
    int processWidget(shared_ptr<Widget> pw, int priority);
    
    在调用processWidget之前有三件事:
    
    1、执行priority()函数
    2、执行new Widget
    3、执行shared_ptr构造函数
    

    C++编译器会以什么样的次序来完成这些事情呢?弹性很大。在Java和C#中,总是以特定的次序来完成这样函数参数的计算,但在C++中却不一定。唯一可以确定的是new Widget在shared_ptr之前调用。但是函数priority排在第几却不一定。假设排在第二,那么顺序就是1、执行new Widget。2、执行函数priority()。3执行shared_ptr构造函数。

    如果对函数priority()调用出现异常,那么new Widget返回的指针还没来得及放入shared_ptr中。这样会造成内存泄露。

    因此可以分开写,先创建,然后在传参

    shared_prt<Widget> pw(new Widget);
    processWidget(pw,priority());
    

请记住:

以独立语句将 newed 对象存储于(置入)智能指针内。如果不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄漏。

4、设计与声明

条款18:让接口容易被正确使用,不容易被误用

1、保证参数一致性:

void print_date(int year, int month, int day)
{......}
print_date(2022, 28, 9);//1
print_date(2022, 9, 28);//2

在这样一个打印时间的函数接口中,我们按照年月日的顺序输出,但是1式却输出年日月。错误的参数传递顺序造成了接口的误用。

解决办法:

class day{...};
class month{...};
class year{...};
void pd(const year& y, const month& m, const day& d){...}

当然,传递某个有返回值的函数也是可以解决的,但这种方法看起来很奇怪。

2、保证接口行为一致性:

内置数据类型(ints, double…)可以进行加减乘除的操作,STL中不同容器也有相同函数(比如size,都是返回其有多少对象),所以,尽量保证用户自定义接口的行为一致性。

3、如果一个接口必须有什么操作,那么在它外面套一个新类型:

比如:

employee* createmp();//其创建的堆对象要求用户必须删除

如果用户忘记使用资源管理类,就有错误使用这个接口的可能,所以必须先下手为强,直接将 createmp() 返回一个资源管理对象,比如智能指针share_ptr 等等:

tr1::share_ptr<employee> createmp();

如此就避免了误用的可能性。

4、有些接口可以定制删除器,就像 STL 容器可以自定义排序,比较函数一样

tr1::share_ptr<employee> p(0, my_delete());//error! 0 不是指针
tr1::share_ptr<employee> p(static_cast<employee*>(0), my_delete());//定义一个 null 指针

第一个参数是被管理的指针,第二个是自定义删除器。

  1. 好的接口容易被正确使用,不容易被误用。
  2. 促进正确使用“的办法包括接口一致性,以及于内置类型兼容。
  3. 阻止误用“方法包括建立新类型、限制类型上的操作、束缚对象值,以及消除客户的资源管理责任。
  4. shared_ptr支持特定的删除器。可以防范cross-DLL problem,可以被用来自动解除互斥锁(就是在释放资源的时候解锁)。
    • shared_ptr一个特别好的性质是:它会自动使用它的“每个指针专属的删除器”,因而消除另一个潜在客户的错误:Corss-DLL Problem。这个问题发生于:对象在一个动态链接库DLL中被new创建,却在另一个DLL内被delete销毁。在许多平台上,这一类跨DLL之new/delete成对使用会导致运行期错误。shared_ptr没有这个问题,因为它的删除器来自其所诞生的那个DLL的delete。
条款19 :设计class犹如设计type

要注意解决以下问题:

  1. 新type的对象应该如何被创建和销毁?
  2. 对象初始化和对象赋值该有什么样的区别? 条款4
  3. 新type的对象如果被pass by value,意味着什么
  4. 什么是新type的合法值?
  5. 新type需要配合某个继承图系(inheritance graph)吗? (条款34和条款36)
  6. 新type需要什么样的转换?
  7. 什么样的操作符和函数对此新type而言是合理的?
  8. 什么样的函数应该被驳回?
  9. 谁该取用新type的成员?
  10. 什么是新type的“未声明接口”(undeclared interface)?
  11. 你的新type有多么一般化?
  12. 你真的需要一个新type吗?
条款20: 宁以pass-by-reference-to-const替换pass-by-value(本条第六点要常看)
  1. 在默认情况下,C++函数传递参数是继承C的方式,是值传递(pass by value)。这样传递的都是实际实参的副本,这个副本是通过调用复制构造函数来创建的。有时候创建副本代价非常昂贵
  2. 以pass by reference-to-const方式传递,可以回避所有构造函数和析构函数。这种方式传递,没有新对象创建,所以自然没有构造和析构函数的调用参数中,以const修饰是比较重要的,原先的pass by value,原先的值自然不会被修改。现在以pass by reference方式传递,函数validateStudent内使用的对象和传进来的同同一个对象,为了防止在函数内修改,加上const限制。
  3. 以pass by reference方式传递,还可以避免对象切割(slicing)问题。一个派生类(derived class)对象以pass by value方式传递,当被视为一个基类对象(base class)时,基类对象的copy构造函数会被调用,此时派生类部分全部被切割掉了,仅仅留下一个base class部分(因为传参的时候是base类创建的副本对象)。
  4. 对于内置类型,pass by value往往比pass by reference更高效((引用本质是指针)。所以在使用STL函数和迭代器时,习惯上都被设计出pass by value
  5. 对象小并不意味着copy构造函数代价小,许多对象(包括STL容器),内涵的成员只不过是一两个指针,但是复制这种对象时,要复制指针指向的每一样东西,这个代价很可能十分昂贵。
  6. 一般情况下,可以假设内置类型和STL迭代器和函数对象以pass by value是代价不昂贵。其他时候最好以pass by reference to const替换掉pass by value。
条款21: 必须返回对象时,别妄想返回其reference

如下这种会出现错误,因为引用只是对象的别名,返回的是局部Rational对象的别名,但是离开函数后该对象就被析构了,返回的是一个无用值,所以要返回一个值

inline const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
	return Rational(lhs.n* rhs.n, lhs.d* rhs.d);//对象析构了,引用别名也是空对象
}

inline const Rational operator*(const Rational& lhs, const Rational& rhs)
{
	return Rational(lhs.n* rhs.n, lhs.d* rhs.d);//返回一个rational对象的拷贝
}

在返回一个reference和返回一个object之间抉择时,挑出行为正确的那个。让编译器厂商为你尽可能降低成本吧!

条款22:将成员变量声明为private
  1. 封装。如果通过函数访问成员变量,日后可以用某个计算替换这个变量,这时class的客户却不知道内部实现已经变化。
  2. 将成员变量声明为private。这可以赋予客户访问数据的一致性、可细微划分访问控制、允许约束条件获得保证,并提供class作者以充分弹性实现。
  3. protected并不比public更具有封装性。
条款23:宁以non-member、non-friend替换member函数

非成员函数,非友元函数,成员函数

释义:如果一个成员函数调用了其他的成员函数,那么就要用一个非成员函数替换这个成员函数。

根据条款22,对类变量的操作只能通过类成员函数实现(因为它是私有变量),那么如果一个成员函数内部实现是调用其他的成员函数,则一个非成员函数也可以做到这样的效果:

class preson
{
public:
    void func1();
    void func2();

    void func3()
    {
        func1();
        func2();
    }

};

void use_all(const person& p)
{
    p.func1();
    p.func2();
}

func3() 和 use_all() 的效果是一样的,但这时候我们倾向于选择 use_all 函数,因为func3()作为一个成员函数,其本身也是个可以访问私有变量的函数。use_all() 函数其本身不可以访问私有变量。所以 use_all() 比 func3() 更有封装性。(能够访问私有变量的函数越少越好)

在了解这点之后,我们做一些更深层次的探讨:

我们称 use_func()(func3()的非成员函数版本)为便利函数。假设一个类有多个诸如 func1() 的函数,根据排列组合,也就有很多便利函数。为了让这些便利函数和它的类看上去更像一个整体,我们把便利函数和类放在一个 namespace 中。于是,我们可以更为轻松地拓展这些便利函数——多做一些排列组合。

若一个成员函数调用其他成员函数,那么这个成员函数的非成员函数版本比之拥有更多的封装性,和机能扩充性。

总结:

  1. 用non-member、non-friend函数替换member函数,这样可以增加封装性、包裹弹性和机能扩充性,因为不能访问私有变量。
  2. namespace可以跨越多个源码文件,class不能,将所有的便利函数放在多个头文件但隶属于同一个命名空间,意味着客户可以轻松扩展这一组遍历函数。他们需要做的是添加更多的非成员函数和非友元函数到这个命名空间内
条款24:若所有参数皆需要类型转换,请为此采用non-member函数
  1. 通常情况下,class不应该支持隐式类型转换

  2. 也有例外,比如建立一个分数管理器,允许隐式类型转换

    class Rational{
    public:
    	Rational(int numerator=0, int denominator=1);//非explicit,允许隐式转换
    	……
    };
    

    当然,若作为成员函数,this指针为隐形的参数,只需要一个变量参数传进去

    class Rational{
    public:
    	……
    	const Rational operator*(const Rational& rhs);
    	……
    };
    

    进行混合运算时

    result=oneHalf*2;//正确,相当于oneHalf.operator*(2);
    result=2*oneHalf;//错误,相当于2.operator*(oneHalf);
    

    这是错误的,2是this指向的对象,必须是该类本身的类型。这是因为

    1. 只有参数列于参数表,才是隐式类型的参与者
    2. 2不是该类型,不能调用成员函数operator *;

    因此可以定义为一个非成员函数,可以进行隐式转换的

    const Rational operator*(const Rational& lhs, const Rational& rhs);
    
  3. 总结:如果需要为某个函数的所有参数(包括this指针所指向的隐喻参数)进行类型转换,这个函数必须是个non-member函数

    另一说法:如果所有参数(运算符左边或者右边的参数)都需要类型转换,用 non-member 函数。

条款25:考虑写出一个不抛出异常的swap函数

周所周知,swap 可以交换两个数的值,标准库的 swap 函数是通过拷贝完成这种运算的。想想,如果是交换两个类对象的值,如果类中变量的个数很少,那么 swap 是有一定效率的,但如果变量个数很多呢?

你一定联想到了之前提过的,引用传递替换值传递。没错,交换两个类对象的地址就可以很有效率地完成大量变量的 swap 操作。不幸的是,标准库的 swap 并无交换对象地址的行为,所以我们需要自己写 swap 函数。

class person{...};
void my_swap(person& p1, person& p2)
{
    swap(p1.ptr, p2.ptr);
}

这个函数无法通过编译,因为类变量是 private,无法通过对象访问。所以要把它变成成员函数。

class person
{
public:
    void my_swap(person& p)
    {
        swap(this->ptr, p.ptr);
    }
    ...
};

如果你觉得 p1.my_swap(p2) 的调用形式太low了,你可以设计一个non-member 函数(如果是在同一个命名空间那就再好不过了),实现swap(p1, p2),这里不做演示。你还可以特化 std 里的 swap 函数:

namespace std
{
    template<>
    void swap<person> (person& p1, person& p2)
    {
        p1.my_swap(p2);
    }
}

值得注意的是,如果你设计的是类模板,而尝试对swap特化,那么会在 std 里发生重载,这是不允许的,因为用户可以特化 std 的模板,但不可以添加新的东西到 std 里。

还有一点:在上面工作全部完成后,如果想使用 swap ,请确定包含一个 using 声明式,一边让 std::swap 可见,然后直接使用 swap。

template<class T>
void do_something(T& a, T& b)
{
    using std::swap;
    ...
    swap(a, b);
    ...
}

其中过程:

如果T在其命名空间有专属的 swap,则调用,否则调用 std 的swap。

如果在 std 有特化的 swap,则调用,否则调用一般的 swap。(也即是拷贝)

总结

  1. 如果std::swap不高效时,提供一个swap成员函数,并且确定这个函数不抛出异常。
  2. 如果提供一个member-swap,也应该提供一个non-member swap来调用前者。对于class(非class template),要特化std::swap。
  3. 调用swap时,针对std::swap使用using形式,然后调用swap并且不带任何命名空间资格修饰。
  4. 为“用户定义类型”进行std template全特化时,不要试图在std内加入某些对std而言是全新的东西。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

考研读研生存指南,注意事项

本视频&#xff0c;涉及考研读研的方方面面&#xff0c;从考研初试→复试面试→研究生生活→导师相处→论文专利写作混毕业&#xff0c;应有尽有。有了他&#xff0c;你的研究生生涯稳了。 读研考研注意事项&#xff0c;研究生生存指南。_哔哩哔哩_bilibili 一、考研初试注意事…

“声音”音源设置和音效播放

学习如何使用音效系统&#xff0c;背景音乐和其他特别的音效&#xff0c;跳跃攻击等等 学习如何在unity当中使用整套的音效系统&#xff0c;使用之前&#xff0c;我们先来确定一下我们要使用的音乐和音效&#xff0c;在Unity Asset Store当中搜索&#xff0c;添加到我们的unit…

ICP许可证网站模板审核专用下载

ICP许可证网站模板审核专用下载 在当今的数字化时代&#xff0c;互联网的合规性变得尤为重要&#xff0c;特别是在中国。ICP许可证&#xff0c;即互联网信息服务业务经营许可证&#xff0c;是经营性网站必须持有的合法证件。为了帮助网站快速达到合规要求&#xff0c;选择合适…

出海IAA产品如何提升广告展示率?

大家好&#xff0c;我是牢鹅&#xff01;对于出海有做IAA的开发者来说&#xff0c;收益的增长至关重要。而广告收益&#xff0c;又与广告展示率息息相关。 牢鹅根据自身经验和AdMob的一些公开资料&#xff0c;总结了下面几点和提升广告展示率的方法&#xff0c;大家可以对照进…

在不支持AVX的linux上使用PaddleOCR

背景 公司的虚拟机CPU居然不支持avx, 默认的paddlepaddle的cpu版本又需要有支持avx才行,还想用PaddleOCR有啥办法呢? 是否支持avx lscpu | grep avx 支持avx的话,会显示相关信息 如果不支持的话,python运行时导入paddle会报错 怎么办呢 方案一 找公司it,看看虚拟机为什么…

数字图像处理的概念(二)

一 图像处理的概念 1 图像处理的内容 它是研究图像的获取、传输、存储、变换、显示、理解与综合利用的一门崭新学科。根据抽象程度不同可分为三个层次&#xff1a;狭义图像处理、图像分析和图像理解。如图 1.2.1 所示。 具体而言&#xff0c;数字图像处理的内容包括 图像的数…

【OceanBase探会】云与 AI 赋能一体化数据库的创新之旅

前言 哈喽&#xff0c;大家好&#xff0c;我是不叫猫先生&#xff0c;非常荣幸受邀参加2024年10月23日的「OceanBase2024年度发布会」&#xff0c;感受这场数据库技术的盛宴。 在云和 AI 时代&#xff0c;构建一体化数据库已成为现代数据架构的核心。随着数据量的激增和应用场…

Linux系统块存储子系统分析记录

1 Linux存储栈 通过网址Linux Storage Stack Diagram - Thomas-Krenn-Wiki-en&#xff0c;可以获取多个linux内核版本下的存储栈概略图&#xff0c;下面是kernel-4.0的存储栈概略图&#xff1a; 2 存储接口、传输速度 和 协议 2.1 硬盘 《深入浅出SSD&#xff1a;固态存储核心…

信息安全工程师(69)数字水印技术与应用

前言 数字水印技术是一种在数字媒体中嵌入特定信息的技术&#xff0c;这些信息可以是版权信息、元数据等。 一、数字水印技术的定义与原理 数字水印技术&#xff08;Digital Watermarking&#xff09;是将一些标识信息&#xff08;即数字水印&#xff09;直接嵌入数字载体&…

ASP.NET Core开发Chatbot API

本文介绍基于ASP.NET Core的Chatbot Restful API开发&#xff0c;通过调用大语言模型的SDK&#xff0c;完成一个简单的示例。并且通过容器化进行部署. 安装 首先需要安装.NET环境&#xff0c;笔者在Ubuntu 22.04通过二进制包进行安装&#xff0c;Windows和Mac下都有installer…

DDR Study - LPDDR Write and Training

参考来源&#xff1a;JESD209-4B&#xff0c;JESD209-4E LPDDR Initial → LPDDR Write Leveling and DQ Training → LPDDR Read and Training → LPDDR Write and Training → LPDDR Clock Switch → PIM Technical Write Command 基于JEDEC标准中可以看到Write Timing信息…

LC专题:图

文章目录 133. 克隆图 133. 克隆图 题目链接&#xff1a;https://leetcode.cn/problems/clone-graph/?envTypestudy-plan-v2&envId2024-spring-sprint-100 又一次写到这个题目&#xff0c;思路仍然不清晰&#xff0c;给我的感觉是使用递归解题&#xff0c;但是递归具体…

基于springboot企业微信SCRM管理系统源码带本地搭建教程

系统是前后端分离的架构&#xff0c;前端使用Vue2&#xff0c;后端使用SpringBoot2。 技术框架&#xff1a;SpringBoot2.0.0 Mybatis1.3.2 Shiro swagger-ui jpa lombok Vue2 Mysql5.7 运行环境&#xff1a;jdk8 IntelliJ IDEA maven 宝塔面板 系统与功能介绍 基…

雷池社区版有多个防护站点监听在同一个端口上,匹配顺序是怎么样的

如果域名处填写的分别为 IP 与域名&#xff0c;那么当使用进行 IP 请求时&#xff0c;则将会命中第一个配置的站点 以上图为例&#xff0c;如果用户使用 IP 访问&#xff0c;命中 example.com。 如果域名处填写的分别为域名与泛域名&#xff0c;除非准确命中域名&#xff0c;否…

顶点着色网格转换为 UV 映射的纹理化网格

https://dylanebert-instanttexture.hf.space/ 简介 顶点着色是一种将颜色信息直接应用于网格顶点的简便方法。这种方式常用于生成式 3D 模型的构建&#xff0c;例如InstantMesh。然而&#xff0c;大多数应用程序更偏好使用 UV 映射的纹理化网格。 InstantMeshhttps://hf.co/sp…

3D-IC——超越平面 SoC 芯片的前沿技术

“3D-IC”&#xff0c;顾名思义是“立体搭建的集成电路”&#xff0c;相比于传统平面SoC&#xff0c;3D-IC引入垂直堆叠芯片裸片&#xff08;die&#xff09;和使用硅通孔&#xff08;TSV&#xff09;等先进封装技术&#xff0c;再提高性能、降低功耗和增加集成度方面展现了巨大…

H7-TOOL的LUA小程序教程第15期:电压,电流,NTC热敏电阻以及4-20mA输入(2024-10-21,已经发布)

LUA脚本的好处是用户可以根据自己注册的一批API&#xff08;当前TOOL已经提供了几百个函数供大家使用&#xff09;&#xff0c;实现各种小程序&#xff0c;不再限制Flash里面已经下载的程序&#xff0c;就跟手机安装APP差不多&#xff0c;所以在H7-TOOL里面被广泛使用&#xff…

springboot080房屋租赁管理系统的设计与实现(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;房屋租赁管理系统的设计与实现 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好…

Java 输入与输出(I\O)之对象流与对象序列化

什么是Java的对象流&#xff1f; Java对象流是用于存储和读取基本数据类型数据或对象数据的输入输出流。 Java的对象流可分为两种&#xff1a; 1&#xff0c;对象输入流类ObjectInputStream 用于从数据源读取对象数据&#xff0c;它是可以读取基本数据类型数据或对象数据的输…

pikachu靶场CSRF-token测试报告

目录 一、测试环境 1、系统环境 2、使用工具/软件 二、测试目的 三、操作过程 1、抓包使用burp生成csrf脚本 四、源代码分析 五、结论 一、测试环境 1、系统环境 渗透机&#xff1a;本机(127.0.0.1) 靶 机&#xff1a;本机(127.0.0.1) 2、使用工具/软件 Burp sui…