6-继承

6-继承

  • 1、基本语法和方式
  • 2、继承的基本特点
    • 2.1 三种继承方式相同的基本点
    • 2.2 三种继承方式的差别
    • 2.3 公有继承的独有特点
  • 3、子类的构造、析构
    • 3.1 子类的构造
    • 3.2 子类的析构
    • 3.3 子类的拷贝构造函数
    • 3.4 子类的拷贝赋值
  • 4、多重继承
    • 4.1 内存布局
    • 4.2 类型转换
    • 4.3 名字冲突问题
  • 5、砖石继承
  • 6、虚继承

1、基本语法和方式

class 子类 : 继承方式1 基类1, 继承方式2 基类2, ... {...
};
  • 继承方式
    公有继承:public
    保护继承:protected
    私有继承:private

2、继承的基本特点

2.1 三种继承方式相同的基本点

  • 继承所要达到的目的:
    • 子类对象包含基类子对象
    • 子类内部可以直接访问基类的所有非私有成员
  • 继承的本质:
    • 基类的非私有成员在子类中仅仅为可见,而非拥有
      注意:
      对于继承切忌不要理解为基类的成员变为子类的成员,继承不会改变类成员的作用域,基类的成员永远都是基类的成员,并不会因为继承而变成子类的成员
  • 尽管基类的公有和保护成员在子类中直接可见,但仍然可以在子类中重新定义这些名字,子类中的名字会隐藏所有基类中的同名定义
  • 如果需要在子类内部访问 一个在基类中定义却被子类标识符所隐藏的名字,可以借助作用域限定操作符“::”实现
  • 因为作用域的不同,分别在子类和基类中定义的同名成员函数(包括静态成员函数),并不构成重载关系,相反是一种隐藏关系
  • 任何时候,在子类的内部,总可以通过作用域限定操作符“::”,显式地调用那些在基类中定义却被子类所隐藏的成员
// 继承最基本的特点
// (1) 子类对象的内部 包含 基类子对象
// (2) 子类内部可以直接访问 基类的 非私有(共有/保护)成员(变量/函数)
// (3) 一旦被子类同名定义隐藏 可以借助::指明访问基类
class Base{
public:int m_a;void foo(){ cout << "Base::foo" << endl; }
protected:int m_b;void bar(){ cout << "Base::Bar" << endl; }
private:int m_c;void hum(){ cout << "Base::hum" << endl; }
};
class Derived :public Base{
public:void fun(){m_a = 100;Base::foo();// 子类的foo函数将基类的foo函数隐藏,但可以通过作用域限定符访问基类的foom_b = 100;bar();  //子类内部可以直接访问 基类的 非私有(共有/保护)成员(变量/函数)//m_c = 100;//hum();}
private:int m_d;void foo(){ cout << "Derived::foo" << endl; }void bar(){ cout << "Derived::Bar" << endl; }
};
int main(){Base b;// 基类对象cout << "基类对象b的大小" << sizeof(b) << endl; // 12Derived d;// 子类对象cout << "子类对象d的大小" << sizeof(d) << endl; // 16  子类对象的内部 包含 基类子对象d.fun();return 0;
}

2.2 三种继承方式的差别

