当前位置: 首页 > news >正文

[C++随想录] 继承

继承

  • 继承的引言
  • 基类和子类的赋值转换
  • 继承中的作用域
  • 派生类中的默认成员函数
  • 继承与友元
  • 继承与静态成员
  • 多继承的结构
  • 棱形继承的结构
  • 棱形虚拟继承的结构
  • 继承与组合

继承的引言

  1. 概念
    继承(inheritance)机制是面向对象程序设计使代码可以 复用的最重要的手段,它允许程序员在保
    持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象
    程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继 承是类设计层次的复用。
  2. 定义
class People
{
public:People(string name = "John", int age = 18){_name = name;_age = age;}void print() {cout << "姓名->" << _name << endl;cout << "年龄->" << _age << endl;}protected:int _age ;string _name;
};class Student : public People
{
public:Student(string name = "muyu", int age = 20, string id = "9210401227"):People(name, age){_id = id;}protected:string _id;
};class Teacher : public People
{
public:Teacher(){People::_name = "mutong";People::_age = 22;}protected:int _JobNumber;
};int main()
{People p;p.print();cout << endl;Student s;s.print();cout << endl;Teacher t;t.print();return 0;
}

运行结果:

姓名->John
年龄->18姓名->muyu
年龄->20姓名->mutong
年龄->22
  1. 解释 class Student : public People

    People类是 父类/ 基类, Student类是 子类/ 派生类
    Student类继承People类的本质就是 复用Student 对象可以使用 People类里面的成员
    成员包括 成员变量 和 成员函数.
    成员变量是在对象空间内的, 而成员函数是不在对象空间内的, 属于整个类.
    成员的 访问限定符 有三种 public, protected, private
    继承方式不同 && 基类成员的访问限定符不同决定了基类的成员在派生类中的存在情况

  2. 继承的方式
    继承方式有三种: public, protected, private
    成员的 限定符 有三种 public, protected, private
    所以, 一共有 九种继承方式 👇👇👇

    1. 基类中的private成员, 在派生类中都是 不可见的
      • 不可见 和 private成员是不一样的, private成员是 类里面可以访问, 类外面不可访问, 不可见是 类里面看不见/ 不可访问, 类外面不可访问
    2. 其余继承方式, 派生类中的情况是 继承方式 和 类成员访问限定符中 权限小的那一个
      • 权限的大小: public > protected > private
    3. 父类如果是 class, 默认继承是 私有继承, 父类如果是 struct, 默认继承是 公有继承. 不过建议显示继承方式
    4. 常用的继承方式为 图中绿色的区域 ⇐ 继承的本质是 复用, 私有继承 和 基类中的私有成员在继承中是没有复用的意义的.
  3. 为什么 派生类没有 print函数 , 但能调用 print函数?
    我们可以认为 子类对象里面包含两个部分: 父类对象成员变量 + 子类本身成员变量

    子类对象中的 成员变量 = 自己本身的成员变量 + 父类的成员变量 (受访问限定符 和 继承方式共同限制)
    子类对象中的 成员函数 = 自己本身的成员函数 + 父类的成员函数 (受访问限定符 和 继承方式共同限制)
    print函数 是公有继承 && 访问限定符是公有 ⇒ 子类对象可以调用

  4. 为什么在 Teacher类中 可以People::_name = "mutong";
    我们已经知道了 派生类对象的基本结构了.
    那么派生类对象在 初始化阶段, 即调用默认构造 是先父类还是先子类呢?
    通过调试, 我们发现: 子类对象调用构造函数的时候, 先调用父类的默认构造函数去初始化子类中父类对象的那一部分, 然后在调用子类对象的默认构造函数
    Person类中有默认构造函数, 但是我们想改变一下 Teacher类对象中的 关于父类对象的那一部分, 那我们该怎么做呢?
    首先, 我们不能直接写

_name = "mutong";
_age = 22;

