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

【C++】继承最全解析(什么是继承?继承有什么用?)

目录

一、前言

二、什么是继承 ? 

💢继承的概念💢 

💢继承的定义💢  

🥝定义格式 

🍇继承权限 

三、基类与派生类对象的赋值转换 

四、继承的作用域 

五、派生类中的默认成员函数 

💢默认成员函数的调用 💢

🔥构造函数与析构函数🔥

🔥拷贝构造🔥 

🔥赋值运算符重载 🔥

💢显示成员函数的调用 💢 

🔥构造函数 🔥

🔥拷贝构造 🔥

🔥赋值运算符重载 🔥

🔥析构函数🔥 

六、继承与友元 

七、继承与静态成员

八、菱形继承 

💧 单继承💧

💧 多继承💧

💧 菱形继承💧 

🍍概念 

🍉现象 

九、继承和组合 

十、继承的总结和反思 

十一、共勉 


一、前言

   继承 是 面向对象三大特性之一(封装、继承、多态),所有的面向对象(OO)语言都具备这三个基本特征,封装相关概念已经在《类和对象》系列中介绍过了,今天主要学习的是 继承即如何在父类的基础之上,构建出各种功能更加丰富的子


二、什么是继承 ? 

什么是继承?是继承 -- 遗产 还是继承 -- 花呗?答案都不是,先来看看官方解释:

继承(inheritance)机制是 ----面向对象程序设计使代码可以复用的重要的手段,它允许程序员在保持原有基类(父类)特性的基础上进行扩展,增加功能,这样产生新的类,称为派生类(子类) 


💢继承的概念💢 