  • 基类中的公有、保护和私有成员,在子类中将对这些基类成员的访问控制限定进行重新标记
// public继承的独特点
class publicBase :public Base{// 子类将对基类的成员重新标记访问限定符// 子类将对基类的成员重新标记访控限定 m_a/foo是public m_b/bar是protected m_c/hum是private
public:void fun(){ // //子类内部访问基类成员时,编译器需要查看这些成员在 基类中的原始标记m_a = 100;Base::foo();// 子类的foo函数将基类的foo函数隐藏,但可以通过作用域限定符访问基类的foom_b = 100;bar();  //子类内部可以直接访问 基类的 非私有(共有/保护)成员(变量/函数)}
private:int m_d;
};
// protected继承的独特点
class protectedBase :protected Base{// 子类将对基类的成员重新标记访问限定符// 子类将对基类的成员重新标记访控限定 m_a/foo是protected m_b/bar是protected m_c/hum是private
public:void fun(){ //子类内部访问基类成员时,编译器需要查看这些成员在 基类中的原始标记m_a = 100;Base::foo();// 子类的foo函数将基类的foo函数隐藏,但可以通过作用域限定符访问基类的foom_b = 100;bar();  //子类内部可以直接访问 基类的 非私有(共有/保护)成员(变量/函数)}
private:int m_d;
};
// private继承的独特点
class privateBase :private Base{// 子类将对基类的成员重新标记访问限定符// 子类将对基类的成员重新标记访控限定 m_a/foo是private m_b/bar是private m_c/hum是private
public:void fun(){ //子类内部访问基类成员时,编译器需要查看这些成员在 基类中的原始标记m_a = 100;Base::foo();// 子类的foo函数将基类的foo函数隐藏,但可以通过作用域限定符访问基类的foom_b = 100;bar();  //子类内部可以直接访问 基类的 非私有(共有/保护)成员(变量/函数)}
private:int m_d;
};
int main(){publicBase d;// 利用子类对象在类外访问 基类成员时,编译器需要查看这些成员在 子类中的重新标记d.m_a = 10;d.foo();//d.m_b = 10; // err//d.bar();// err//d.m_c = 30;// err//d.hum();// errprotectedBase b;// 利用子类对象在类外访问 基类成员时,编译器需要查看这些成员在 子类中的重新标记//b.m_a = 10;// err//b.foo();// err//b.m_b = 10; // err//b.bar();// err//b.m_c = 30;// err//b.hum();// errprivateBase cd;// 利用子类对象在类外访问 基类成员时,编译器需要查看这些成员在 子类中的重新标记//cd.m_a = 10;// err//cd.foo();// err//cd.m_b = 10; // err//cd.bar();// err//cd.m_c = 30;// err//cd.hum();// errreturn 0;
}
基类中的在公有子类中标记为在保护子类中标记为在私有子类中标记为
公有成员公有成员保护成员私有成员
保护成员保护成员保护成员私有成员
私有成员私有成员私有成员私有成员
  • “通过”子类访问其所继承的基类的成员时,需要考虑因继承方式对访问控制限定的影响

2.3 公有继承的独有特点

(1) 只有在公有继承下,子类对象在类外可以访问基类的 公有成员(其他继承不可以)
(2) 如果被子类同名标识符隐藏可以借助::指明访问基类的成员
(3) 只有在公有继承下 子类类型指针 和 基类类型指针 之间可以进行转换
子类类型引用 和 基类类型引用 之间可以进行转换

class publicBase :public Base{
public:void foo(){cout << "publicBase::foo" << endl;}
private:int m_d;
};
class Human{
public:int m_d;string m_name;
};
class Student :public Human{
public :int m_no;
};
int main(){publicBase b;b.m_a = 1000;b.foo(); // 调用的是自己的b.Base::foo(); // 调用的是基类的Human h;cout << "基类对象h的大小" << sizeof(h) << endl;Student s;cout << "子类对象s的大小" << sizeof(s) << endl;// 子类类型指针 和 基类类型指针Human* ph = &s; // Student* --> Human*// 子类类型引用 和 基类类型引用Human& rh = s;// 以上两种转换,编译器认为访问范围缩小,是安全的//Student* ps = &h;//Student& rs = h;// 以上两种转换,编译器认为访问范围扩大,是危险的Student* ps = static_cast<Student*> (&h);Student& rs = static_cast<Student&>(h);// 通过静态转换虽然可以成功,但是风险依旧存在,极其不建议这么使用Student* ps = static_cast<Student*>(ph);// ph 指向的就是C6_Student的对象Student& rs = static_cast<Student&>(rh);// rh 就是C6_Student对象的引用// 以上两种转换毫无风险,极其建议大家这么做// 基类指针或引用的实际目标,究竟是不是子类对象,完全由我们自己判断return 0;
}

向上造型:

  • 子类类型指针 隐式 转换为基类类型指针
  • 子类类型引用 隐式 转换为基类类型引用

3、子类的构造、析构

3.1 子类的构造