因为受 的影响, 域是编译器在编译阶段查找变量的规则.
虽然, 我们可以认为子类对象中有 父类对象成员 + 子类对象成员, 但彼此是 独立的.
调用默认构造函数还是去 Person类中去调用
编译器在 编译阶段默认查找的顺序是 局部域 , 子类域, 父类域, 全局域
我们在子类中去给父类对象成员赋值 ⇒ 我们应该告诉编译器, 这个变量直接去父类中去查找就OK
即, 这个时候我们要用 Person(父类)::

  1. 为什么在 Student类中 可以 :People(name, age)
    子类对象调用构造函数的时候, 先调用父类的默认构造函数去初始化子类中父类对象的那一部分, 然后在调用子类对象的默认构造函数.
    那么如果 父类对象没有默认构造函数呢?
    我们就需要 在子类的初始化列表处 显示调用父类的构造

基类和子类的赋值转换

int main()
{People p;Student st;st = p; // errorp = st; // 可以进行转换return 0;
}
  • 父类对象 不能 赋值给子类对象, 而子类对象 可以 赋值给父类对象
    可以这样想: 子类对象的成员 > 父类对象的成员可以 变小一点, 但不能变大一点

父类对象 = 子类对象, 属于不同类型之间的赋值 ⇒ 一般都会发生 类型转换 ⇒ 类型转换, 那就意味着要产生 临时常量拷贝. 但结果真的如我们想的这般吗?

  • 验证 父类对象 = 子类对象 是否有临时常量拷贝
    拷贝是 常量的 ⇒ 要进行区分, 我们可以使用 引用 &
    如果生成了临时拷贝, 我们用普通引用 就会导致 权限的放大 , 就会报错
    如果没有生成临时拷贝, 我们用普通引用, 就是 权限的平移, 就不会报错
int main()
{// 类型转换int i = 0;double d = i;// double& dd = i // errorconst double& dd = i;// 赋值兼容转换Student st;People ps = st;People& p = st;return 0;
}

派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫 切片 或者切割 . 寓意把派生类中父类那部分切来赋值过去

🗨️那么这个切片是怎样完成的呢?

继承中的作用域

🗨️在继承过程中, 可能会出现 父类中的成员名 和 子类中的成员名相同的情况, 那么派生类对象调用该成员会是怎样的情况呢?

  • 先看下面的代码:
class People
{
public:People(string name = "John", int age = 18){_name = name;_age = age;}void print() {cout << "class People" << endl;}protected:int _age ;string _name;
};class Student : public People
{
public:Student(string name = "muyu", int age = 20, string id = "9210401227"):People(name, age){_id = id;}void print(){cout << "class Student : public People" << endl;}protected:string _id;
};void test1()
{Student st;st.print();
}int main()
{test1();return 0;
}

运行结果:

class Student : public People

父类和子类中都有 print函数, 通过结果显示 派生类内部的print函数
这是因为 , 跟上面的People::_name = "muyu";是一样的道理
那么, 如果我们非要通过派生类对象 调用基类中的print函数呢?👇👇👇

void test1()
{Student st;st.People::print();
}

总结:

  1. 子类和父类中的成员尽量不同名!
  2. 上面的例子, 子类和父类有同名的成员, 子类隐藏父类的成员, 这种关系叫做 隐藏/ 重定义
    • 注意: 隐藏 != 重载
      重载的前提条件是 同一作用域, 而隐藏是 父类和子类成员同名
    • 隐藏 != 重写
      隐藏是 子类中同名成员隐藏父类中同名成员, 而重写是 子类中重写父类有关函数的实现

派生类中的默认成员函数

6个默认成员函数,“默认”的意思就是指我们不写,编译器会变我们自动生成一个,那么在派生类
中,这几个成员函数是如何生成的呢?

  1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认 的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
  2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
  3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
  4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能
    保证派生类对象先清理派生类成员再清理基类成员的顺序。
  5. 派生类对象初始化先调用基类构造再调派生类构造。
  6. 派生类对象析构清理先调用派生类析构再调基类的析构。
  7. 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同(这个我们后面会讲
    解)。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加
    virtual的情况下,子类析构函数和父类析构函数构成隐藏关系