继承相关概念: 

  • 被继承对象:父类 / 基类 (base
  • 继承方:子类 / 派生类 (derived

继承的本质 就是 ------------ 复用代码 


举个例子 : 假设我现在要设计一个校园管理系统,那么肯定会设计很多角色类,比如学生老师保安保洁等等之类的。 

设计好以后,我们发现,有些数据和方法是每个角色都有的,而有些则是每个角色独有的。 

为了复用代码、提高开发效率,可以从各种角色中选出共同点,组成 基类,比如每个 人 都有姓名、年龄、联系方式等基本信息,而 教职工学生 的区别就在于 管理与被管理,因此可以在 基类 的基础上加一些特殊信息如教职工号 表示 教职工,加上 学号 表示学生,其他细分角色设计也是如此

这样就可以通过 继承 的方式,复用 基类 的代码,划分出各种 子类 

 像上面共同拥有的数据和方法我们可以重新设计一个类Person ,然后让 StudentTeacher 去继承它,如下:

// 大众类  --- 基础属性
class Person
{
public:Person(string name = string(), string tell = string(), int age = int()):_name(name),_tell(tell),_age(age){}void Print(){cout << "我的名字是 :" << _name << endl;cout << "我的电话是 :" << _tell << endl;cout << "我的年龄是 :" << _age << endl;}
protected:string _name; // 姓名string _tell; // 电话int _age; // 年龄
};// 学生类  --- 派生/子属性
class Student : public Person
{
public:Student(int stuId = 1578):Person("XAS","123456789",26),_stuId(stuId){cout << "我是一个学生" << endl;cout << "以下是我的个人信息 :" << endl;cout << endl;cout << "我的学号为 :" << _stuId << endl;}
protected:int _stuId; // 学号
};// 老师类  --- 派生/子属性
class Teacher : public Person
{
public:Teacher(int workId = 2024916):Person("xas","987654321",26),_workId(workId){cout << "我是一个老师" << endl;cout << "以下是我的个人信息 :" << endl;cout << endl;cout << "我的工号为 :" << _workId << endl;}
protected:int _workId; // 工号
};int main()
{Student s;s.Print();cout << "---------------------" << endl;cout << "---------------------" << endl;Teacher t;t.Print();return 0;
}

继承后父类的 Person 成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了Student Teacher 复用了 Person 


💢继承的定义💢  

了解完 继承相关概念 后,就可以开始学习 使用继承  

🥝定义格式 

格式如下: Person 是 父类,也称作基类; Student子类,也称作派生类。 

格式为 :子类 : 继承方式 父类,比如 class a : public b 就表示 a 继承了 b,并且还是 公有继承 

注:Java 中的继承符号为 extern,而 C++ 中为 : 


🍇继承权限 

继承有权限的概念,分别为:公有继承(public)、保护继承(protected)、私有继承(private 

没错,与  中的访问 限定修饰符 一样,不过这些符号在这里表示 继承权限 

简单回顾下各种限定符的用途

  • 公有 public:公开的,任何人都可以访问
  • 保护 protected:保护的,只有当前类和子类可以访问
  • 私有 private:私有的,只允许当前类进行访问

权限大小:公有 > 保护 > 私有
保护 protected 比较特殊,只有在 继承 中才能体现它的价值,否则与 私有 作用一样

此时我们发现  ---- 访问权限三种     继承权限三种

根据排列组合,可以列出以下多种搭配方案 

父类成员 / 继承权限publicprotectedprivate
父类的 public 成员外部可见,子类中可见外部不可见,子类中可见外部不可见,子类中可见
父类的 protected 成员外部不可见,子类中可见外部不可见,子类中可见外部不可见,子类中可见
父类的 private 成员都不可见都不可见都不可见

注:所谓的--外部--其实就是 子类对象

总结:

  • 无论是哪种继承方式,父类中的 private 成员始终不可被 [子类 / 外部] 访问;当外部试图访问父类成员时,依据 min(父类成员权限, 子类继承权限),只有最终权限为 public 时,外部才能访问
  • 在实际运用中一般使用都是 public 继承,几乎很少使用 protetced/private 继承,也不提倡使用 protetced/private继承,因为 protetced/private 继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强

如何证明呢? 通过一下的代码,我们来验证上面的结论!!

// 父类
class A
{
public:int _a;
protected:int _b;
private:int _c;
};// 子类
class B : public A
{
public:B(){cout << _a << endl;cout << _b << endl;cout << _c << endl;}
};int main()
{// 外部(子类对象)B b;      b._a;
}
  • public 继承 

  •  protected 继承 

  •   private 继承 

之所以说 C++ 的继承机制设计复杂了,是因为 protected 和 private 继承时的效果一样

其实 C++ 中搞这么多种情况(9种)完全没必要,实际使用中,最常见到的组合为 public : public 和 protected : public 


如何优雅的使用好  ---- 继承权限 

对于只想自己类中查看的成员,设为 private,对于想共享给子类使用的成员,设为 protected其他成员都可以设为 public

比如在张三家中,张三家的房子面积允许公开,家庭存款只限家庭成员共享,而个人隐私数据则可以设为私有

class Home
{
public:int area = 500;	//500 平米的大房子
};class Father : public Home
{
protected:int money = 50000;	//存款五万
private:int privateMoney = 100;	//私房钱,怎能公开?
};class Zhangsan : public Father
{
public:Zhangsan(){cout << "我是张三" << endl;cout << "我知道我家房子有 " << area << " 平方米" << endl;cout << "我也知道我家存款有 " << money << endl;cout << "但我不知道我爸爸的私房钱有多少" << endl;}
};class Xiaoming
{
public:Xiaoming(){cout << "我是小明" << endl;cout << "我只知道张三家房子有 " << Home().area << " 平方米" << endl;cout << "其他情况我一概不知" << endl;}
};int main()
{Zhangsan z;cout << "================" << endl;Xiaoming x;return 0;
}


三、基类与派生类对象的赋值转换 

在继承中,允许将 子类 对象直接赋值给 父类,但不允许 父类 对象赋值给 子类 

  • 这其实很好理解,儿子以后可以当父亲,父亲还可以当儿子吗?

并且这种 赋值 是非常自然的,编译器直接处理,不需要调用 赋值重载 等函数 


派生类对象   可以赋值给      基类的对象 / 基类的指针 / 基类的引用。 

(1) 子类对象可以赋值给父类对象 

//基类
class Person
{
public:string _name; // 姓名string _sex; // 性别int _age; // 年龄
};//派生类
class Student : public Person
{
public:int _id;
};int main()
{Person p;Student s;s._name = "张三";s._sex = "男";s._age = 20;s._id = 8888;p = s; // 子类对象赋值给父类对象return 0;
}

 通过调式可以看到,为什么没有把 id 赋值过去呢?

这里有个形象的说法叫切片或者切割,相当于把派生类中父类那部分切来赋值过去,如图所示: 


(2) 子类对象可以赋值给父类指针  

//基类
class Person
{
public:string _name; // 姓名string _sex; // 性别int _age; // 年龄
};//派生类
class Student : public Person
{
public:int _id;
};int main()
{Student s;s._name = "张三";s._sex = "男";s._age = 20;s._id = 8888;Person* p = &s;return 0;
}

可以看到,当父类对象是一个指针的时候,照样可以赋值过去:

子类对象赋值给父类指针切片图: 


(3) 子类对象可以赋值给父类引用 

//基类
class Person
{
public:string _name; // 姓名string _sex; // 性别int _age; // 年龄
};//派生类
class Student : public Person
{
public:int _id;
};int main()
{Student s;s._name = "张三";s._sex = "男";s._age = 20;s._id = 8888;Person& rp = s;return 0;
}

 可以看到,当父类对象是一个引用的时候,也可以赋值过去:

 子类对象赋值给父类引用切换图片:


(4) 父类对象不能赋值给子类对象

//基类
class Person
{
public:string _name; // 姓名string _sex; // 性别int _age; // 年龄
};//派生类
class Student : public Person
{
public:int _id;
};int main()
{Student s;Person p;s = p;return 0;
}

编译会报错: 


四、继承的作用域 

在继承体系中 基类派生类 都有独立的作用域,如果子类和父类中有同名成员子类成员将屏蔽父类对同名成员的直接访问,这种情况叫 隐藏,也叫重定义。

代码示例: Student 的 _num Person的 _num 构成隐藏关系,可以看出这样代码虽然能跑,但是非常容易混淆。

// 基类
class Person
{
protected:string _name = "Edison"; // 姓名int _num = 555; // 身份证号
};// 派生类
class Student : public Person
{
public:void Print(){cout << "姓名:" << _name << endl;cout << "学号:" << _num << endl;}
protected:int _num = 888; // 学号
};int main()
{Student s1;s1.Print();return 0;
}

 运行可以看到,访问的是子类中的_num (类似于局部优先的原则)

那么如果我想访问父类中的_num 呢 ? 可以使用基类 :: 基类成员显示的去访问 : 

// 基类
class Person
{
protected:string _name = "Edison"; // 姓名int _num = 555; // 身份证号
};// 派生类
class Student : public Person
{
public:void Print(){cout << "姓名:" << _name << endl;cout << " 身份证号:" << Person::_num << endl;}
protected:int _num = 888; // 学号
};int main()
{Student s1;s1.Print();return 0;
}

 可以看到,此时就是访问的父类中的 _num 

还有一点需要注意的是 : 如果是成员函数的隐藏,只需要函数名相同就构成隐藏。 

// 基类
class A
{
public:void fun(){cout << "A::func()" << endl;}
};// 派生类
class B : public A
{
public:void fun(int i){cout << "B::func()" << endl;cout << "func(int i)->" << i << endl;}
};int main()
{B b;b.fun(10);return 0;
}

 可以看到,默认是去调用子类的 fun() 函数,因为成员函数满足函数名相同就构成隐藏。

如果想调用父类的 fun() 还是需要指定作用域 

// 基类
class A
{
public:void fun(){cout << "A::func()" << endl;}
};// 派生类
class B : public A
{
public:void fun(int i){cout << "B::func()" << endl;cout << "func(int i)->" << i << endl;}
};int main()
{B b;b.A::fun();return 0;
}

 运行可以看到,此时就是调用父类中的 fun()

注意 :B 中的 fun 和 A 中的 fun 不是构成函数重载,而是隐藏 !函数重载的要求是在同一作用域里面!!

另外,在实际中在继承体系里面最好不要定义同名的成员。


五、派生类中的默认成员函数 

派生类(子类)也是 ,同样会生成 六个默认成员函数(用户未定义的情况下)

不同于单一的 子类 是在 父类 的基础之上创建的,因此它在进行相关操作时,需要为 父类 进行考虑

这里我们只以下面的两个类为基础,讨论四类默认成员函数:构造函数、拷贝构造、赋值运算符重载、析构函数 

class Person
{
public:Person(const std::string name = std::string(), const int age = 18, const int sex = 1): _name(name), _age(age), _sex(sex){std::cout << "Person()" << std::endl;}Person(const Person& p): _name(p._name), _age(p._age), _sex(p._sex){std::cout << "Person(const Person& p)" << std::endl;}Person& operator= (const Person& p){std::cout << "operator= (const Person& p)" << std::endl;if (this != &p){_name = p._name;}return *this;}~Person(){std::cout << "~Person()" << std::endl;}
protected:std::string _name;int _age = 18;int _sex = 1;
};class Student : public Person
{
public:
protected:long long _st_id;
};

💢默认成员函数的调用 💢

🔥构造函数与析构函数🔥

int main()
{Student st;return 0;
}

output: 

Person()     // s1 的构造
~Person()    // s1 的析构

说明了,派生类的默认构造函数和默认析构函数都会 -----自动调用基类的构造和析构 


🔥拷贝构造🔥 

int main()
{Student st1;Student st2(st1);return 0;
}

 output:

Person()                 // s1 的构造函数
Person(const Person& p)  // s2 拷贝构造
~Person()                // s2 的析构函数
~Person()                // s1 的析构函数

说明了,派生类的默认拷贝构造会自动调用基类的拷贝构造 


🔥赋值运算符重载 🔥

int main()
{Student st1, st2;st1 = st2;return 0;
}

output:

Person()    // s1 的拷贝构造
Person()    // s2 的拷贝构造
operator= (const Person& p)    // 赋值重载
~Person()   // s2 的析构函数
~Person()   // s1 的析构函数

说明了,派生类的默认赋值运算符重载会自动调用基类的赋值运算符重载 


实际上,我们可以将派生类的成员分成三部分:基类成员、内置类型成员、自定义类型成员 

  • 继承相较于我们以前学的类和对象,可以说就是多了基类那一部分

  • 当调用派生类的默认成员函数时,对于基类成员都会调用对应基类的默认成员函数来处理


💢显示成员函数的调用 💢 

🔥构造函数 🔥

当实现派生类的构造函数时,就算不显示调用基类的构造,系统也会自动调用基类的构造 

class Student : public Person
{
public:Student(long long st_id = 111): _st_id(st_id){std::cout << "Student()" << std::endl;}
protected:long long _st_id;
};
int main()
{Student st;return 0;
}

 output:

Person()
Student()
~Person()

如果需要显示的调用基类的构造函数,应该这样写: 

Student(long long st_id = 111): _st_id(st_id), Person("xas", 18)
{std::cout << "Student()" << std::endl;
}

 特别注意:

Person("xas", 18)如果放在初始化列表,那就使显式调用基类的构造函数;如果放在函数体内,那就使创建一个基类的匿名对象 


🔥拷贝构造 🔥

 当实现派生类的拷贝构造时,如果没有显式调用基类的拷贝构造,那么系统就会自动调用基类的构造

class Student : public Person
{
public:Student(long long st_id = 111): _st_id(st_id), Person("xas", 18){std::cout << "Student()" << std::endl;}Student(const Student& s): _st_id(s._st_id){std::cout << "Student(const Student& s)" << std::endl;}
protected:long long _st_id;
};
int main()
{Student st1;Student st2(st1);return 0;
}

output: 

Person()
Student()
Person()	//系统自动调用了基类的构造
Student(const Student& s)
~Person()
~Person()

 也可以像显示调用构造函数一样显式调用基类的拷贝构造:

Student(const Student& s): Person(s), _st_id(s._st_id)
{std::cout << "Student(const Student& s)" << std::endl;
}

🔥赋值运算符重载 🔥

 在实现派生类的赋值运算符重载时,如果没有显式调用基类的赋值运算符重载,系统也不会自动调用基类的赋值运算符重载

class Student : public Person
{
public:Student(long long st_id = 111): _st_id(st_id), Person("xas", 18){std::cout << "Student()" << std::endl;}Student& operator= (const Student& s){std::cout << "operator= (const Student& s)" << std::endl;if (this != &s){_st_id = s._st_id;}return *this;}
protected:long long _st_id;
};
int main()
{Student st1, st2;st1 = st2;return 0;
}

output: 

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

因此,在实现派生类的赋值运算符重载时,必须显示调用基类的赋值运算符重载: 

Student& operator= (const Student& s)
{std::cout << "operator= (const Student& s)" << std::endl;if (this != &s){Person::operator=(s);	//由于基类和派生类的赋值运算符重载构成隐藏,因此要用 :: 指定类域_st_id = s._st_id;}return *this;
}

🔥析构函数🔥 

 在实现派生类的析构函数时,不要显式调用基类的析构函数,系统会在派生类的析构完成后自动调用基类的析构

class Student : public Person
{
public:Student(long long st_id = 111): _st_id(st_id), Person("xas", 18){std::cout << "Student()" << std::endl;}~Student(){std::cout << "~Student()" << std::endl;}
protected:long long _st_id;
};int main()
{Student st1;return 0;
}

output: 

Person()
Student()
~Student()
~Person()
  • 和前面的默认成员函数不同,在实现派生类的析构时,基类的析构不能显式调用
  • 这是因为,如果显示调用了基类的析构,就会导致基类成员的资源先被清理,如果此时派生类成员还访问了基类成员指向的资源就,就会导致野指针问题
  • 因此,必须保证析构顺序为先子后父,保证数据访问的安全

六、继承与友元 

友元关系不能继承,也就是说 基类友元不能访问子类私有和保护成员,只能访问自己的私有和保护成员。 

下面代码中,Display 函数是基类 Person 的友元,但是 Display 函数不是派生类 Student 的友元,也就是说 Display 函数无法访

class Student;class Person
{
public:friend void Display(const Person& p, const Student& s);
protected:string _name; // 姓名
};class Student : public Person
{
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;Display(p, s);return 0;
}

可以看到运行会报错:

如果想让 Display 函数也能够访问派生类Student 的私有和保护成员,只需要在派生类Student 当中进行友元声明。 

class Student;class Person
{
public:friend void Display(const Person& p, const Student& s); // 声明Display是Person的友元
protected:string _name; // 姓名
};class Student : public Person
{
public:friend void Display(const Person& p, const Student& s); // 声明Display是Student的友元
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;Display(p, s);return 0;
}

七、继承与静态成员

 如果基类中定义了static 静态成员,则整个继承体系里面只有一个这样的成员。 无论派生出多少个子类,都只有一个 static 成员实例

下面代码中,在基类 Person 当中定义了静态成员变量 _ count, 派生类 StudentGraduate 继承了Person, 但是,在整个继承体 

// 基类
class Person
{
public:Person() { ++_count; }
protected:string _name; // 姓名
public:static int _count; // 统计人的个数。
};// 静态成员在类外面定义
int Person::_count = 0; // 派生类
class Student : public Person
{
protected:int _stuNum; // 学号
};// 派生类
class Graduate : public Student
{
protected:string _seminarCourse; // 研究科目
};int main()
{Student s1;Student s2;Student s3;Graduate s4;Person s;cout << " 人数 :" << Person::_count << endl;cout << " 人数 :" << Student::_count << endl;cout << " 人数 :" << s4._count << endl;return 0;
}

 我们定义了5个对象,那么每定义一个对象都会去调用一次++ _count ,打印以后可以看到,这几个对象里面的 _count 都是一样的:

同时,我们还可以打印一下地址,可以看到也是同-个: 

 总结: 关于父类中的静态成员,子类继续下来以后都是同一个,类似于“传家宝"。


八、菱形继承 

💧 单继承💧

一个子类只有一个直接父类时称这个继承关系为单继承   

💧 多继承💧

 一个子类有两个或以上直接父类时称这个继承关系为多继承


💧 菱形继承💧 

 C++ 支持多继承,即支持一个子类继承多个父类,使其基础信息更为丰富,但凡事都有双面性多继承 在带来巨大便捷性的同时,也带来了个巨大的坑:菱形继承问题

🍍概念 

首先 C++ 允许出现多继承的情况,如下图所示 

这样看很正常是吧,但如果出现以下这种 重复继承 的情况,就比较麻烦了 

此时 普通人X 会纠结于使用哪一个 不用吃饭 的属性!这对于编译器来说,是一件无法处理的事 

🍉现象 

将上述概念转化为代码,观察实际现象

注:多继承时,只需要在 父类 之后,添加 , 号,继续增加想要继承的父类

class Person
{
public:string _name;	//姓名
};//本科生
class Undergraduate : public Person
{};//研究生
class Postgraduate : public Person
{};//毕业生
class Graduate : public Undergraduate, public Postgraduate
{};int main()
{Graduate g1;g1._name = "zhangsan";return 0;
}

无法编译! 


原因分析: 

 Undergraduate 中继承了 Person 的 _namePostgraduate 也继承了 Person 的 _name

Graduate 多继承 Undergraduate Postgraduate 后,同时拥有了两个 _name,使用时,无法区分!

通过监视窗口查看信息: 


解决方法: 

想要解决二义性很简单,通过 :: 限制访问域即可 

Graduate g1;
g1.Undergraduate::_name = "zhangsan";
cout << g1.Undergraduate::_name << endl;

但这没有从本质上解决问题!而且还没有解决数据冗余问题 

 真正的解决方法:虚继承

 注:虚继承是专门用来解决 菱形继承 问题的,与多态中的虚函数没有直接关系


虚继承:在菱形继承的腰部继承父类时,加上 virtual 关键字修饰被继承的父类 

class Person
{
public:string _name;	//姓名
};//本科生
class Undergraduate : virtual public Person
{};//研究生
class Postgraduate : virtual public Person
{};//毕业生
class Graduate : public Undergraduate, public Postgraduate
{};int main()
{Graduate g1;g1._name = "zhangsan";cout << g1._name << endl;return 0;
}

此时可以解决 菱形继承 的 数据冗余 和 二义性 问题 

 虚继承是如何解决菱形继承问题的?

  • 利用 虚基表 将冗余的数据存储起来,此时冗余的数据合并为一份
  • 原来存储 冗余数据 的位置,现在用来存储 虚基表指针

此时无论这个 冗余 的数据存储在何处,都能通过 基地址 + 偏移量 的方式进行访问 


九、继承和组合 

public 继承是一种 is-a 的关系。也就是说每个派生类对象都是一个基类对象。 
组合 是一种 has-a 的关系。假设 B 组合了 A,每个 B对象中都有一个 A对象。

举个例子: 轿车和奔驰就构成 is-a 的关系,所以可以使用继承。 

// 车类
class Car
{
protected:string _colour = "黑色"; // 颜色string _num = "川A66688"; // 车牌号
};// 奔驰
class Benz : public Car
{
public:void Drive(){cout << "好开-操控" << endl;}
};

再举个例子:汽车和轮胎之间就是 has-a 的关系,它们之间则适合使用组合。 

// 轮胎
class Tire {
protected:string _brand = "Michelin"; // 品牌size_t _size = 17; // 尺寸};// 汽车
class Car {
protected:string _colour = "黑色"; // 颜色string _num = "川A66688"; // 车牌号Tire _t; // 轮胎
};

 实际项目中,更推荐使用 组合 的方式,这样可以做到 解耦,避免因父类的改动而直接影响到子类

  • 公有继承:is-a —> 高耦合,可以直接使用父类成员
  • 组合:has-a —> 低耦合,可以间接使用父类成员

十、继承的总结和反思 

 1. 很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设计出菱形继承。否则在复杂度及性能上都有问题。


2. 多继承可以认为是C++的缺陷之一,很多后来的面向对象语言都没有多继承,如Java。


十一、共勉 

 以下就是我对 【C++】继承 的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对 【C++】多态 的理解,请持续关注我哦!!!    

相关文章:

【C++】继承最全解析(什么是继承?继承有什么用?)

目录 一、前言 二、什么是继承 ? &#x1f4a2;继承的概念&#x1f4a2; &#x1f4a2;继承的定义&#x1f4a2; &#x1f95d;定义格式 &#x1f347;继承权限 三、基类与派生类对象的赋值转换 四、继承的作用域 五、派生类中的默认成员函数 &#x1f4a2…...

STM32-外部中断浅析

本篇解释了STM32中断原理 MCU为什么需要中断 中断&#xff0c;是嵌入式系统中很重要的一个功能&#xff0c;在系统运行过程中&#xff0c;当出现需要立刻处理的情况时&#xff0c;暂停当前任务&#xff0c;转而处理紧急任务&#xff0c;处理完毕后&#xff0c;恢复之前的任务…...

Spring-Data-Elasticsearch

简介 Spring Data for Elasticsearch 是 Spring Data 项目的一部分&#xff0c;该项目旨在为新数据存储提供熟悉且一致的基于 Spring 的编程模型&#xff0c;同时保留特定于存储的特性和功能。 Spring Data Elasticsearch 项目提供了与 Elasticsearch 搜索引擎的集成。Spring…...

代码随想录二刷7.22|977.有序数组的平方

暴力解法&#xff1a; ——如果想暴力解决这个问题的话&#xff0c;可以像题目那样&#xff0c;先将每一个元素平方&#xff0c;然后再排序 双指针&#xff1a; ——从题目中找到的信息&#xff1a;这是一个非递减顺序的整数数组&#xff0c;从例子中&#xff0c;可以容易看…...

redis介绍与布署

redis remote dictionary server&#xff08;远程字典服务器&#xff09; 是一个开源的&#xff0c;使用c语言编写的非关系型数据库&#xff0c;支持内存运行并持久化&#xff0c;采用key-value的存储形式。 单进程模型意味着可以在一台服务器上启动多个redis进程&#xff0c;…...

PMON的解读和开发

提示&#xff1a;龙芯2K1000PMON相关记录 文章目录 1 PMON的发展和编译环境PMONPMON2000 2 PMON2000的目录结构3 Targets目录的组成4 PMON编译环境的建立5 PMON2000的框架6 异常向量表7 Pmon的空间分配8 PMON的汇编部分(starto.S或sbdreset.S)的解读Start.SC代码部分dbginit 9 …...

初识c++(构造函数,析构函数,拷贝构造函数,赋值运算符重载)

一、类的默认函数 默认成员函数就是用户没有显式实现&#xff0c;编译器会自动生成的成员函数称为默认成员函数。 #include<iostream> using namespace std; class Date { public:Date(){_year 1;_month 1;_day 1;cout << _year << "/" <&…...

CANoe:为什么两个VLAN接口不能设置同一个网络的IP地址呢?

经常玩CANoe的人应该配置过TCP/IP Stack中网络节点的网卡信息&#xff0c;基本的信息包含&#xff1a;MAC地址、IP地址、子网掩码、默认网关、MTU值、IPv6地址。 如果你想让发送出去的报文携带VLAN tag&#xff0c;可以在网卡上添加VLAN tag信息。 此时你就能得到两个新的网卡V…...

SpringBoot新手快速入门系列教程七:基于一个低配centoos服务器,如何通过宝塔面板部署一个SpringBoot项目

1&#xff0c;如何打包一个项目 通过IDEA自带的命令行&#xff0c;执行 ./gradlew clean build 2&#xff0c;检查生成的JAR文件 进入 build/libs 目录&#xff0c;你应该会看到一个类似 helloredis-0.0.1-SNAPSHOT.jar 的文件。 3&#xff1a;运行生成的JAR文件 你可以在…...

性能测试的流程(企业真实流程详解)(二)

性能测试的流程 1.需求分析以及需求确定(指标值&#xff0c;场景&#xff0c;环境&#xff0c;人员) 一般提出需求的人员有&#xff1a;客户&#xff0c;产品经理&#xff0c;项目组领导等 2.性能测试计划和方案制定 基准测试: 负觋测试: 压力测试: 稳定性测试: 其他:配置测试…...

使用sklearn的基本流程

scikit-learn&#xff0c;通常简称为 sklearn&#xff0c;是一个开源的Python库&#xff0c;是基于 Python 编程语言的一个非常流行的机器学习库。它建立在 NumPy 和 SciPy 这两个科学计算库之上&#xff0c;并与 Matplotlib 配合使用&#xff0c;为数据预处理、模型训练、评估…...

力扣题解(乘积为正数的最长子数组长度)

1567. 乘积为正数的最长子数组长度 已解答 中等 给你一个整数数组 nums &#xff0c;请你求出乘积为正数的最长子数组的长度。 一个数组的子数组是由原数组中零个或者更多个连续数字组成的数组。 请你返回乘积为正数的最长子数组长度。 本题要求乘积为正数&#xff0c;而整…...

PPTP、L2TP、IPSec、IPS 有什么区别?

随着互联网的发展&#xff0c;保护网络通信的安全越来越重要。PPTP、L2TP、IPSec、IPS是常见的网络安全协议和技术&#xff0c;在保护网络通信安全方面发挥着不同的作用和特点。下面介绍PPTP、L2TP、IPSec、IPS之间的区别。 点对点隧道协议&#xff08;PPTP&#xff09;是一种用…...

SpringBoot注解--11--@JSONField @JsonProperty

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一个问题&#xff1a;后端实体类isXXX开头的属性&#xff0c;传到前端后自动去掉is解决方法&#xff1a; JsonProperty和JSONField1.简介2.注解的区别2.1 底层框架不…...

C语言 | Leetcode C语言题解之第221题最大正方形

题目&#xff1a; 题解&#xff1a; int maximalSquare(char** matrix, int matrixSize, int* matrixColSize){int dp[301][301]{0};int wid0;if(matrixSize0&&matrixColSize[0]0){return 0;}for(int i0;i<matrixSize;i){for(int j0;j<matrixColSize[0];j){if(m…...

AI数据服务如何驱使AI商业化,实现在各行业落地融合

AI技术经历数十载的发展&#xff0c;正在加速向各行各业渗透。近年来深度学习加速了人工智能技术的商业化落地的同时&#xff0c;也带来了大量AI算法训练需求&#xff0c;推动AI数据服务市场的快速增长。作为AI快速发展的“基石”&#xff0c;AI数据服务AI数据服务如何驱使AI在…...

户用光伏项目开发流程

1、收集业主信息 管理业主基本信息&#xff0c;包括但不限于联系方式、地址、房屋信息等。 2、业主开卡 每户都需要办理银行卡&#xff0c;用于电费结算和划转。 3、合同签约 业主开卡完成之后&#xff0c;平台方发起签约&#xff08;支持线上签约&#xff09;&#xff0c…...

C++ 函数返回值是引用类型使用场景

目录 1、希望返回函数内部的局部静态变量或全局变量 2、希望通过函数返回一个对象的成员变量 3、希望实现链式操作 4、避免对象的拷贝 5、需要注意的事项 在C中&#xff0c;函数的返回值可以是引用类型的情况主要有以下几种&#xff1a; 1、希望返回函数内部的局部静态变…...

CUDA原子操作

代码 #include <cuda_runtime.h> #include <stdio.h>__global__ void atomicAddAndGet(int *result, int *valueToAdd) {// 原子加法int addedValue atomicAdd(result, *valueToAdd);// 通过原子操作后读取值&#xff0c;确保是加法后的值addedValue *valueToAd…...

08.C2W3.Auto-complete and Language Models

往期文章请点这里 目录 N-Grams: OverviewN-grams and ProbabilitiesN-gramsSequence notationUnigram probabilityBigram probabilityTrigram ProbabilityN -gram probabilityQuiz Sequence ProbabilitiesProbability of a sequenceSequence probability shortcomingsApproxi…...

【linux】log 保存和过滤

log 保存 ./run.sh 2>&1 | tee -a /home/name/log.txt log 过滤 import os import re# Expanded regular expression to match a wider range of error patterns error_patterns re.compile(# r(error|exception|traceback|fail|failed|fatal|critical|warn|warning…...

GeoTrust ——适合企业使用的SSL证书!

GeoTrust是一家全球知名的数字证书颁发机构&#xff08;CA&#xff09;&#xff0c;其提供的SSL证书非常适合企业使用。GeoTrust的SSL证书为企业带来了多重优势&#xff0c;不仅在验证级别、加密强度、兼容性、客户服务等方面表现出色&#xff0c;而且其高性价比和灵活的证书选…...

Kubelet 认证

当我们执行kubectl exec -it pod [podName] sh命令时&#xff0c;apiserver会向kubelet发起API请求。也就是说&#xff0c;kubelet会提供HTTP服务&#xff0c;而为了安全&#xff0c;kubelet必须提供HTTPS服务&#xff0c;且还要提供一定的认证与授权机制&#xff0c;防止任何知…...

aws slb

NLB 目标组 Target is in an Availability Zone that is not enabled for the load balancer 解决&#xff1a; https://docs.aws.amazon.com/zh_cn/elasticloadbalancing/latest/network/load-balancer-troubleshooting.html 负载均衡器添加 后端EC2 所在的vpc网段即可。…...

【AI大模型】ChatGPT-4 对比 ChatGPT-3.5:有哪些优势

引言 ChatGPT4相比于ChatGPT3.5,有着诸多不可比拟的优势&#xff0c;比如图片生成、图片内容解析、GPTS开发、更智能的语言理解能力等&#xff0c;但是在国内使用GPT4存在网络及充值障碍等问题&#xff0c;如果您对ChatGPT4.0感兴趣&#xff0c;可以私信博主为您解决账号和环境…...

详解yolov5的网络结构

转载自文章 网络结构图&#xff08;简易版和详细版&#xff09; 此图是博主的老师&#xff0c;杜老师的图 网络框架介绍 前言&#xff1a; YOLOv5是一种基于轻量级卷积神经网络&#xff08;CNN&#xff09;的目标检测算法&#xff0c;整体可以分为三个部分&#xff0c; ba…...

汽车零配件行业看板管理系统应用

生产制造已经走向了精益生产&#xff0c;计算时效产出、物料周转时间等问题&#xff0c;成为每一个制造企业要面临的问题&#xff0c;工厂更需要加快自动化&#xff0c;信息化&#xff0c;数字化的布局和应用。 之前的文章多次讲解了企业MES管理系统&#xff0c;本篇文章就为大…...

【Go】函数的使用

目录 函数返回多个值 init函数和import init函数 main函数 函数的参数 值传递 引用传递&#xff08;指针&#xff09; 函数返回多个值 用法如下&#xff1a; package mainimport ("fmt""strconv" )// 返回多个返回值&#xff0c;无参数名 func Mu…...

宝塔面板运行Admin.net框架

准备 宝塔安装 .netcore安装 Admin.net框架发布 宝塔面板设置 完结撒花 1.准备 服务器/虚拟机一台 系统Windows server / Ubuntu20.04&#xff08;本贴使用的是Ubuntu20.04版本系统&#xff09; Admin.net开发框架 先安装好服务器系统&#xff0c;这里就不做安装过程描述了&…...

Javaweb11-Filter过滤器

Filter过滤器 1.Filter的基本概念&#xff1a; 在Java Servlet中&#xff0c;Filter接口是用来处理HttpServletRequest和HttpServletResponse的对象的过滤器。主要用途是在请求到达Servlet之前或者响应离开Servlet之前对请求或响应进行预处理或后处理。 2.Filter常见的API F…...

wordpress网址更换/杭州网站制作排名

哲学家就餐问题问题描述哲学家就餐问题(Dining philosophers problem)可以这样表述&#xff0c;假设有五位哲学家围坐在一张圆形餐桌旁&#xff0c;做以下两件事情之一&#xff1a;吃饭&#xff0c;或者思考。吃东西的时候&#xff0c;他们就停止思考&#xff0c;思考的时候也停…...

专业做外贸网站公司/什么是电商平台推广

插件主页: http://www.yhuan.com/ 求助帖&#xff0c;每帖必应 QQ交流群:74106519 加入QQ群&#xff0c; 4.0.1修复了4.0目前已知的所有BUG&#xff0c;以及以前版本一直未解决的问题。 2011/5/22 jQuery formValidator 4.0.1 ver 规范插件的命名&#xff1a;1、对插件所有函数…...

网站建设区域加盟/100个裂变营销案例

十几岁的孩子常常会摔门、翻白眼&#xff0c;还会显出一副冷冰冰的样子&#xff0c;甚至连之前表现友善的孩子也是这样。一些家长会担心自己做错了什么&#xff0c;或者担心孩子会永远不考虑别人而只考虑自己。新的研究显示&#xff0c;这种现象归咎于生理&#xff0c;而不是家…...

门户网站排版/怎么去推广自己的平台

我试图运行/在我的vServer的安装码头工人&#xff0c;如果它甚至有可能无法找到的信息。我试过的CentOS(6 & 7)&#xff0c;Ubuntu的&#xff0c;Debian和Fedora的现在&#xff0c;我只是不能让docker守护进程运行。在虚拟服务器上运行docker-可能与否&#xff1f;docker.s…...

群晖 搭建两个wordpress/seo网络推广外包公司

Spring AOP 提供了 5 种类型的通知&#xff0c;它们分别是 Before Advice&#xff08;前置通知&#xff09;、After Returning Advice&#xff08;后置通知&#xff09;、Interception Around Advice&#xff08;周围通知)、Throws Advice&#xff08;异常通知&#xff09;和 I…...

网站设计团队有哪些职业/最好用的免费建站平台

在正式开始解码练习前先了解下关于FFmpeg中比较重要的函数以及数据结构。 1. 数据结构&#xff1a; (1) AVFormatContext AVFormatContext是一个贯穿始终的数据结构&#xff0c;很多函数都要用到它作为参数。FFmpeg代码中对这个数据结构的注释是&#xff1a;format I/O conte…...