  • 子类没有定义构造函数
    • 编译器为子类提供的默认无参构造函数,定义基类子对象,并调用其基类的无参构造函数,构造该子类对象中的基类子对象。
  • 子类定义构造函数但没有在初始化表中指明基类部分构造方式
    • 定义基类子对象,并调用其基类的无参构造函数,构造该子类对象中的基类子对象。
  • 子类定义构造函数并在初始化表中指明基类部分构造方式
    • 定义基类子对象并 调用指明的其基类的构造函数。
  • 子类对象的构造过程
    • 构造基类子对象->构造成员变量->执行构造代码

3.2 子类的析构

  • 子类没有定义析构函数
    • 编译器将提供一个默认析构函数,析构完所有的成员变量以后,会自动调用其基类的析构函数.
  • 子类定义析构函数
    • 子类的析构函数在执行完自身析构代码,并析构完所有的成员变量以后,会自动调用其基类的析构函数.
  • 子类对象的析构过程
    • 执行析构代码->析构成员变量->析构基类子对象

3.3 子类的拷贝构造函数

  • 子类没有定义拷贝构造函数
    • 编译器为子类提供的默认拷贝构造函数,定义基类子对象,并调用其基类的拷贝构造函数构造该子类对象中的基类子对象
  • 子类定义了拷贝构造函数,但没有在初始化表指明其基类部分的构造方式
    • 定义基类子对象,并调用其基类的无参构造函数,构造该子类对象中的基类子对象
  • 子类定义了拷贝构造函数,同时初始化表中指明了其基类部分以拷贝方式构造
    • 定义基类子类对象,并调用其基类的拷贝构造函数,构造该子类对象中的基类子对象

3.4 子类的拷贝赋值

  • 子类没有定义拷贝赋值函数
    • 编译器为子类提供的缺省拷贝赋值函数,会自动调用其基类的拷贝赋值函数,复制该子类对象中的基类子对象
  • 子类定义了拷贝赋值函数,但没有显式调用其基类的拷贝赋值函数
    • 子类对象中的基类子对象将得不到复制
  • 子类定义了拷贝赋值函数,同时显式调用了其基类的拷贝赋值函数
    • 子类对象中的基类子对象将得到复制
class C6_people{
public:C6_people(int age = 0, const char*name = "无名"):m_age(age), m_name(name){//【int m age=age;】定义mage,初值为age//【string m name(name);】定义m name,利用m name,string(name)cout << "people类的缺省构造函数被调用" << endl;}C6_people(const C6_people&that):m_age(that.m_age),m_name(that.m_name){// 【int m_age = that, mage; 】定义mage, 初值为that.m age//【string m name=that.m name;】定义m name,利用m name.string(that.m name)cout << "people类的拷贝构造函数被调用" << endl;}C6_people&operator=(const C6_people& that){//编译器不会在自定义拷贝赋值函数中塞任何操作cout << "people类的拷贝赋值函数被调用" << endl;this->m_age=that.m_age;this->m_name=that.m_name; // this->m name.operator=(that.m name)return *this;}~C6_people(){cout << "C6_people类的析构函数被调用" << endl;//对于基本类型的成员变量m_age,什么都不做//对于类类型的成员变量m_name,利用m_name,~string()//释放m_age/m_name本身所占内存空间}void getinfo(){cout << "姓名:" << m_name << ",年龄:" << m_age ;}
private:int m_age;// 基本类型成员变量string m_name;// 类类型成员变量
};
class C6_stu : public C6_people{
public://子类没有定义构造函数编译器为子类提供的默认无参构造函数//C6_stu(){//	【Human(); 】定义基类子对象,利用基类子对象.Human()//  【float m score; 】//	【string m remark; 】//}C6_stu(int age = 45, string name = "张三", float score = 0.0, string remark = "优秀") :m_score(score), m_remark(remark), C6_people(age,name.c_str()){cout << "基类的缺省构造被调用了" << endl;}void getinfo(){C6_people::getinfo();cout << "分数:" << m_score << "评语:" << m_remark << endl;}//子类没有定义析构函数编译器将提供一个默认析构函数//~C6_stu(){//对于基本类型的成员变量m_score,什么都不做//对于类类型的成员变量m m_remark,利用m_remark,~string()// 对于基类子对象,利用基类子对象.~C6_people()//释放m_score/m_remark本身所占内存空间//}~C6_stu(){cout << "C6_stu类的析构函数被调用" << endl;}//子类没有定义拷贝构造函数编译器为子类提供的默认拷贝构造函数//C6_stu(const C6_stu& that){//	// [C6_people(that)] 定义基类子对象,利用基类子对象.C6_people(that) ->C6_people类拷贝构造函数//	// ...//}// 子类没有定义拷贝赋值函数编译器为子类提供的缺省拷贝赋值函数//C6_stu& operator=(const C6_stu& that){//	C6_people *p = this;//	*p = that;// C6_people类的拷贝赋值函数//}private:float m_score;string m_remark;
};
int main(){cout << "----s1" << endl;C6_stu s1(22,"李四",89.7,"良好");// 构造s1.getinfo();cout << "----s2" << endl;C6_stu s2 = s1; // 拷贝构造s2.getinfo();cout << "----s3" << endl;C6_stu s3;cout << "s3赋值前" << endl;s3.getinfo();s3 = s2; // 赋值拷贝cout << "s3赋值后" << endl;s3.getinfo();return 0;
}

4、多重继承

4.1 内存布局