class Person
{
public:Person(string name = "muyu", int age = 20):_name(name),_age(age){cout << "Person()" << endl;}Person(const Person& tem){_name = tem._name;_age = tem._age;cout << "Person(const Person& tem)" << endl;}Person& operator=(const Person& tem){_name = tem._name;_age = tem._age;return *this;cout << "Person& operator=(Person& tem)" << endl;}~Person(){cout << "~Person()" << endl;}protected:string _name;int _age;
};class Student : public Person
{
public:Student(const string name,const int age, const int num): Person(name,age), _num(num){cout << "Student()" << endl;}Student(const Student& s): Person(s), _num(s._num){cout << "Student(const Student& s)" << endl;}Student& operator = (const Student& s){cout << "Student& operator= (const Student& s)" << endl;if (this != &s){Person::operator =(s);_num = s._num;}return *this;}~Student(){cout << "~Student()" << endl;}
protected:int _num; //学号
};void test2()
{Student st1("牧童", 20, 20230101);Student st2(st1);Student st3("沐雨", 18, 20230102);st3 = st1;}int main()
{// test1();test2();return 0;
}

运行结果:

Person()
Student()
Person(const Person& tem)
Student(const Student& s)
Person()
Student()
Student& operator= (const Student& s)
~Student()
~Person()
~Student()
~Person()
~Student()
~Person()

🗨️其他函数都是 先父类, 后子类, 唯独 析构函数 先子类后父类?

  • 首先, 构造函数是 先父类, 后子类
    , 先进后出 ⇒ 析构的时候, 先子类, 后父类.
    其次, 父类可以调用子类的成员, 而子类不能调用父类的成员
    如果先析构父类, 如果子类对象还想调用父类的成员,那就完蛋了!

