C++:组合、继承与多态

面向对象设计的重要目的之一就是代码重用,这也是C++的重要特性之一。代码重用鼓励人们使用已有的,得到认可并经过测试的高质量代码。多态允许以常规方式书写代码来访问多种现有的且已专门化了的相关类。继承和多态是面向对象程序设计方法的两个最主要的特性。继承可以将一群相关的类组织起来,并共享它们之间的相同数据和操作行为;多态使程序员在这些类上编程时,就像在操作一个单一体,而非相互独立的类,并且可以有更多灵活性来加入和删除类的一些属性或方法。
在C++中可以用类的方法解决代码重用,通过创建新类重用代码,而不是从头创建,这样可以使用其他人已经创建并调试过的类,其关键是使用类而不是更改已存在的代码。下面将介绍两个方法:第一种方法是简单的创建一个包含已存在的类对象的新类称为组合,因为这个新类是由于存在类的对象组合的;第二种方法是创建一个新类作为一个已存在类的类型,采用这个已存在类的形式,只对它增加代码,但不修改,这种方法称为继承,其中大量的工作有编译器完成。继承是面向对象程序设计的核心。

组合

对于比较简单的类,其数据成员对位基本数据类型,但对于某些复杂的类来说,其某些数据成员可能又是另一些类的类型,这就形成了类的组合(聚集)。
例如:一类方式板寸某个班级的名称、人数以及每个学生的学号、姓名、学习成绩(省略类Student

//MyClass.h
#ifndef MYCLASS_H
#define MYCLASS_H_
#include "student.h"
class MyClass
{enum{NUM = 50};char Name[20];int Num;Student stuList[NUM];
public:MyClass();const char* GetClassName();const char* GetStuName(int No);
};#endif // !MYCLASS_H//MyClass.cpp
#include "MyClass.h"
#include <cstddef>
MyClass::MyClass()
{Num = 0;
}inline const char* MyClass::GetClassName()
{return Name;
}const char* MyClass::GetStuName(int No)
{for (int i = 0; i < NUM; i++){if (stuList[i].GetNo() == No)return stuList[i].GetName();}return NULL;
}

若嵌入的对象是公有的,也可以用“多级”访问。

继承

类的继承就是新类从已有类中获得以有的属性和行为,或者说就是从基类派生出既有基类特征又有新特征的派生类。
创建新类,但不是从头创建,开源使用其他人已经创建并调试过的类。关键是使用类,而不是更改已存在的代码。
类继承格式为:class 子类名:[pubilc|private|protected]父类名{···}
子类(派生类)具有父类(基类)的所有属性和行为,且可以增加新的行为和属性。
C++提供了三种继承的方式:公有(public)、受保护(protected)和私有(private)。

  1. 公有继承
    a. 基类的private、public和protected成员的访问属性在派生类中保持不变。
    b.派生类中继承的成员函数可以直接访问基类中的所有成员,派生类中新增二点成员函数只能访问基类的public和protected成员,不能访问基类的private成员。
    c.通过派生类的对象只能访问基类的public成员。
  2. 受保护继承
    a.基类的public和protected成员都已protected身份出现在派生类中。
    b.派生类中新增的成员函数可以直接访问基类中的public和protected成员,但不能访问基类中private成员。
    c.通过派生类的对象不能访问基类中的任何成员。
  3. 私有继承
    a.基类的public和protected成员都已private身份出现在派生类中。
    b.派生类中新增的成员函数可以直接访问基类中的任何成员。
    c.通过派生类的对象不能访问基类中的任何成员。

继承与组合

在实际工作中往往一个新类中既有从已有类中继承的,也有由其他类组合的,这需要把组合和继承放在一起使用。
组合通常希望新类内部有已存在类性能时使用,而不希望已存在类作为其接口。也就是说,嵌入 一个计划用于实现新类性能的对象,而新类的用户看到的时新定义的接口,而不是来自父类的接口。
继承是取一个已存在的类,并制作它的一个专门的版本。通常,这意味着取一个一般目的的类,并为特殊的需要对它进行专门化。

继承与组合中的构造和析构

成员对象初始化

对于继承,应在冒号之后和这个类体的左花括号“{”之前放基类。而在过早函数的初始化表达式中,可以将对子对象构造函数的调用语句放在构造函数参数表和冒号之后,在函数体的左花括号“{”之前。
对于组合应给出对象的名字而不是类名。若在初始化表达式中有多于一个的构造函数调用,应当用逗号隔开,例如:

Student::Student(const char*Name = NULL,int Age = 0, char Sex = 'm', int No = 0):Person(CpName,Age,Sex),No(No),Ave(0){···}

需要注意的是,这里对基本数据类型的初始化工作成为“伪构造函数”,甚至可以应用到类外:int i(10);

构造和析构顺序

对于析构函数来说,执行次序与构造函数相反,系统调用构造函数生成对象,调用析构函数释放对象所占用的内存空间。当采用继承方式创建子类对象时,先从父类开始执行构造函数->父类的成员->执行子类的构造函数->子类成员;当撤销子类对象时,执行相反的顺序,即先撤销子类的成员->执行子类的析构函数->撤销父类成员->执行父类的析构函数。
例如:在采用继承方式生成的类中,构造函数与析构函数的调用顺序。

#include <iostream>
using namespace std;
class A
{int a;
public:A(int i = 0) :a(i){cout << "A is constructed" << endl;}~A(){cout << "A is destructed" << endl;}
};class B:public A
{int b;
public:B(int i = 0) :b(i){cout << "B is constructed" << endl;}~B(){cout << "B is destructed" << endl;}
};int main()
{B b;return 0;
}

程序运行结果:

A is constructed
B is constructed
B is destructed
A is destructed

相对于基类和派生类,若类中有静态数据成员,构造顺序又是怎样?仍然时“仿生”自然界的顺序,即先父类再子类,按照属性成员声明的先后顺序进行构造:

  1. 调用基类的构造函数;
  2. 根据类中声明的顺序构造函数组合对象;
  3. 派生类中构造函数的执行。
    派生类构造函数的格式为:
class 派生类:[public|private|protested]基类名
{public:派生类名(参数列表1):基类名(参数列表2),组合对象列表{···}
};

析构函数的顺序正好相反。
例如:组合类中构造函数与析构函数的调用顺序

#include <iostream>
using namespace std;
class X
{
public:X(){cout << "X is constructed" << endl;}~X(){cout << "X is destructed" << endl;}
};class A
{int a;X x;//组合对象
public:A(int i = 0) :a(i)//基类构造函数{cout << "A is constructed" << endl;}~A()//基类析构函数{cout << "A is destructed" << endl;}
};class Y
{int y;
public:Y(int i = 0){y = i;cout << "Y is constructed" << endl;}~Y(){cout << "Y is destructed" << endl;}
};class Z
{int z;
public:Z(int i = 0){z = i;cout << "Z is constructed" << endl;}~Z(){cout << "Z is destructed" << endl;}
};class B:public A
{int b;Y y;//派生类组合对象Z z;
public:B(int i = 0) :A(1), b(i), z(i), y(i)//派生类构造函数的后面为内嵌对象列表{cout << "B is constructed" << endl;}~B()//派生析构函数{cout << "B is destructed" << endl;}
};int main()
{B b;return 0;
}

程序运行结果为:

X is constructed
A is constructed
Y is constructed
Z is constructed
B is constructed
B is destructed
Z is destructed
Y is destructed
A is destructed
X is destructed

注意:

  1. 派生类不能继承基类的构造函数和析构函数,当基类有带参数的构造函数时,则派生类必须定义构造函数,以便把参数传递给基类构造函数
  2. 当派生类也作为基类使用时,则各派生类子负责其直接的基类构造。
  3. 因为析构函数不带参数,所以派生类中析构函数的存在不依赖于基类,基类中析构函数的存在也不依赖于派生类。
    例如:继承类中构造函数与析构函数的调用顺序。
#include <iostream>
using namespace std;
class A
{int a;
public:A(int i = 0) :a(i){cout << "A is constructed" << endl;}~A(){cout << "A is destructed" << endl;}
};class Y
{int y;
public:Y(int i = 0){y = i;cout << "Y is constructed" << endl;}~Y(){cout << "Y is destructed" << endl;}
};class B:public A
{int b;Y y;
public:B(int i = 0) :b(i), y(i){cout << "B is constructed" << endl;}~B(){cout << "B is destructed" << endl;}
};class C:public B
{int c;
public:C(int i = 0) :B(1), c(i){cout << "C is constructed" << endl;}~C(){cout << "C is destructed" << endl;}
};int main()
{C c(2);return 0;
}

程序运行结果为:

A is constructed
Y is constructed
B is constructed
C is constructed
C is destructed
B is destructed
Y is destructed
A is destructed

名字覆盖

若再基类中有一个函数名被重载多次,在派生类中又重定义了这个函数名,则在派生类中会掩盖这个函数的所有基类定义。也就是说,通过派生类来访问该函数时,由于采用就近匹配的原则,只会调用在派生类中所定义的该函数,基类中所定义的函数都变得不再可用。
若要访问基类中声明的函数,则有以下方法:

  1. 使用作用域标识符限定。
  2. 避免名称覆盖。

虚函数

多态是面向对象程序设计的重要特性,重载和虚函数是体现多态的两个重要手段。虚函数体现了多态的灵活性,可进一步减少冗余信息,显著提高软件的可扩充性。
学习函数重载与继承的方法后,经常会遇见到下面问题,在派生类中存在对基类函数的重载,当通过派生类对象调用重载函数时却调用了基类中的原函数。
例如:通过派生类对象简介调用重载函数

#include <iostream>
using namespace std;
class A
{
public:void play() const{cout << "A::play" << endl;}
};class B:public A
{
public:void play() const{cout << "B::play" << endl;}
};void tune(A& i)
{i.play();
}int main()
{B b;tune(b);return 0;
}

程序运行结果为:

A::play

可以看出,输出结果并不是我们想要的B::play,而是A::play。显然,这不是所希望的输出结果,因为这个对象实际上就是B类型,而不只是A类型。C++类型虽然语法检验很严格,但是函数tune(通过引用)接受一个A类型的对象,也不拒绝任何从A派生的类对象。为了理解这个问题引入下面的概念。

虚函数的定义

虚函数定义格式为:

class 基类名
{virtual 返回值类型 将要在派生类中重载的函数名(参数列表);
};

例如,用虚函数修改上例

#include <iostream>
using namespace std;
class A
{
public:virtual void play() const{cout << "A::play" << endl;}
};class B:public A
{
public:void play() const{cout << "B::play" << endl;}
};class C:public B
{
public:void play() const{cout << "C::play" << endl;}
};void tune(A& i)
{i.play();
}int main()
{B b;tune(b);C c;tune(c);A* p = &b;tune(*p);p = &c;tune(*p);A a;tune(a);return 0;
}

程序运行结果为:

B::play
C::play
B::play
C::play
A::play

不管有多少层,虚函数都能得到很好的应用。因为一旦在基类中声明为虚函数,就可根据类对象的类型动态决定调用基类或派生类的函数。
使用虚函数需要注意以下5点:

  1. 在基类中声明虚函数,即需要在派生类中重载的函数,必须在基类中声明。
  2. 虚函数一经声明,在派生类中重载的基类的函数即是虚函数,不在需要加virtual
  3. 只有非静态成员函数可以声明为虚函数,静态成员函数和全局函数不能声明为虚函数。
  4. 编译器把名称相同、参数不同的函数看作不同的函数。基类和派生类中名字相同但参数不同的函数,不需要声明为虚函数。
  5. 普通对象调用虚函数时,系统仍然以静态绑定方式调用函数。因为编译器编译时能确切地知道对象的类型,且能确切地调用其成员函数。

虚析构函数

通过了解虚函数可以掌握虚函数在继承和派生中的调用方式。那么类的两种特殊的函数——构造函数和析构函数是否可以声明为虚函数呢?

  1. 构造函数不能声明为虚函数。因为构造函数有其特殊的工作,它处在对象创建初期,先要调用基类构造函数,然后调用按照继承顺序派生的派生类的构造函数。
  2. 析构函数能够且经常是虚函数。系后汉书调用顺序与构造函数完全相反,从最晚派生类开始,以此向上到基类。因此,析构函数确切地知道它是从按个类派生而来的。
    虚析构函数声明格式为:virtual~析构函数名称();

虚函数的目的是让派生类编制自己的行为,所以应该在基类中声明虚析构函数。当类中存在虚函数时,也应该使用虚析构函数,这样保证类对象销毁时能得到“完全”的空间释放。
若某个类不包含虚函数时,一般表示它将不作为一个基类来使用,建议不要将析构函数声明为虚函数,以保证程序执行的高效性。

纯虚函数和抽象基类

在实际工作中往往需要定义这样一个类,对这个类中的处理函数只需要说明函数的名称、参数列表、以及返回值的类型,只提供一个接口以及说明和规范其他程序对此服务的调用,至于这个函数如何实现,根据具体需要在派生类中定义。通常把这样的类称为抽象基类,而把这样的函数成为纯虚函数。
纯虚函数定义格式为:virtual 返回值类型 函数名称(参数列表)=0;
当一个类中存在纯虚函数时,这个类就是抽象类。抽象类的主要作用是,为一个类建立一个公共的接口,使它们能够更有效地发挥多态特性。使用抽象类需要注意:

  1. 抽象类只能用于其他类的基类,不能建立抽象类对象。抽象类处于继承层次结构的较上层,抽象类自身无法实例化,只能通过继承机制生成抽象类的非抽象派生类,然后再实例化。
  2. 抽象类不能用于参数类型、函数返回值或显示转换的类型。
  3. 可以声明一个抽象类的指针和引用。通过指针或引用可以指向并访问派生类对象,以访问派生类的成员。
  4. 抽象类派生出新的类之后,若派生类给出所有纯虚函数的函数实现,这个派生类就可以声明自己的对象,因而其不再是抽象类;反之,若派生类没有给出全部纯虚函数的实现,这时的派生类仍然是一个抽象类

纯虚函数非常有用,因为它使类有明显的抽象性,并告诉用户和编译器希望如何使用。再基类中,对纯虚函数提供定义是可能的,告诉编译器不允许纯抽象基类声明对象,而且纯虚函数再派生类中必须定义,以便创建对象。然而,若希望一段代码对于一些或所有派生类定义能共同使用,而不希望在每个函数中重复这段代码,具体实现方法如下:
例如:虚函数与纯虚函数的使用。

#include <iostream>
using namespace std;
class A
{
public:virtual void play() const = 0;virtual void show()const = 0{cout << "A:show()" << endl;}
};class B :public A
{
public:void play() const{cout << "B::play" << endl;}void show() const{A::show();}
};class C :public B
{
public:void play() const{cout << "C::play" << endl;}void show() const{A::show();}
};void tune(A& i)
{i.play();
}int main()
{B b;tune(b);b.show();C c;tune(c);c.show();return 0;
}

程序运行结果为:

B::play
A:show()
C::play
A:show()

多重继承

在派生类中声明,基类名既可以有一个,也可以有多个。若只有一个基类名,则这种继承方式称为单继承;若基类名有多个,则这种继承方式称为多继承,这时派生类就同时得到多个已有类的特征。如图:
在这里插入图片描述

多继承语法

多继承允许派生类有两个或多个基类的能力,就是想使多个类以这种方式组合起来,使派生类对象的行为具有多个基类对象的特征。在多继承中各个基类名之间用逗号隔开。
多继承的声明格式为:class 派生类名:[继承方式]基类名1,[继承方式]基类名2,···,[继承方式]基类名n{···};
例如:多继承的使用

#include <iostream>
using namespace std;
class A
{int a;
public:void SetA(int i){a = i;}
};class B
{int b;
public:void SetB(int i){b = i;}
};class C:public A,private B
{int c;
public:void SetC(int, int, int);
};
//派生类成员函数直接访问基类的公有成员
void C::SetC(int x, int y, int z)
{SetA(x);SetB(y);c = z;
}int main()
{C obj;obj.SetA(5);obj.SetC(6, 7, 9);obj.SetB(6);//错误,不能访问私有继承的基类成员return 0;
}

虚基类

在多继承中,经常会遇到这样的情况,若两个及其以上的基类中有相同的成员函数,那么它们的派生类声明的对象将调用哪个基类的函数呢?
解决的方法是使用域名进行控制,但是增加作用域分辨符虽然可以消除二义性,但显然降低了程序的可读性。同时,多继承里还有一种极端的情况,即由相同基类带来的二义性,该继承方式称为“菱形”方式,它使子类对象重叠,增加了额外的空间开销。为此,C++引入了虚基类。
二义性问题需要在基类中重新定义函数,额外的空间开销问题可采用虚基类的方式。
把一个基类定义为虚基类,必须在派生子类时在父类名字前加关键字virtual.
定义格式为:class 派生类名:virtual 访问权限修饰符 父类名{};
例如:虚基类使用方法举例。

#include <iostream>
using namespace std;
class base
{
public:virtual const char* show() = 0;
};class d1 :virtual public base
{
public:const char* show(){return "d1";}
};class d2 :virtual public base
{
public:const char* show(){return "d2";}
};class m :public d1, public d2
{
public:const char* show(){return d2::show();//为消除二义性,使用作用域运算符}
};int main()
{m m1;m1.show();return 0;
}

这样不仅消除了二义性,而且类m1中只有一个基类base,也节省了空间。

最终派生类

在上述例中,各类没有构造函数,使用的是默认构造函数。若类里有了带有参数的构造函数,情形将有所不同。
在派生类声明对象时,编译器报错,表示没有合适的构造函数调用。即使在基类的派生类d1,d2中也会增加对基类base的构造,情况也是如此。为了解决此类问题,可引入最终派生类(most derived class)的概念。
最终派生类(最晚辈派生类)指当前所在的累。在基类base的构造函数,base就是最终派生类;在基类d1的构造函数,d1就是最终派生类;在基类m的构造函数,m就是最终派生类。
当使用虚基类时,尤其是带有参数的构造函数的虚基类时,最终派生类的构造函数必须对虚基类初始化。不管派生类离虚基类有多远,都必须对虚基类进行初始化。
例如:含虚基类构造函数的使用方法。

#include <iostream>
using namespace std;
class base
{int i;
public:base(int x):i(x){}int geti(){return i;}virtual const char* show(){return "base";}
};class d1 :virtual public base
{int id1;
public:d1(int x = 1) :base(0), id1(x) {}const char* show(){return "d1";}
};class d2 :virtual public base
{int id2;
public:d2(int x = 2) :base(1), id2(x) {}const char* show(){return "d2";}
};class m :public d1, public d2
{int im;
public:m(int x = 0) :base(3), im(x) {}const char* show(){return d2::show();}
};int main()
{m m1;cout << m1.show() << endl;cout << m1.geti() << endl;d1 d;cout << d.geti() << endl;cout << d.show() << endl;return 0;
}

程序运行结果是:

d2
3
0
d1

使用虚基类时要注意:

  1. 必须在派生类的构造函数中调用初始化虚函数的构造函数;
  2. 给虚基类安排默认构造函数,可使虚基类的程序开发变得简单易行。

多继承的构造顺序

例如:修改上例。

#include <iostream>
using namespace std;
class base
{int i;
public:base(int x):i(x){cout << "base is constructed" << endl;}virtual ~base(){cout << "base is destructed" << endl;}virtual const char* show() = 0;
};class d1 :virtual public base
{int id1;
public:d1(int x = 1) :base(0), id1(x) {cout << "d1 is constructed" << endl;}virtual ~d1(){cout << "d1 is destructed" << endl;}const char* show(){return "d1";}
};class d2 :virtual public base
{int id2;
public:d2(int x = 2) :base(1), id2(x) {cout << "d2 is constructed" << endl;}virtual ~d2(){cout << "d2 is destructed" << endl;}const char* show(){return "d2";}
};class m :public d1, public d2
{int im;
public:m(int x = 0) :base(3), im(x) {cout << "m is constructed" << endl;}~m(){cout << "m is destructed" << endl;}const char* show(){return d2::show();}
};int main()
{m m1(5);return 0;
}

程序运行结果是:

base is constructed
d1 is constructed
d2 is constructed
m is constructed
m is destructed
d2 is destructed
d1 is destructed
base is destructed

多继承构造顺序与单继承构造顺序类似,从基类开始,沿着派生顺序逐层向下,当同一层次派生同一个类时,按照声明继承的顺序自左向右。析构顺序与构造顺序相反。

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

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

相关文章

小程序 样式 WXSS

文章目录 样式 WXSS尺⼨单位样式导⼊选择器⼩程序中使⽤less 样式 WXSS WXSS( WeiXin Style Sheets )是⼀套样式语⾔&#xff0c;⽤于描述 WXML 的组件样式。 与 CSS 相⽐&#xff0c;WXSS 扩展的特性有&#xff1a; 响应式⻓度单位 rpx样式导⼊ 尺⼨单位 rpx &#xff08;…

基于frp工具实现内网穿透,跨局域网远程SSH登录

文章目录 一.概述1.1 为什么要内网穿透&#xff1f;1.2 什么是frp&#xff1f; 二.frp安装管理流程2.1 frp下载2.2 部署2.3 通过systemd系统服务管理启动程序 三.frp配置测试&#xff08;通过SSH访问内网机器C&#xff09;3.1 服务端配置文件frps.toml修改3.2 客户端配置文件fr…

tcp/ip分层模型

文章目录 tcp/ip分层模型封装和分用 tcp/ip分层模型 上面这个图主要介绍了osi七层模型和tcp/ip五层模型&#xff08;有的资料上说的是tcp/ip四层模型&#xff0c;把物理层算在了硬件设备上&#xff0c;这篇文章还是按照五层模型来讲&#xff09;。但osi七层模型实现起来比较复杂…

代码随想录算法训练营第16天 | 104.二叉树的最大深度, 111.二叉树的最小深度 ,222.完全二叉树的节点个数

二叉树理论基础&#xff1a; https://programmercarl.com/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE 104.二叉树的最大深度 题目链接&#xff1a;https://leetcode.cn/problems/maximum-depth-…

【TCP】传输控制协议

前言 TCP&#xff08;Transmission Control Protocol&#xff09;即传输控制协议&#xff0c;是一种面向连接的、可靠的、基于字节流的传输层通信协议。它由IETF的RFC 793定义&#xff0c;为互联网中的数据通信提供了稳定的传输机制。TCP在不可靠的IP层之上实现了数据传输的可…

如何使用Docker部署导航页工具Dashy并实现任意浏览器远程访问——“cpolar内网穿透”

文章目录 简介1. 安装Dashy2. 安装cpolar3.配置公网访问地址4. 固定域名访问 简介 Dashy 是一个开源的自托管的导航页配置服务&#xff0c;具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。你可以将自己常用的一些网站聚合起来放在一起&#xff0c;形成自己的导航…

最大流解决二分图匹配问题

文章目录 零、前言一、二分图匹配转化为网络流模型1.1建模步骤1.2整数值最大流和二分图匹配的关系1.3代码实现 二、OJ练习P2756 飞行员配对方案问题P3254 圆桌问题 零、前言 阅读本文前&#xff0c;需具备以下知识&#xff1a; 二分图及染色法判定-CSDN博客 二分图最大匹配—…

【JavaSE】P114~P147 ArrayList集合,Scanner类,Random,字符串及相关常用方法,静态static

目录 1 ArrayList 集合装箱&#xff0c;拆箱及包装类 2 API 概述和使用Scanner类匿名对象Random生成随机数 3 字符串字符串的31种构造方法字符串的常量池equals和 字符串的获取相关方法字符串的截取方法字符串的转换相关方法字符串的分割方法 4 静态static关键字静态static的内…

对话泛能网程路:能源产业互联网,行至中程

泛能网的能源产业互联网的标杆价值还不仅于此。其在产业互联之外&#xff0c;也更大的特殊性在于其也更在成为整个碳市场的“辅助运营商”&#xff0c;包括电力、碳等一系列被泛能网帮助企业改造和沉淀的要素资产&#xff0c;都在构成着碳交易市场的未来底层。 这恰是产业互联…

PCL-IO输入输入模块

IO输入输入模块 一、概述二、点云数据格式1. PCD 格式2. PLY 格式3. OBJ 格式4. STL 格式5. OFF 格式 三、读取3D文件1. API 总览2. 示例 四、保存3D文件1. API 总览2. 示例 一、概述 PCL 库提供了一个模块用来对3D数据进行读写操作&#xff0c;这个库提供了一个模块&#xff…

2007-2022年全国货币供应量M2、失业率、CPI、第三方互联网支付、出口、人口等宏观经济指标数据(年度、季度)

2007-2022年全国货币供应量M2、失业率、CPI、第三方互联网支付、出口、人口等宏观经济指标数据&#xff08;年度、季度&#xff09; 1、时间&#xff1a;2007-2022年&#xff08;季度、年度&#xff09; 2、指标&#xff1a; 季度指标&#xff1a;时间、GDP不变价累计值(亿元…

性能优化-高通的Hexagon DSP和NPU

原文来自【 Qualcomm’s Hexagon DSP, and now, NPU 】 本文主要介绍Qualcomm Hexagon DSP和NPU&#xff0c;这些为处理简单大量运算而设计的硬件。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;高性能&#xf…

多臂老虎机理论系列

[多臂老虎机理论](Lower bounds for non-adaptive exploration) 多臂老虎机之Lower bounds 定理 2.12&#xff1a; 定理的意义在于&#xff0c;对于任何不调整探索策略的算法&#xff0c;存在至少一个问题实例&#xff0c;使得随着时间的推移&#xff0c;该算法的预期遗憾将…

Linux常见的管理命令

1. whoami 作用&#xff1a; 显示出当前有效的用户名称&#xff0c;Linux是多用户多任务 语法&#xff1a;whoami(选项) 选项&#xff1a; --help&#xff1a;在线帮助 --version&#xff1a;显示版本信息和退出 场景使用&#xff1a; 1. 当用户想要查看当前登录系统的用户…

14.case条件测试语句(5)

case语句是在多个范围内匹 配数据&#xff0c;若匹配成功则执行相关命令并结束整个条件测试&#xff1b;如果数据不在所列出的范围内&#xff0c; 则会去执行星号&#xff08;*&#xff09;中所定义的默认命令&#xff08;C语言中的default语句&#xff09; 提示用户输入一个字…

爬取樱花动漫名侦探柯南最新剧场版ts格式

import os import requests import zipfile from tqdm import tqdm import tkinter as tkfilename 名侦探柯南\\ if not os.path.exists(filename):os.mkdir(filename) # https://vip.ffzy-online6.com/20231129/22304_740e70d0/2000k/hls/cedd2dc1ecb000001.ts # https://vip…

硬件基础:存储器

之前对存储器做过简单的汇总&#xff0c;参考这篇文章&#xff1a; 计算机/微机存储技术_路溪非溪的博客-CSDN博客 这次&#xff0c;我们从数字集成电路的角度再次补充学习一下存储器的知识。 定义和分类 从这里面我们能知道一些关键词。 存储介质主要是半导体器件和磁性材料。…

亿发中小型企业erp软件智能化赋能,专业助力广东制造行业生产流程管理

在当前经济全球化的环境下&#xff0c;广东省的中小型制造业企业正面临多方面的严峻挑战。包括产品质量的维护、分销渠道的稳定、生产成本降低以及减轻生产过程中的资源消耗等难题。目前&#xff0c;随着信息技术的迅速发展&#xff0c;一些先进的IT工具&#xff0c;比如企业资…

STM32实现软件IIC协议操作OLED显示屏(1)

时间记录&#xff1a;2024/1/25 一、IIC协议介绍 &#xff08;1&#xff09;协议介绍 IIC&#xff08;又称I2C&#xff0c;Inter-Integrated Circuit&#xff09;&#xff0c;即集成电路总线&#xff0c;是一种两线式串行总线&#xff0c;由PHILIPS公司开发&#xff0c;用…

OSS上传下载乱码问题

配置headers&#xff1a; "Content-Disposition": attachment; filename*UTF-8${encodeURIComponent(file.file.name)},