  • 子类对象中的多个基类子对象,按照继承表的顺序依次被构造,析构的顺序则与构造严格相反,各个基类子对象按照从低地址到高地址排列
    在这里插入图片描述
class C06_A{
public:int m_a;C06_A(){ cout << "C06_A构造" << endl; }~C06_A(){ cout << "C06_A析构" << endl; }
};
class C06_B{
public:int m_b;C06_B(){ cout << "C06_B构造" << endl; }~C06_B(){ cout << "C06_B析构" << endl; }
};
class C06_C{
public:int m_c;C06_C(){ cout << "C06_C构造" << endl; }~C06_C(){ cout << "C06_C析构" << endl; }
};
class C06_D :public C06_A, public C06_B, public C06_C{ // 汇聚子类
public:int m_d;C06_D(){ cout << "C06_D构造" << endl; }~C06_D(){ cout << "C06_D析构" << endl; }
};
int main(){C06_D d;cout << "汇聚子类对象d的大小:" << sizeof(d) << endl;C06_D * pd = &d;cout << "整个汇聚子类对象的首地址D* pd:" << pd << endl;cout<< "A基类子对象的首地址:" << &d.m_a<<endl;cout << "B基类子对象的首地址:" << &d.m_b << endl;cout << "C基类子对象的首地址:"<<&d.m_c<< endl;cout << "D基类子对象的首地址:" << &d.m_d << endl;return 0;
}

4.2 类型转换

  • 将多重继承的子类对象的指针,隐式转换为它的基类类型,编译器会根据各个基类子对象在子类对象中的内存位置,进行适当的偏移计算
  • 反之,将任何一个基类类型的指针静态转换为子类类型,编译器同样会进行适当的偏移计算
  • 无论在哪个方向上,重解释类型转换(reinterpret_cast)都不进行任何偏移计算–不要使用
    在这里插入图片描述
int main(){C06_D d;cout << "汇聚子类对象d的大小:" << sizeof(d) << endl;C06_D * pd = &d;cout << "整个汇聚子类对象的首地址D* pd:" << pd << endl;// 004FF730cout << "A基类子对象的首地址:" << &d.m_a << endl; // 004FF730cout << "B基类子对象的首地址:" << &d.m_b << endl; // 004FF734cout << "C基类子对象的首地址:" << &d.m_c << endl; // 004FF738cout << "D基类子对象的首地址:" << &d.m_d << endl; // 004FF73CC06_A* pa = pd;cout << "隐式转换" << endl;cout << "D* pd ---> A* pa:" << pa << endl; // 004FF730C06_B* pb = pd;cout << "D* pb ---> B* pb:" << pb << endl; // 004FF734C06_C* pc = pd;cout << "D* pd ---> C* pc:" << pc << endl; // 004FF738cout << "静态转换" << endl;C06_D* p1 = static_cast<C06_D*>(pa);cout << "A* pd ---> D* p1:" << p1 << endl; // 004FF730C06_D* p2 = static_cast<C06_D*>(pb);cout << "B* pb ---> D* p2:" << p2 << endl; // 004FF730C06_D* p3 = static_cast<C06_D*>(pc);cout << "C* pc ---> D* p3:" << p3 << endl; // 004FF730return 0;
}
  • 引用的情况与指针类似,因为引用的本质就是指针
    在这里插入图片描述

4.3 名字冲突问题