🗨️在子类的析构函数中, 调用父类的析构函数

  • 首先,
	~Student(){~Person(); // 提示有一个重载cout << "~Student()" << endl;}

纳闷? 这个还能有重载?
因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同(这个我们后面会讲
解)。那么编译器会对析构函数名进行特殊处理,处理成destrutor().
那么子类和父类中的 析构函数名 都是 destruction ⇒ 那么就构成了隐藏关系
那么我们在子类中调用父类的析构函数应该如下:

	~Student(){Person::~Person();cout << "~Student()" << endl;}

结果如下:

  • 编译器默认帮我们 先调用了父类的析构函数不信任我们用户, 由编译器自己完成

继承与友元

基类的友元, 派生类不会继承, 即基类的友元不能访问 子类中的 私有和保护成员

// 类的声明
class Student;class Person
{
public:friend void Display(const Person& p, const Student& s);protected:string _name; // 姓名};class Student : public Person
{
public:// friend void Display(const Person& p, const Student& s);
protected:int _stuNum; // 学号};void Display(const Person& p, const Student& s)
{cout << p._name << endl;cout << s._stuNum << endl; // error: “Student::_stuNum”: 无法访问 protected 成员(在“Student”类中声明)
}void test3()
{Person p;Student s;Display(p, s);
}int main()
{test3();return 0;
}

解决方法就是: 让 Display函数也充当 子类的友元👇👇👇

class Student : public Person
{
public:friend void Display(const Person& p, const Student& s);
protected:int _stuNum; // 学号};

继承与静态成员

基类中定义了一个静态成员, 则在整个继承体系中, 仅此一份. 子类不会单独拷贝一份, 继承的是使用权

🗨️只创建子类对象, 问一共创建了多少个子类对象?

  • 1. 可以在子类的默认构造中创建一个静态成员变量.
class A
{
public:A(){}
};class B :public A
{
public:B(){_count++;}
public:static int _count;
};int B::_count = 0;void test4()
{B b1;B b2;B b3;B b4;B b5;B b6;cout << "子类中的个数->" << B::_count << endl;}int main()
{test4();return 0;
}

运行结果:

子类中的个数->6
  1. 可以在父类的默认构造中创建一个静态成员变量.
class A
{
public:A(){++_count;}
public:static int _count;
};int A::_count = 0;class B :public A
{};void test4()
{B b1;B b2;B b3;B b4;B b5;B b6;cout << "子类的个数->" << A::_count << endl;}int main()
{test4();return 0;
}

运行结果:

子类中的个数->6

多继承的结构

一个子类只有一个直接父类, 叫做 单继承

一个子类有两个及以上的父类, 叫做 多继承

  1. 单继承的结构

    其实在 内存中不是这样 "点开" 的关系, 而是连续的空间

  2. 多继承的机构

    当然, 也是连续的空间

棱形继承的结构

多继承会有一种情况是 棱形继承

D继承B和C, B和C又同时继承A ⇒ 就会导致D对象中有两个A对象成员
在这里插入图片描述

这样就会导致 冗余性和二义性

其实, 解决 访问不明确/ 二义性 可以使用 基类::

但是 内存中D还是存储了两份 A类对象 造成的数据冗余性问题还没解决呢?

棱形虚拟继承的结构

棱形虚拟继承解决的就是 数据冗余性 和 二义性的问题


通过 内存窗口, 我们发现:

  1. 把A从B 和 C中抽出来了, 让A既不属于B, 也不属于C
  2. B和C类中多了一个位置出来
  • B和C类中多了一个位置的用处是什么?

    我们发现: 地址指向的空间第一个位置是 0, 第二个位置分别是 20(十六进制转二进制) 和 12(十六进制转二进制)

    虽然, 把A类单独放在一个空间, 但 A类中的成员还是B和C类得一部分 =>
    这里是通过了B和C的两个指针,指向的一张表。这两个指针叫 虚基表指针,这两个表叫 虚基表。虚基表中存的 偏移量 。通过偏移量可以找到下面的A。

那么这个时候, 我们修改A类的对象, 就不会有 冗余性和二义性的问题了👇👇👇

继承与组合

继承是一种 is-a的关系, 是一种 白箱复用, 子类跟父类之间的 耦合度高
对象组合是一种 has-a的关系, 是一种 黑箱复用, 耦合度低

  • is-a 和 has-a
    is-a,就表示 子类是一个特殊的父类
    has-a, 就表示 A对象中有B对象

  • 白箱复用 和 黑箱复用
    白箱复用, 透明的, 即 子类知道父类内部的细节, 方法的实现
    黑箱复用, 不透明的, 即 对象之间不知道彼此的内部的细节

  • 耦合度
    打个比方:
    父类A中的成员 有20个是public, 80个是protected的; 派生类是public继承
    那么在派生类B中, A的成员都是可见的 ⇒ 耦合度是 100%
    同样的,
    A对象中的成员, 有20个是public, 80个是protected的;
    那么B对象想用A对象里面的成员, 只能使用 20个public的成员 ⇒ 耦合度是 20%

🗨️那对象组合这么好, 我们就用对象组合, 不用继承了是吧?

  • 首先, 存在即合理 ⇒ 全部都这样, 或者全部都那样的想法就是错误的
    1. 合理使用: 符合is-a 关系的就使用 继承; 符合 has-a关系就使用 对象组合; 如果 既符合has-a, 又符合 is-a关系使用 对象组合
    2. 实现 多态 , 必须使用继承

学后反思:

  1. 什么是菱形继承?菱形继承的问题是什么?
  2. 什么是菱形虚拟继承?如何解决数据冗余和二义性的
  3. 继承和组合的区别?什么时候用继承?什么时候用组合?

与朋友论学,须委曲谦下,宽以居之。 — — 王阳明
译文:与朋友谈论学问,必须婉转曲从谦虚下问,与之宽和相处

相关文章:

[C++随想录] 继承

继承 继承的引言基类和子类的赋值转换继承中的作用域派生类中的默认成员函数继承与友元继承与静态成员多继承的结构棱形继承的结构棱形虚拟继承的结构继承与组合 继承的引言 概念 继承(inheritance)机制是面向对象程序设计使代码可以 复用的最重要的手段&#xff0c;它允许程序…...

ARM-day9

按键控制小灯、蜂鸣器、风扇&#xff0c;按一次启动&#xff0c;第二次关闭 key_it.c #include "key_it.h"//按键3的配置 void key3_it_config() {//RCC使能GPIOF时钟RCC->MP_AHB4ENSETR | (0x1<<5);GPIOF->MODER & (~(0x3<<16));EXTI->E…...

2386: [余姚2015] 幸运数字(luck)

目录 题目描述 输入 输出 样例输入 样例输出 提示 来源: 代码&#xff1a; 题目描述 今年圣诞节&#xff0c;小明收到了很多礼物&#xff0c;每个礼物上都有一个数字&#xff0c;表示对小明的祝福。可是小明有自己的想法&#xff0c;对小明来说&#xff0c;4或者7的倍数…...

【JUC系列-13】深入理解DelayQueue延迟队列的底层原理

JUC系列整体栏目 内容链接地址【一】深入理解JMM内存模型的底层实现原理https://zhenghuisheng.blog.csdn.net/article/details/132400429【二】深入理解CAS底层原理和基本使用https://blog.csdn.net/zhenghuishengq/article/details/132478786【三】熟练掌握Atomic原子系列基本…...

Leetcode---365周赛

题目列表 2873. 有序三元组中的最大值 I 2874. 有序三元组中的最大值 II 2875. 无限数组的最短子数组 2876. 有向图访问计数 一、有序三元组中的最大值I 看一眼该题的数据范围&#xff0c;直接三层for循环暴力枚举&#xff0c;时间复杂度O(n^3)&#xff0c;代码如下 class…...

Java使用opencv实现人脸识别、人脸比对

1. opencv概述 OpenCV是一个开源的计算机视觉库&#xff0c;它提供了一系列丰富的图像处理和计算机视觉算法&#xff0c;包括图像读取、显示、滤波、特征检测、目标跟踪等功能。 opencv官网&#xff1a;https://opencv.org/ opencv官网文档&#xff1a;https://docs.opencv.or…...

Redis HyperLogLog的使用

Redis HyperLogLog知识总结 一、简介二、使用 一、简介 Redis HyperLogLog是一种数据结构&#xff0c;用于高效地计算基数&#xff08;集合中唯一元素的数量&#xff09;。它的主要作用是用于在内存中高效地存储和计算大量数据的基数&#xff0c;而无需完全存储所有的数据。Hy…...

Apisix-Ingress服务发现详解

apisix Apache APISIX 是一个基于微服务 API 网关&#xff0c;其不仅可以处理南北向的流量&#xff0c;也可以处理东西向的流量即服务之间的流量。Apache APISIX 集成了控制面板和数据面&#xff0c;与其他 API 网关相比&#xff0c;Apache APISIX 的上游、路由、插件全是动态的…...

spring6-事务

文章目录 1、JdbcTemplate1.1、简介1.2、准备工作1.3、实现CURD①装配 JdbcTemplate②测试增删改功能③查询数据返回对象④查询数据返回list集合⑤查询返回单个的值 2、声明式事务概念2.1、事务基本概念①什么是事务②事务的特性 2.2、编程式事务2.3、声明式事务 3、基于注解的…...

JavaFx学习问题2--音频、视频播放失败情况

文章目录 一、路径注意事项&#xff1a;① 用相对路径的时候别忘了前面的斜杠② uri问题 二、播放不了的问题① 获取的媒体文件路径本身就是不对的② 必须是uri③ 特殊情况 额外收获: 一、路径注意事项&#xff1a; 完整代码如下: import javafx.application.Application; im…...

第55节—— redux-toolkit中的createReducer——了解

一、概念 当我们使用 Redux 开发应用程序时&#xff0c;一个非常重要的概念就是 reducer。一个 reducer 是一个纯函数&#xff0c;它接受先前的状态和一个动作&#xff0c;然后返回一个新状态。每个动作都会引起状态的变化&#xff0c;从而使应用程序状态管理更加清晰和可控。…...

JUC并发编程——JUC并发编程概述及Lock锁(重点)(基于狂神说的学习笔记)

基于bilibili狂神说JUC并发编程视频所做笔记 概述 什么是JUC JUC时java.util工具包中的三个包的简称 java.util.concurrent java.util.concurrent.atomic java.util.concurrent.locks 业务&#xff1a;普通的线程代码中&#xff0c;我们常使用Runnable接口 但Runnable没有返…...

深入了解 Java 中的时间信息定义、转换、比较和操作

1. 简介 在过去的传统Java日期处理中&#xff0c;经常面临着一些问题。比如&#xff0c;java.util.Date和java.util.Calendar在表示日期和时间时存在着一些奇怪的行为&#xff0c;如月份从0开始计数、对日期进行格式化的方式繁琐不直观等。这些问题给开发带来了一定的困扰。 …...

2023年中国智能矿山发展历程及趋势分析:智能矿山健康有序发展[图]

智能矿山系统对矿山生产提质增效的效果已经开始显现&#xff1a;对不合规、有风险的行动进行及时预警&#xff0c;减少安全事故发生概率&#xff0c;避免因停产整顿产生的巨额亏损&#xff1b;精细化管理整个生产流程&#xff0c;避免过往传统粗放的流程导致的浪费&#xff0c;…...

acwing算法基础之基础算法--整数离散化算法

目录 1 知识点2 模板 1 知识点 整个范围很大&#xff0c;但存在的数据点很少。比如从 − 1 0 9 -10^9 −109到 1 0 9 10^9 109&#xff0c;但总共只有 1 0 6 10^6 106个数。 可以采用离散化的思想来做&#xff0c;即将离散的大数值映射成连续的小数值&#xff08;一般是 1 , …...

基于SSM框架的安全教育平台

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…...

Kafka生产者使用案例

1.生产者发送消息的过程 首先介绍一下 Kafka 生产者发送消息的过程&#xff1a; 1)Kafka 会将发送消息包装为 ProducerRecord 对象&#xff0c; ProducerRecord 对象包含了目标主题和要发送的内容&#xff0c;同时还可以指定键和分区。在发送 ProducerRecord 对象前&#xff0c…...

EasyX图形库实现贪吃蛇游戏

⭐大家好&#xff0c;我是Dark Falme Masker,学习了动画制作及键盘交互之后&#xff0c;我们就可以开动利用图形库写一个简单的贪吃蛇小游戏&#xff0c;增加学习乐趣。 ⭐专栏&#xff1a;EasyX部分小游戏实现详细讲解 最终效果如下 首先包含头文件 #include<stdio.h> #…...

利用 Amazon CodeWhisperer 激发孩子的编程兴趣

我是一个程序员&#xff0c;也是一个父亲。工作之余我会经常和儿子聊他们小学信息技术课学习的 Scratch 和 Kitten 这两款图形化的少儿编程工具。 我儿子有一次指着书房里显示器上显示的 Visual Studio Code 问我&#xff0c;“为什么我们上课用的开发界面&#xff0c;和爸爸你…...

2023年中国分子筛稀土催化材料竞争格局及行业市场规模分析[图]

稀土催化材料能够起到提高催化剂热稳定性、催化剂活性、催化剂储氧能力&#xff0c;以及减少贵金属活性组分用量等作用&#xff0c;广泛应用于石油化工、汽车尾气净化、工业废气和人居环境净化、燃料电池等领域。 2015-2023年中国稀土催化材料规模及预测 资料来源&#xff1a;…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

wpf在image控件上快速显示内存图像

wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像&#xff08;比如分辨率3000*3000的图像&#xff09;的办法&#xff0c;尤其是想把内存中的裸数据&#xff08;只有图像的数据&#xff0c;不包…...