C++——继承
目录
前言
1. 继承的概念和定义
1.1 继承的概念
1.2 继承的定义
1.2.1 定义格式
1.2.2 继承基类成员访问方式的变化
1.3 继承类模板
2. 基类和派生类之间的转换
3. 继承中的作用域
3.1 隐藏规则
3.2 考察继承作用域相关选择题
4. 派生类的默认成员函数
4.1 4个常见默认成员函数
4.2 实现⼀个不能被继承的类
5. 继承和友元
6. 继承与静态成员
7. 多继承及其菱形继承问题
7.1 继承模型
7.2 虚继承
7.3 多继承指针的偏移问题
8. 继承和组合
9. 总结
前言
我们知道面向对象的编程语言有三大特性,即封装、继承、多态;C++便属于面向对象的编程语言,今天我们就来介绍C++中的继承特性,如果你对C++感兴趣,欢迎继续阅读,下面进入正文部分。
1. 继承的概念和定义
1.1 继承的概念
继承机制是面向对象程序设计使代码可以复用的重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称为派生类。
继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,而继承便是类设计层次的复用。
下面我们看到没有继承之前我们设计了两个类Student和Teacher,Student和Teacher都有姓名/地址/ 电话/年龄等成员变量,都有identity⾝份认证的成员函数,设计到两个类里面就是冗余的。当然它们也有⼀些不同的成员变量和函数,比如老师独有成员变量是职称,学⽣的独有成员变量是学号;学生的独有成员函数是学习,老师的独有成员函数是授课。
class Student
{
public:// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证 void identity(){// ...}// 学习 void study(){// ...}
protected:string _name = "peter"; // 姓名 string _address; // 地址 string _tel; // 电话 int _age = 18; // 年龄 int _stuid; // 学号
};
class Teacher
{
public:// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证 void identity(){// ...}// 授课 void teaching(){//...}
protected:string _name = "张三"; // 姓名 int _age = 18; // 年龄 string _address; // 地址 string _tel; // 电话 string _title; // 职称
};
int main()
{return 0;
}
下面我们公共的成员都放到Person类中,Student和teacher都继承Person,就可以复用这些成员,就不需要重复定义了,省去了很多麻烦。
class Person
{
public:// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证 void identity(){cout << "void identity()" << _name << endl;}
protected:string _name = "张三"; // 姓名 string _address; // 地址 string _tel; // 电话 int _age = 18; // 年龄
};
class Student : public Person
{
public:// 学习 void study(){// ...}
protected:int _stuid; // 学号
};
class Teacher : public Person
{
public:// 授课 void teaching(){//...}
protected:string title; // 职称
};
int main()
{Student s;Teacher t;s.identity();t.identity();return 0;
}
void identity()张三
void identity()张三
C:\code\test_c\test_12_18\x64\Debug\test_12_18.exe (进程 26020)已退出,代码为 0 (0x0)。
按任意键关闭此窗口. . .
大家可以看到,在子类中没有_name这个成员变量,也没有identity这个函数,但是编译成功了,我们可以访问它们,这就是继承的妙处。
1.2 继承的定义
1.2.1 定义格式
我们看到Person是基类,也称作父类。Student是派生类,也称作子类。
1.2.2 继承基类成员访问方式的变化
1. 基类private成员在派⽣类中⽆论以什么方式继承都是不可见的。这⾥的不可见是指基类的私有成员还是被继承到了派⽣类对象中,但是语法上限制派⽣类对象不管在类里面还是类外面都不能去访问它。
2. 基类private成员在派⽣类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派⽣类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
既然不能直接访问,那么我们可不可以间接访问呢?
答案是可以的,大家来看下面的代码
class Person
{
public:// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证 void identity(){cout << "void identity()" << _name << endl;cout << _age << endl;}
protected:string _name = "张三"; // 姓名 string _address; // 地址 string _tel; // 电话
private:int _age = 18; // 年龄
};
class Student : public Person
{
public:// 学习 void study(){//cout << _age << endl;// ...}
protected:int _stuid; // 学号
};
class Teacher : public Person
{
public:// 授课 void teaching(){//...}
protected:string title; // 职称
};
int main()
{Student s;Teacher t;s.identity();t.identity();return 0;
}
void identity()张三
18
void identity()张三
18
C:\code\test_c\test_12_18\x64\Debug\test_12_18.exe (进程 28820)已退出,代码为 0 (0x0)。
按任意键关闭此窗口. . .
父类的private成员在子类中不可访问,但是在父类自己中当然可以访问,这里我们调用identity函数的时候,就会间接地访问到_age。
3. 实际上⾯的表格我们进行⼀下总结会发现,基类的私有成员在派⽣类都是不可见。基类的其他成员在派生类的访问方式==Min(成员在基类的访问限定符,继承方式)public >protected> private。
4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
5. 在实际运⽤中⼀般使用都是public继承,⼏乎很少使⽤protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派⽣类的类⾥⾯使⽤,实 际中扩展维护性不强。
1.3 继承类模板
namespace bit
{template<class T>class stack : public std::vector<T>{public:void push(const T& x){// 基类是类模板时,需要指定⼀下类域, // 否则编译报错:error C3861: “push_back”: 找不到标识符// 因为stack<int>实例化时,也实例化vector<int>了 // 但是模版是按需实例化,push_back等成员函数未实例化,所以找不到 vector<T>::push_back(x);//push_back(x);}void pop(){vector<T>::pop_back();}const T& top(){return vector<T>::back();}bool empty(){return vector<T>::empty();}};
}
int main()
{bit::stack<int> st;st.push(1);st.push(2);st.push(3);while (!st.empty()){cout << st.top() << " ";st.pop();}return 0;
}
3 2 1
C:\code\test_c\test_12_18\x64\Debug\test_12_18.exe (进程 26512)已退出,代码为 0 (0x0)。
按任意键关闭此窗口. . .
这里我们继承了vector,其实我们还可以继承list、deque,大家来看下面的代码;
//#define CONTAINER std::vector
#define CONTAINER std::list
//#define CONTAINER std::deque
namespace bit
{template<class T>class stack : public CONTAINER<T>{public:void push(const T& x){// 基类是类模板时,需要指定⼀下类域, // 否则编译报错:error C3861: “push_back”: 找不到标识符// 因为stack<int>实例化时,也实例化vector<int>了 // 但是模版是按需实例化,push_back等成员函数未实例化,所以找不到 CONTAINER<T>::push_back(x);//push_back(x);}void pop(){CONTAINER<T>::pop_back();}const T& top(){return CONTAINER<T>::back();}bool empty(){return CONTAINER<T>::empty();}};
}
int main()
{bit::stack<int> st;st.push(1);st.push(2);st.push(3);while (!st.empty()){cout << st.top() << " ";st.pop();}return 0;
}
3 2 1
C:\code\test_c\test_12_18\x64\Debug\test_12_18.exe (进程 26512)已退出,代码为 0 (0x0)。
按任意键关闭此窗口. . .
我们利用宏定义来解决就会非常方便,直接相当于替换上去。
2. 基类和派生类之间的转换
• public继承的派⽣类对象可以赋值给基类的指针、基类的引用。这⾥有个形象的说法叫切⽚或者切割。寓意把派⽣类中基类那部分切出来,基类指针或引⽤指向的是派⽣类中切出来的基类那部分。
• 基类对象不能赋值给派⽣类对象。
• 基类的指针或者引⽤可以通过强制类型转换赋值给派⽣类的指针或者引⽤。但是必须是基类的指针 是指向派⽣类对象时才是安全的。
class Person
{
protected:string _name; // 姓名 string _sex; // 性别 int _age; // 年龄
};
class Student : public Person
{
public:int _No; // 学号
};
int main()
{Student sobj;// 1.派⽣类对象可以赋值给基类的指针/引⽤ Person* pp = &sobj;Person& rp = sobj;// 派⽣类对象可以赋值给基类的对象是通过调⽤后⾯会讲解的基类的拷⻉构造完成的 Person pobj = sobj;//2.基类对象不能赋值给派⽣类对象,这⾥会编译报错 //sobj = pobj;Student* ptr = (Student*)pp;Student& ret = (Student&)rp;return 0;
}
这里大家要与类型转换进行区分,这里基类与派生类之间的转换没有产生临时变量,而类型转换会产生临时变量。大家可以看下面的例子。
临时变量具有常性,所以这里不能完成赋值,证明产生了临时变量;而上面派生类对象赋值给基类没有出现编译错误,也就说明并没有产生临时变量。
3. 继承中的作用域
3.1 隐藏规则
1. 在继承体系中基类和派⽣类都有独立的作用域。
2. 派⽣类和基类中有同名成员,派⽣类成员将屏蔽基类对同名成员的直接访问,这种情况叫隐藏。 (在派⽣类成员函数中,可以使⽤“基类::基类成员”显示访问)。
3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
4. 注意在实际中在继承体系⾥⾯最好不要定义同名的成员。
// Student的_num和Person的_num构成隐藏关系,可以看出这样代码虽然能跑,但是⾮常容易混淆
class Person
{
protected:string _name = "小李子"; // 姓名 int _num = 111; // 身份证号
};
class Student : public Person
{
public:void Print(){cout << "姓名:" << _name << endl;cout << "身份证号:" << Person::_num << endl;cout << "学号:" << _num << endl;}
protected:int _num = 999; // 学号
};
int main()
{Student s1;s1.Print();return 0;
};
姓名:小李子
身份证号:111
学号:999
C:\code\test_c\test_12_18\x64\Debug\test_12_18.exe (进程 40548)已退出,代码为 0 (0x0)。
按任意键关闭此窗口. . .
3.2 考察继承作用域相关选择题
class A
{
public:void fun(){cout << "func()" << endl;}
};
class B : public A
{
public:void fun(int i){cout << "func(int i)" << i << endl;}
};
int main()
{B b;b.fun(10);b.fun();return 0;
};
大家看到上面的代码,有两道选择题:
首先来看第一题,这道题非常具有迷惑性,很多人会选择A,认为这是函数重载,但是实际上本题选择B;这里大家需要结合上面提到的隐藏规则第三条,函数名只要相同就可以构成隐藏;而且大家还需要明确,函数重载是发生在同一作用域中的,这里基类和派生类完全是两个独立的作用域,所以并不存在函数重载一说;
关于第二题,有了上面的铺垫,就很容易理解了,这里派生类的func函数会把基类的func隐藏,调用func()时就会发生编译报错:error C2660: “B::fun”: 函数不接受 0 个参数。所以本题选择A
那么我们如果想调用应该怎么做呢?其实指定一下作用域即可;
class A
{
public:void fun(){cout << "func()" << endl;}
};
class B : public A
{
public:void fun(int i){cout << "func(int i)" << i << endl;}
};
int main()
{B b;b.fun(10);b.A::fun();return 0;
};
这样来写,就不会发生编译报错了。
4. 派生类的默认成员函数
4.1 4个常见默认成员函数
1. 派⽣类的构造函数必须调⽤基类的构造函数初始化基类的那⼀部分成员。如果基类没有默认的构造函数,则必须在派⽣类构造函数的初始化列表阶段显⽰调⽤。
2. 派⽣类的拷⻉构造函数必须调⽤基类的拷⻉构造完成基类的拷⻉初始化。
3. 派⽣类的operator=必须要调⽤基类的operator=完成基类的复制。需要注意的是派⽣类的 operator=隐藏了基类的operator=,所以显⽰调⽤基类的operator=,需要指定基类作⽤域。
4. 派⽣类的析构函数会在被调⽤完成后⾃动调⽤基类的析构函数清理基类成员。因为这样才能保证派⽣类对象先清理派⽣类成员再清理基类成员的顺序。
5. 派⽣类对象初始化先调⽤基类构造再调派⽣类构造。
6. 派⽣类对象析构清理先调⽤派⽣类析构再调基类的析构。
7. 因为多态中⼀些场景析构函数需要构成重写,重写的条件之⼀是函数名相同(这个我们多态章节会讲 解)。那么编译器会对析构函数名进⾏特殊处理,处理成destructor(),所以基类析构函数不加 virtual的情况下,派⽣类析构函数和基类析构函数构成隐藏关系。
简单来说,子类的一系列函数操作,都需要调用父类的来实现,这本质上是一种复用;这里大家需要注意的就是除了析构不需要显示调用父类的,其他3个都需要显示调用父类的,一般来说,子类的构造函数是需要我们自己实现的。
class Person
{
public:Person(const char* name = "peter"): _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;}
protected:string _name; // 姓名
};
class Student : public Person
{
public:Student(const char* name, int num): Person(name), _num(num){cout << "Student()" << endl;}//严格来说Student默认生成的拷贝构造已经够用了//如果有显示释放的资源,才需要自己实现Student(const Student& s): Person(s), _num(s._num){cout << "Student(const Student& s)" << endl;}//严格来说Student默认生成的赋值重载已经够用了//如果有显示释放的资源,才需要自己实现Student& operator = (const Student& s){cout << "Student& operator= (const Student& s)" << endl;if (this != &s){// 构成隐藏,所以需要显⽰调⽤ Person::operator =(s);_num = s._num;}return *this;}//严格来说Student默认生成的析构已经够用了//如果有显示释放的资源,才需要自己实现//析构函数都会被特殊处理成destructor()~Student(){//子类的析构与父类的析构构成隐藏关系//不需要显示调用父类的析构,以免出现重复析构,子类函数析构后,会自动调用父类的析构//析构顺序:先子后父cout << "~Student()" << endl;}
protected:int _num; //学号
};
int main()
{Student s1("jack", 18);Student s2(s1);Student s3("rose", 17);s1 = s3;return 0;
}
4.2 实现⼀个不能被继承的类
方法1:基类的构造函数私有,派⽣类的构成必须调⽤基类的构造函数,但是基类的构成函数私有化以 后,派⽣类看不⻅就不能调⽤了,那么派⽣类就⽆法实例化出对象。
方法2:C++11新增了⼀个final关键字,final修改基类,派⽣类就不能继承了。
// C++11的⽅法
class Base final
{
public:void func5() { cout << "Base::func5" << endl; }
protected:int a = 1;
private:// C++98的⽅法 /*Base(){}*/
};
class Derive :public Base
{void func4() { cout << "Derive::func4" << endl; }
protected:int b = 2;
};
int main()
{Base b;Derive d;return 0;
}
这里加了final就会发生报错:error C3246: "Derive": 无法从 "Base" 继承,因为它已声明为 "final"
两种方法都可以实现,C++11的方法更加明显,无论我们是否定义变量,这里都会直接报错。
5. 继承和友元
友元关系不能继承,也就是说基类友元不能访问派⽣类私有和保护成员。
class Student;//前置声明
class Person
{
public:friend void Display(const Person& p, const Student& s);
protected:string _name; // 姓名
};
class Student : public Person
{//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;
}
int main()
{Person p;Student s;// 编译报错:error C2248: “Student::_stuNum”: ⽆法访问 protected 成员 // 解决⽅案:Display也变成Student 的友元即可 Display(p, s);return 0;
}
6. 继承与静态成员
基类定义了static静态成员,则整个继承体系⾥⾯只有⼀个这样的成员。⽆论派⽣出多少个派⽣类,都 只有⼀个static成员实例。
class Person
{
public:string _name;static int _count;
};
int Person::_count = 0;
class Student : public Person
{
protected:int _stuNum;
};
int main()
{Person p;Student s;// 这⾥的运⾏结果可以看到⾮静态成员_name的地址是不⼀样的 // 说明派⽣类继承下来了,⽗派⽣类对象各有⼀份 cout << &p._name << endl;cout << &s._name << endl;// 这⾥的运⾏结果可以看到静态成员_count的地址是⼀样的 // 说明派⽣类和基类共⽤同⼀份静态成员 cout << &p._count << endl;cout << &s._count << endl;// 公有的情况下,⽗派⽣类指定类域都可以访问静态成员 cout << Person::_count << endl;cout << Student::_count << endl;cout << p._count << endl;cout << s._count << endl;return 0;
}
0000000BA650F6D8
0000000BA650F718
00007FF6A58914C4
00007FF6A58914C4
0
0
0
0
C:\code\test_c\test_12_19\x64\Debug\test_12_19.exe (进程 27340)已退出,代码为 0 (0x0)。
按任意键关闭此窗口. . .
这里大家可以看到,_count的地址的一样的,说明它们是同一个_count。
7. 多继承及其菱形继承问题
7.1 继承模型
单继承:⼀个派⽣类只有⼀个直接基类时称这个继承关系为单继承。
多继承:⼀个派⽣类有两个或以上直接基类时称这个继承关系为多继承,多继承对象在内存中的模型 是,先继承的基类在前⾯,后⾯继承的基类在后⾯,派⽣类成员在放到最后⾯。
菱形继承:菱形继承是多继承的⼀种特殊情况。菱形继承的问题,从下⾯的对象成员模型构造,可以 看出菱形继承有数据冗余和⼆义性的问题,在Assistant的对象中Person成员会有两份。⽀持多继承就 ⼀定会有菱形继承,像Java就直接不⽀持多继承,规避掉了这⾥的问题,所以实践中我们也是不建议 设计出菱形继承这样的模型的。
class Person
{
public:string _name; // 姓名
};
class Student : public Person
{
protected:int _num; //学号
};
class Teacher : public Person
{
protected:int _id; // 职⼯编号
};
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};
int main()
{// 编译报错:error C2385: 对“_name”的访问不明确 Assistant a;a._name = "peter";// 需要显⽰指定访问哪个基类的成员可以解决⼆义性问题,但是数据冗余问题⽆法解决 a.Student::_name = "xxx";a.Teacher::_name = "yyy";return 0;
}
上面的代码就是一个典型的菱形继承,这里的_name就会出现二义性,编译器不知道该访问哪个。
那么我们怎么才能解决这个问题呢?大家请继续看下面的内容。
7.2 虚继承
很多⼈说C++语法复杂,其实多继承就是⼀个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂,性能也会有⼀些损失,所以最好不要设计出菱形继承。多继承可以认为是C++的缺陷之⼀,后来的⼀些编程语言都没有多继承,如Java。
我们可以设计出多继承,但是不建议设计出菱形继承,因为菱形虚拟继承以后,⽆论是使⽤还是底层 都会复杂很多。当然有多继承语法⽀持,就⼀定存在会设计出菱形继承,像Java是不⽀持多继承的, 就避开了菱形继承。
class Person
{
public:string _name; // 姓名
};
class Student :virtual public Person
{
protected:int _num; //学号
};
class Teacher :virtual public Person
{
protected:int _id; // 职⼯编号
};
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};
int main()
{Assistant a;a._name = "peter";a.Student::_name = "xxx";a.Teacher::_name = "yyy";return 0;
}
我们加上virtual,这里就解决了二义性和冗余的问题。
这里有个小问题,大家可以来看一下:
当出现这样一个模型的时候,首先这也是一个菱形继承,那么我们的virtual应该加在哪里呢?答案是B、C处;这里大家记住一个原则,哪里产生了数据冗余,哪里就加virtual,上图中B和C都继承了A的数据,相当于A冗余了,那么就要在B和C处加上virtual形成虚继承。
7.3 多继承指针的偏移问题
class Base1 { public: int _b1; };
class Base2 { public: int _b2; };
class Derive : public Base1, public Base2 { public: int _d; };
int main()
{Derive d;Base1* p1 = &d;Base2* p2 = &d;Derive* p3 = &d;return 0;
}
下列说法正确的是?
这里我们来分析一下三个指针的指向:
p2就是所说的偏移指针,大家需要注意一点:先继承的在前面;根据上图可知本题选择C,我们也可以运行一下程序来进行验证。
000000480958FC68
000000480958FC6C
000000480958FC68
C:\code\test_c\test_12_19\x64\Debug\test_12_19.exe (进程 22108)已退出,代码为 0 (0x0)。
按任意键关闭此窗口. . .
8. 继承和组合
• public继承是⼀种is-a的关系。也就是说每个派⽣类对象都是⼀个基类对象。
• 组合是⼀种has-a的关系。假设B组合了A,每个B对象中都有⼀个A对象。
• 继承允许你根据基类的实现来定义派⽣类的实现。这种通过⽣成派⽣类的复⽤通常被称为⽩箱复⽤。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对派⽣类可见。继承⼀定程度破坏了基类的封装,基类的改变,对派⽣类有很⼤的影响。派⽣类和基类间的依 赖关系很强,耦合度高。
• 对象组合是类继承之外的另⼀种复⽤选择。新的更复杂的功能可以通过组装或组合对象来获得。象组合要求被组合的对象具有良好定义的接⼝。这种复用风格被称为黑箱复⽤, 因为对象的内部细节是不可见的。对象只以“⿊箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。
• 优先使用组合,而不是继承。实际尽量多去⽤组合,组合的耦合度低,代码维护性好。不过也不太那么绝对,类之间的关系就适合继承(is-a)那就⽤继承,另外要实现多态,也必须要继承。类之间的 关系既适合⽤继承(is-a)也适合组合(has-a),就⽤组合。
9. 总结
本篇博客为大家介绍了继承的内容,大家会发现,里面很多内容都与我们前面学过的类和对象的内容相关,这里我们需要前后结合着来理解,了解它们之间的关联,在实际运用中,继承的运用场景很多,所以大家需要掌握好继承的相关内容,最后,希望本篇博客可以为大家带来帮助,感谢阅读!
相关文章:
C++——继承
目录 前言 1. 继承的概念和定义 1.1 继承的概念 1.2 继承的定义 1.2.1 定义格式 1.2.2 继承基类成员访问方式的变化 1.3 继承类模板 2. 基类和派生类之间的转换 3. 继承中的作用域 3.1 隐藏规则 3.2 考察继承作用域相关选择题 4. 派生类的默认成员函数 4.1 4个常…...
01:C语言的本质
C语言的本质 1、ARM架构与汇编2、局部变量初始化与空间分配2.1、局部变量的初始化2.1、局部变量数组初始化 3、全局变量/静态变量初始化化与空间分配4、堆空间5、函数 1、ARM架构与汇编 ARM简要架构如下:CPU,ARM(能读能写),Flash(…...
Jmeter进阶篇(32)Jmeter 在 MySQL 数据库压测中的应用
一、引言 在当今数字化时代,数据库性能的优化对于企业的发展至关重要。随着业务量的不断增长,数据库需要承受越来越大的压力。MySQL作为一种广泛使用的开源数据库,其性能和稳定性备受关注。为了确保数据库在高负载情况下能够正常运行,进行压测是必不可少的环节。Jmeter作为…...
TCPDump参数详解及示例
TCPDump参数详解及示例 TCPDump参数详解TCPDump -G的示例TCPDump -i any -s 2048 -G 600 -p udp -Z root -n -X -tt -w %Y_%m%d_%H%M_%S.pcap &的含义TCPDump是一款强大的网络数据包截获分析工具,可以将网络中传送的数据包的完全截获下来提供分析。它支持针对网络层、协议…...
Protocol Buffer
1、什么是 Protocol Buffers? Protocol Buffers (protobuf) 是一种序列化结构化数据的方法,由 Google 开发。它们提供了一种与语言无关、与平台无关且可扩展的机制,用于高效序列化结构化数据。 Protocol Buffers 中的…...
高等数学学习笔记 ☞ 连续与间断
1. 连续 1. 点连续定义: 设函数在点的某邻域内有定义,取附近的点,对应的函数值分别和, 令,当时,若,则称函数在点处连续。 记作。 此式为增量形式。 又知,则可改写为:。 …...
【three.js】Shader着色器
原始着色器材质RawShaderMaterial 两种着色器材质的 RawShaderMaterial 和 ShaderMaterial 的区别和用法 区别: ShaderMaterial 会自动将一些初始化着色器的参数添加到代码中(内置 attributes 和 uniforms) RawShaderMaterial 则什么都不会添…...
在 macOS 中,设置自动将文件夹排在最前
文章目录 1、第一步访达设置2、第二步排序方式 需要两步设置 1、第一步访达设置 按名称排序的窗口中 2、第二步排序方式 选择名称...
创建并配置华为云虚拟私有云
目录 私有云 创建虚拟私有云 私有云 私有云是一种云计算模式,它将云服务部署在企业或组织内部的私有基础设施上,仅供该企业或组织内部使用,不对外提供服务.私有云的主要特点包括: 私密性:私有云的资源(如…...
Spark是什么?Flink和Spark区别
Spark是什么?Flink和Spark区别 一、Spark二、Spark和Flink区别三、总结 一、Spark Apache Spark 是一个开源的大数据处理框架,主要用于大规模数据处理和分析。它支持多种数据处理模式,包括批处理、流处理、SQL 查询、机器学习和图处理等。 核…...
代码随想录 day 25
第七章 回溯算法 part04 491.递增子序列 本题和大家刚做过的 90.子集II 非常像,但又很不一样,很容易掉坑里。 https://programmercarl.com/0491.%E9%80%92%E5%A2%9E%E5%AD%90%E5%BA%8F%E5%88%97.html 视频讲解:https://www.bilibili.com/…...
数据仓库中的指标体系模型介绍
数据仓库中的指标体系介绍 文章目录 数据仓库中的指标体系介绍前言什么是指标体系指标体系设计有哪些模型?1. 指标分层模型2. 维度模型3. 指标树模型4. KPI(关键绩效指标)模型5. 主题域模型6.平衡计分卡(BSC)模型7.数据指标框架模…...
xr-frame 通过shader去除视频背景色,加载透明视频
目录 前言 实现思路 获取 XR 框架系统: 注册自定义效果 创建效果对象 渲染通道配置 着色器代码 顶点着色器 片元着色器(颜色分量g达到条件的片元将被透透明) effect-removeBlack 完整代码 wxml中使用 前言 实现了一个用于注册自定…...
论文解读 | NeurIPS'24 IRCAN:通过识别和重新加权上下文感知神经元来减轻大语言模型生成中的知识冲突...
点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入! 点击 阅读原文 观看作者讲解回放! 作者简介 史丹,天津大学博士生 内容简介 大语言模型(LLM)经过海量数据训练后编码了丰富的世界知识。最近的研究表明,…...
Kraft模式安装Kafka(含常规、容器两种安装方式)
一、#创作灵感# 公司使用Kafka的软件项目较多,故写技术笔记巩固知识要点 二、软件环境 - Kafka 3.9.0 官方下载地址:Kafka 3.9.0 - Docker Desktop 4.37 容器图形化工具 官方下载地址:Docker Desktop 4.37 特别说明 - Docker Desktop…...
旷视科技C++面试题及参考答案
在 Linux 系统下常用的命令有哪些? 在 Linux 系统中有许多常用命令。首先是文件和目录操作相关的命令。“ls” 命令用于列出目录的内容,它有很多选项,比如 “ls -l” 可以以长格式显示文件和目录的详细信息,包括文件权限、所有者、大小、修改时间等;“ls -a” 则会显示所有…...
IWOA-GRU和GRU时间序列预测(改进的鲸鱼算法优化门控循环单元)
时序预测 | MATLAB实现IWOA-GRU和GRU时间序列预测(改进的鲸鱼算法优化门控循环单元) 目录 时序预测 | MATLAB实现IWOA-GRU和GRU时间序列预测(改进的鲸鱼算法优化门控循环单元)预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现IWOA-GRU和GRU时间序列预测…...
edeg插件/扩展推荐:助力生活工作
WeTab 此插件在我看来有2个作用 1.改变edeg的主页布局和样式,使其更加精简,无广告 2.提供付费webtab Ai(底层是chatGpt) 沉浸式翻译 此插件可翻译网页的内容 假设我们浏览github 翻译前 翻译后 Better Ruler 可以对网页的距离进行测量 适合写前端的小伙伴 用法示例:...
TPS和QPS的区别
TPS全称(Transactions Per Second)QPS全称(Queries Per Second)它们都是衡量系统性能的指标,都是表示单位时间内处理的事务或者查询的数量 TPS 定义:TPS指的是系统每秒可以处理的事务数量,一个…...
鸿蒙HarmonyOS开发:拨打电话、短信服务、网络搜索、蜂窝数据、SIM卡管理、observer订阅管理
文章目录 一、call模块(拨打电话)1、使用makeCall拨打电话2、获取当前通话状态3、判断是否存在通话4、检查当前设备是否具备语音通话能力 二、sms模块(短信服务)1、创建短信2、发送短信 三、radio模块(网络搜索&#x…...
如何处理 JavaScript 中的函数防抖问题?
在 JavaScript 中,函数防抖(Debouncing)是一种控制函数执行频率的技术,通常用于处理用户输入事件(例如键盘输入、滚动事件等)。防抖的核心思想是:在连续触发某个事件时,只有在事件停…...
sql server期末复习
表操作 创建create 删除drop 修改alter 数据操作 查询 select from <tableName> 插入 insert into <tableName> values 修改 update <tableName> set 删除 delete from <tableName> 授权与收回对数据的操作权限 授予 grant <权…...
初学STM32 --- 外部SRAM
目录 SRAM简介 SRAM特性: XM8A51216 功能框图 8080并口读时序编辑 8080并口写时序 SRAM 读写操作步骤 FSMC介绍 FSMC时序介绍 FSMC控制器对内核地址映射编辑 FSMC HAL库相关驱动 SRAM驱动步骤 SRAM简介 静态随机存取存储器(Static Random-Access Memory&am…...
XXX公司面试真题
一、一面问题 1.线程池的主要参数 核心线程数最大线程数空闲线程存活时间存活时间单位任务队列线程工厂拒绝策略允许核心线程超时 2. 线程的状态 新建状态就绪状态运行状态阻塞状态死亡状态 补充:线程阻塞的原因 线程调用sleep()方法进入睡眠状态 线程得到一个…...
MySQL 01 02 章——数据库概述与MySQL安装篇
一、数据库概述 (1)为什么要使用数据库 数据库可以实现持久化,什么是持久化:数据持久化意味着将内存中的数据保存到硬盘上加以“固化”持久化的主要作用是:将内存中的数据存储在关系型数据库中,当然也可以…...
[读书日志]8051软核处理器设计实战(基于FPGA)第四篇:verilog语法特性
第一篇https://blog.csdn.net/m0_74021449/article/details/144796689 第二篇https://blog.csdn.net/m0_74021449/article/details/144813103 第三篇https://blog.csdn.net/m0_74021449/article/details/144834117 4.verilog硬件描述语言基础 这部分主要讲述verilog基础语法…...
大模型高效推理综述
大模型高效推理综述 1 Introduction2 Preliminaries2.1 transformer架构的LLM2.2 大模型推理过程2.3 推理效率分析 3 TAXONOMY(分类)4.数据级别优化4.1输入压缩4.1.1 提示词裁剪(prompt pruning)4.1.2 提示词总结(prompt summary)…...
HTML5实现好看的博客网站、通用大作业网页模板源码
HTML5实现好看的博客网站、通用大作业网页模板源码 前言一、设计来源1.1 主界面1.2 列表界面1.3 文章界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载结束语 HTML5实现好看的博客网站、通用大作业网页模板源码,博客网站源码,HTML模板源码࿰…...
在Microsoft Windows上安装MySQL
MySQL仅适用于Microsoft Windows 64位操作系统,在Microsoft Windows上安装MySQL有不同的方法:MSI、包含您解压缩的所有必要文件的标准二进制版本(打包为压缩文件)以及自己编译MySQL源文件。 注意:MySQL8.4服务器需要在…...
adaface人脸特征提取之ncnn推理
目录 1. 背景2. 准备工作2.1 ncnn库下载2.2 adaface模型下载2.3 模型转换 3. 代码实现4. 模型量化 1. 背景 最近项目要求Android端使用adaface做人脸特征提取,最终选择ncnn作为推理框架 2. 准备工作 2.1 ncnn库下载 https://github.com/Tencent/ncnn/tree/maste…...
iOS 逆向学习 - iOS Security Features:硬件与软件多重防护体系
iOS 逆向学习 - iOS Security Features:硬件与软件多重防护体系 iOS 安全特性全面解析:构筑多层次防御体系一、iOS 的硬件安全特性1. Secure Enclave(安全隔区)2. Hardware Root of Trust(硬件信任根)3. De…...
纯前端实现将pdf转为图片(插件pdfjs)
需求来源 预览简历功能在移动端,由于用了一层iframe把这个功能嵌套在了app端,再用一个iframe来预览,只有ios能看到,安卓就不支持,查了很多资料和插件,原理基本上都是用iframe实现的。最终转换思路…...
stm32HAL库使LED闪烁
PC13引脚为开漏接法 生成代码时设置为out put open drain gpio out put level 设置为high 1表示熄灭 我们将pa9引脚连接为推挽接法 生成代码时设置为 out put push pull Gpio out put level 设置为low 0 表示熄灭 代码使其亮起再延时0.5秒再熄灭再延时0.5秒...
《数据结构》期末考试测试题【中】
《数据结构》期末考试测试题【中】 21.循环队列队空的判断条件为?22. 单链表的存储密度比1?23.单链表的那些操作的效率受链表长度的影响?24.顺序表中某元素的地址为?25.m叉树第K层的结点数为?26. 在双向循环链表某节点…...
【Vue3项目实战系列一】—— 全局样式处理,导入view-ui-plus组件库,定制个性主题
😉 你好呀,我是爱编程的Sherry,很高兴在这里遇见你!我是一名拥有十多年开发经验的前端工程师。这一路走来,面对困难时也曾感到迷茫,凭借不懈的努力和坚持,重新找到了前进的方向。我的人生格言是…...
ChatGPT 主流模型GPT-4/GPT-4o mini的参数规模是多大?
微软论文又把 OpenAI 的机密泄露了??在论文中明晃晃写着: o1-preview 约 300B;o1-mini 约 100BGPT-4o 约 200B;GPT-4o-mini 约 8BClaude 3.5 Sonnet 2024-10-22 版本约 175B微软自己的 Phi-3-7B,这个不用约…...
初学stm32 --- RTC实时时钟
目录 RTC简介 常用的RTC方案 STM32 F1 RTC框图介绍 后备寄存器和RTC寄存器特性(F1) F1 RTC相关寄存器介绍 RCC_APB1ENR寄存器编辑 PWR_CR寄存器 RCC_BDCR寄存器 RTC_CRL寄存器 RTC_CRH寄存器 RTC_PRLH寄存器 RTC_PRLL寄存器 RTC_CNTH寄存器 …...
Qt之屏幕录制设计(十六)
Qt开发 系列文章 - screencap(十六) 目录 前言 一、实现原理 二、实现方式 1.创建录屏窗口 2.录屏窗口类定义 3.自建容器对象定义 4.用户使用 5.效果演示 总结 前言 利用Qt实现屏幕录制设计,可以通过使用Qt自带的类QScreen、QPixma…...
25年1月更新。Windows 上搭建 Python 开发环境:Python + PyCharm 安装全攻略(文中有安装包不用官网下载)
引言 随着 Python 在数据科学、Web 开发、自动化脚本等多个领域的广泛应用,越来越多的开发者选择它作为首选编程语言。而 PyCharm 作为一个功能强大的集成开发环境(IDE),为 Python 开发者提供了极大的便利。本文将详细介绍如何在 …...
CTF杂项——[LitCTF 2024]涐贪恋和伱、甾―⑺dé毎兮毎秒
得到一张图片 有两种方式可以得到flag 第一种:LSB 第二种:zsteg...
从零开始手写缓存之如何实现固定缓存大小
cache 发展之路 1、HashMap或者ConcurrentHashMap public class CustomerService {private HashMap<String,String> hashMap new HashMap<>();private CustomerMapper customerMapper;public String getCustomer(String name){String customer hashMap.get(nam…...
Kubernetes——part4-1 Kubernetes集群 服务暴露 Nginx Ingress Controller
Kubernetes集群 服务暴露 Nginx Ingress Controller 一、ingress控制器 1.1 ingress控制器作用 (类似于slb,做代理服务) ingress controller可以为kubernetes 集群外用户访问Kubernetes集群内部pod提供代理服务。 提供全局访问代理访问流程…...
前端小案例——520表白信封
前言:我们在学习完了HTML和CSS之后,就会想着使用这两个东西去做一些小案例,不过又没有什么好的案例让我们去练手,本篇文章就提供里一个案例——520表白信封 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主…...
《机器学习》——线性回归模型
文章目录 线性回归模型简介一元线性回归模型多元线性回归模型误差项分析一元线性模型实例完整代码 多元线性模型实例完整代码 线性回归模型简介 线性回归是利用数理统计中回归分析,来确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法。 相关关系&…...
测试用例颗粒度说明
当我们在编写测试用例时,总是会遇到一个问题:如何确定测试用例的颗粒度?测试用例过于粗糙,可能无法全面覆盖系统的细节;而颗粒度过细,又会导致测试重复、冗余。掌握合适的颗粒度,不仅可以提高测…...
Kali 离线安装 ipmitool 笔记
在Kali Linux上离线安装 ipmitool 的步骤如下: 一、获取 ipmitool 的安装包和依赖 由于是离线安装,您需要先在一台可以联网的机器上下载 ipmitool 及其所有依赖包。 方法一:使用 apt 下载包 在联网的机器上,运行以下命令以准备…...
基于word2vec的文本大数据分析
基于word2vec的文本大数据分析 效果: 一、简介 Word2Vec是一种词向量表示方法,是在自然语言处理领域(NLP)的神经网络模型,是一种无监督学习,包含两种模型架构:CBOW模型与Skip-Gram模型。 常用于:相似度计算、词类聚类、文本分类、句子和文档表示、搜索引擎优化、情…...
BGP基础配置实验
一、实验拓补 二、实验要求及分析 实验要求: 1,R1为AS 100区域;R2、R3、R4为AS 200区域且属于OSPF协议;R5为AS 300区域; 2,每个设备上都有环回,且通过环回可以使设备互通; 实验分…...
Maven核心与单元测试
目录 一. Maven概述二. IDEA集成Maven2.1 创建Maven项目2.2 Maven坐标2.3 导入Maven项目 三. 依赖管理四. Maven的生命周期五. 单元测试5.1 快速入门5.2 断言5.3 常见注解5.4 依赖范围 六. Maven常见问题 \quad 一. Maven概述 \quad \quad 二. IDEA集成Maven \quad 2.1 创建Mav…...
Go语言的 的继承(Inheritance)基础知识
Go语言的继承(Inheritance)基础知识 引言 在面向对象编程(OOP)中,继承是一个重要的概念,它允许一个类(子类)继承另一个类(父类)的属性和方法。这种机制在许…...