  • 如果在子类的多个基类中,存在同名的标识符,那么任何试图通过子类对象,或在子类内部访问该名字的操作,都将引发歧义。
    名字冲突问题解决方法
  • 子类隐藏该标识符–不建议使用
  • 通过作用域限定操作符“::”显式指明所属基类
class C06_E{
public:int m_a;int m_c;
};
class C06_F{
public:int m_b;int m_c;
};
class C06_G :public C06_E, public C06_F{
public:int m_d;void foo(){// m_c = 100;歧义C06_E::m_c = 100;}
};
int main(){C06_G g;cout << "汇聚子类对象g的大小:" << sizeof(g) << endl;// g.m_c=100; 歧义g.C06_E::m_c = 100;return 0;
}

5、砖石继承

  • 砖石继承的问题
    一个子类继承自多个基类,而这些基类又源自共同的祖先,这样的继承结构称为钻石继承(菱形继承)
    在这里插入图片描述
class C06_AA{ // 公共基类
public:int m_a;
};
class C06_X :public C06_AA{ // 中间子类
public:int m_x;
};
class C06_Y :public C06_AA{ // 中间子类
public:int m_y;
};
class C06_Z :public C06_X, public C06_Y{ // 汇聚子类
public:int m_z;
};
int main(){C06_Z z; // X中间子类子对象|Y中间子类子对象|m_z|-->// A公共基类子对象 m_x|A公共基类子对象 m_y|m_z|-->// m_a m_x |m_a m_y|m_z|cout << "汇聚子类对象g的大小:" << sizeof(z) << endl;// 20return 0;
}
  • 钻石继承问题
    在汇聚子类内部,或通过汇聚子类对象,访问公共基类的成员,会因继承路径的不同而导致匹配歧义

6、虚继承

解决砖石继承存在的问题而诞生的

  • 在继承表中使用virtual关键字

  • 虚继承可以保证
    (1)公共虚基类子对象在汇聚子类对象中仅存一份实例
    在这里插入图片描述

    (2)公共虚基类子对象被多个中间子类子对象所共享
    虚继承实现原理

  • 汇聚子类对象中的每个中间子类子对象都持有一个指针,通过该指针可以获取中间子类子对象的首地址公共虚基类子对象的首地址的偏移量
    在这里插入图片描述

class C06_AA{ // 公共基类
public:int m_a;
};
class C06_X :virtual public C06_AA{ // 中间子类
public:int m_x;void setAge(/* X* this */int age){m_a = age; // this -> X 中间子类子对象-》指针1-》偏移量-》this+偏移量-》A公共基类子对象-》m_a}};
class C06_Y :virtual public C06_AA{ // 中间子类
public:int m_y;int getAge(/* Y* this*/){ return m_a;// this->Y 中间子类子对象 - 》指针2 - 》偏移量 - 》this + 偏移量 - 》A公共基类子对象 - 》m_a}
};
class C06_Z :public C06_X, public C06_Y{ // 汇聚子类
public:int m_z;
};
int main(){C06_Z z; // X中间子类子对象|Y中间子类子对象|m_z|A公共基类子对象|-->// 指针1 m_x |指针2 m_y|m_z|m_a|cout << "汇聚子类对象g的大小:" << sizeof(z) << endl;// 32z.setAge(100); // setAge(&z) -->实参类型Z*cout << "age:" << z.getAge() << endl; // getAge(&z) -->实参类型Z*return 0;
}

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

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

相关文章

10. C++异步IO处理库和使用libevent实现高性能服务器

C比较有名的异步IO处理库 libevent 这个主要使用的是epoll。libevthplibuvlibev 我们主要介绍libevent。 libevent重要函数 event_base_new 这个可以对应于epoll_create也就是创建一个实例。还可以初始化libevent所有管理相关的代码。比如说所能用到的队列&#xff0c;栈&a…

Linux内核编译流程3.10

一、内核源代码编译流程 编译环境&#xff1a; cat /etc/redhat-release CentOS Linux release 7.4.1708 (Core) Linux内核版本&#xff1a; uname -r 3.10.0-693.el7.x86_64 编译内核源代码版本&#xff1a;linux-4.19.90-all-arch-master cp /boot/config-xxx到内核源…

数据库(9)——DQL基础查询

数据查询 数据查询是SQL中最复杂的&#xff0c;语法结构为 SELECT 字段列表 FROM 表名列表 WHERE 条件列表 GROUP BY 分组字段列表 HAVING 分组后字段列表 ORDER BY 排序字段列表 LIMIT 分页参数 查询多个字段 SELECT 字段1&#xff0c;字段2...FROM…

LeetCode583:两个字符串的删除操作

题目描述 给定两个单词 word1 和 word2 &#xff0c;返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符串中的一个字符。 代码 解法1 /*dp[i][j]&#xff1a;以i-1为结尾的wrod1中有以j-1为尾的word2的个数为了让word1和word2相同&#xff0c;最少操作…

linux开发之设备树基本语法一

设备树的根节点 设备树子节点和子子节点,子节点在根节点范围内 包含子节点以及子子节点 节点名称 比如这里led就是这个gpio的小名,可以直接用 gpio22020101是这里的名字,也就是要用这个gpio,符号后面的一串数字使用了这个gpio的寄存器地址,因为可能会用很多gpio,所以加入寄存…

Pushmall共享分销电商SaaS版2024年 5月模块开发优化完成

Pushmall共享分销电商 2024年 5月模块开发优化完成 1、**实现SaaS框架业务&#xff1a;**多租户、多商家、多门店&#xff0c;及商家入驻、商品管理。 2、租户小程序管理&#xff1a;对租户的小程序业务管理。 3、店铺小程序管理&#xff1a;对租户多店铺小程序绑定。 4、会员分…

新火种AI|警钟长鸣!教唆自杀,威胁人类,破坏生态,AI的“反攻”值得深思...

作者&#xff1a;小岩 编辑&#xff1a;彩云 在昨天的文章中&#xff0c;我们提到了谷歌的AI Overview竟然教唆情绪低迷的网友“从金门大桥跳下去”。很多人觉得&#xff0c;这只是AI 模型的一次错误判断&#xff0c;不会有人真的会因此而照做。但现实就是比小说电影中的桥段…

精酿啤酒:品质与口感对啤酒市场价格的影响

啤酒作为一种大众化的产品&#xff0c;其品质与口感对市场价格有着显著的影响。对于Fendi club啤酒而言&#xff0c;其卓着的品质和与众不同的口感又加上市场价格相对实惠&#xff0c;受到消费者的青睐。 品质是决定啤酒市场价格的重要因素。Fendi club啤酒选用天然小麦原料&am…

【leetcode2765--最长交替子数组】

要求&#xff1a;给定一个数组&#xff0c;找出符合【x, x1,x,x-1】这样循环的最大交替数组长度。 思路&#xff1a;用两层while循环&#xff0c;第一个while用来找到符合这个循环的开头位置&#xff0c;第二个用来找到该循环的结束位置&#xff0c;并比较一下max进行记录。 …

太速科技-16通道24bit 256kHZ 的振动信号千兆网络采集器

16通道24bit 256kHZ 的振动信号千兆网络采集器 一、产品概述 数据采集器是一台运行Linux操作系统的智能终端&#xff0c;在以太网络的支持下&#xff0c;可迅速构建起大规模的分布式智能数据采集系统。采集器终端体积小&#xff0c;功耗低&#xff0c;易集成&#xff0c…

Apache Impala 4.4.0正式发布了!

历时半年多&#xff0c;Impala 4.4终于发布了&#xff01;本次更新带来了不少新功能&#xff0c;受限于篇幅&#xff0c;这里简要列举一些&#xff0c;后续文章再挑重点的进行介绍。 支持更多Iceberg表上的语句 支持对 Iceberg V2 表的 UPDATE 语句&#xff0c;用来更新已有数…

解析新加坡裸机云多IP服务器网线路综合测评解析

在数字化高速发展的今天&#xff0c;新加坡裸机云多IP服务器以其卓越的性能和稳定性&#xff0c;成为了众多企业和个人用户的首选。源库主机评测将对新加坡裸机云多IP服务器的网线路进行综合测评&#xff0c;以帮助读者更深入地了解这一产品的优势。 一、性能表现 新加坡裸机云…

【RocketMQ】安装RocketMQ5.2.0(单机版)

下载 官网下载地址&#xff1a;下载 | RocketMQ github地址&#xff1a;Tags apache/rocketmq GitHub 选择对应的版本下载。https://dist.apache.org/repos/dist/release/rocketmq/5.2.0/rocketmq-all-5.2.0-bin-release.zip 5.2.0的二进制包&#xff1a;下载地址 5.2.0的…

设计模式:装饰模式(Decorator)

设计模式&#xff1a;装饰模式&#xff08;Decorator&#xff09; 设计模式&#xff1a;装饰模式&#xff08;Decorator&#xff09;模式动机模式定义模式结构时序图模式实现在单线程环境下的测试在多线程环境下的测试模式分析优缺点适用场景应用场景应用实例模式扩展参考 设计…

Firefox浏览器网页上的按钮点击无效解决办法

我在github下点下载经常不好使&#xff0c;查了原因&#xff0c;原来是浏览器的问题。在Firefox浏览器的设置里面&#xff0c;去掉一些cookies的禁用即可。之后&#xff0c;就可以点击按钮成功响应了。

2024 全新 Javascript 面试题目基础篇

1. JavaScript 是单线程的吗&#xff1f; 没错&#xff0c;JavaScript 是 一种 单线程语言。这意味着它只有 一个调用栈和一个内存堆。每次只执行一组指令。 此外&#xff0c;JavaScript 是同步和阻塞 的性质。这意味着代码是逐行执行的&#xff0c;一个任务必须在下一个任务…

JVM的相关知识

目录 JVM内存划分 类加载过程 类加载中的“双亲委派模型” JVM内存划分 JVM也就是java进程。这个进程一旦跑起来之后&#xff0c;就会从操作系统里&#xff0c;申请一大块内存空间。JVM接下来就要进一步的对这个大的空间进行划分。划分成不同区域&#xff0c;从而每个区域都…

如何实时掌握手机号状态的API利器分析

在移动互联网的时代&#xff0c;手机号码不仅是通信的连接点&#xff0c;也是用户身份的关键识别。手机状态查询API 通过提供实时的手机号码状态查询服务&#xff0c;协助企业和组织更有效地管理用户信息&#xff0c;提升服务流程。 手机状态查询API 通过与电信运营商的数据库进…

Golang | Leetcode Golang题解之第117题填充每个节点的下一个右侧节点指针II

题目&#xff1a; 题解&#xff1a; func connect(root *Node) *Node {start : rootfor start ! nil {var nextStart, last *Nodehandle : func(cur *Node) {if cur nil {return}if nextStart nil {nextStart cur}if last ! nil {last.Next cur}last cur}for p : start; …

学至乎没而后止也

开场白 学至后没而后止也这个题目的原话来自与荀子《劝学》。大家知道什么意思吗&#xff1f;学习要学到你人没了&#xff0c;才算停止了。通俗点说就是只要没学死就往死里学&#xff0c;高雅点说就是要保持终身学习。 在以前说终身学习好像是一种良好习惯或品德&#xff0c;…