C++进阶-继承

news2025/1/21 15:32:34

继承

  • 一、继承的概念及定义
    • 1.1 继承的概念
    • 1.2 继承的定义
      • 1.2.1 定义格式
      • 1.2.2 继承关系和访问限定符
      • 1.2.3 继承基类成员访问方式的变化
  • 二、基类和派生类对象赋值转换
  • 三、继承中的作用域
    • 3.1 同名成员变量
    • 3.2 同名成员函数
  • 四、派生类的默认成员函数
  • 五、继承与友元
  • 六、继承与静态成员
  • 七、复杂的菱形继承及菱形虚拟继承
  • 八、继承的总结和反思

一、继承的概念及定义

1.1 继承的概念

  继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用

class Person
{
public:
	void Print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
protected:
	string _name = "peter"; // 姓名
	int _age = 18; // 年龄
};

class Student : public Person
{
protected:
	int _stuid; // 学号
};
class Teacher : public Person
{
protected:
	int _jobid; // 工号
};

int main()
{
	Student s;
	Teacher t;
	s.Print();
	t.Print();
	
	return 0;
}

  继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了Student和Teacher复用了Person的成员。使用监视窗口查看Student和Teacher对象,可以看到变量的复用。调用Print可以看到成员函数的复用。

1.2 继承的定义

1.2.1 定义格式

  下面我们看到Person是父类,也称作基类。Student是子类,也称作派生类。
在这里插入图片描述

1.2.2 继承关系和访问限定符

在这里插入图片描述

1.2.3 继承基类成员访问方式的变化

类成员/继承方式public继承protected继承private继承
基类的public成员派生类的public成员派生类的protected成员派生类的private成员
基类的protected成员派生类的protected成员派生类的protected成员派生类的private成员
基类的private成员在派生类中不可见在派生类中不可见在派生类中不可见

总结:

  1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。
  2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
  3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected> private。
  4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
  5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。
class Person
{
public :
	void Print ()
	{
	cout<<_name <<endl;
	}
protected :
	string _name ; // 姓名
private :
	int _age ; // 年龄
};

//class Student : protected Person
//class Student : private Person
class Student : public Person
{
protected :
	int _stunum ; // 学号
};

实例演示三种继承关系下基类成员的各类型成员访问关系的变化

public继承
protected访问限定符修饰的变量在派生类内能被访问
private访问限定符修饰的变量在派生类中不可见
public访问限定符修饰的变量在派生类内外都可以被访问

protected继承
protected访问限定符修饰的变量在派生类内能被访问
private访问限定符修饰的变量在派生类中不可见
经过protected继承后,父类中由public访问限定符修饰的函数(及成员变量)在派生类外不能被访问

private继承
protected访问限定符修饰的变量在派生类内能被访问
private访问限定符修饰的变量在派生类中不可见
经过private继承后,父类中由public访问限定符修饰的函数(及成员变量)在派生类外不能被访问

二、基类和派生类对象赋值转换

1.派生类对象可以赋值给基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。
2.基类对象不能赋值给派生类对象。
3.基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(RunTime Type Information)的dynamic_cast 来进行识别后进行安全转换。
在这里插入图片描述
注意:只有public才能切割,而private和protected不能被切割
将派生类中基类的那部分切割进行赋值过去。
在这里插入图片描述
将派生类引用赋给基类引用不是类型转换,是语法天然支持的行为。引用是引用派生类中基类的那部分成员变量。
在这里插入图片描述
指针指向的是派生类中基类的那部分成员变量

class Person
{
protected :
	string _name; // 姓名
	string _sex; // 性别
	int _age; // 年龄
};
class Student : public Person
{
public :
	int _No ; // 学号
};
void Test ()
{
	Student sobj ;
	// 1.子类对象可以赋值给父类对象/指针/引用
	Person pobj = sobj ;
	Person* pp = &sobj;
	Person& rp = sobj;
	
	//2.基类对象不能赋值给派生类对象
	sobj = pobj;
	
	// 3.基类的指针可以通过强制类型转换赋值给派生类的指针
	pp = &sobj
	Student* ps1 = (Student*)pp; // 这种情况转换时可以的。
	ps1->_No = 10;
	
	pp = &pobj;
	Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问
题
	ps2->_No = 10;
}

三、继承中的作用域

  1. 在继承体系中基类派生类都有独立的作用域
  2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问
  3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
  4. 注意在实际中在继承体系里面最好不要定义同名的成员

3.1 同名成员变量

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; // 学号
};

void Test()
{
	Student s1;
	s1.Print();
};

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

3.2 同名成员函数

class A
{
public:
	void fun()
	{
		cout << "func()" << endl;
	}
};
class B : public A
{
public:
	void fun(int i)
	{
		A::fun();
		cout << "func(int i)->" <<i<<endl;
	}
};

void Test()
{
	B b;
	b.fun(10);
};

  B中的fun和A中的fun不是构成重载,因为不是在同一作用域
  B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。

四、派生类的默认成员函数

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

  1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
  2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
  3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
  4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
  5. 派生类对象初始化先调用基类构造再调派生类构造。
  6. 派生类对象析构清理先调用派生类析构再调基类的析构。
  7. 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。
    在这里插入图片描述

在这里插入图片描述
先进行基类函数的构造,再进行派生类函数的构造,再对派生类函数进行析构,最后对基类函数进行析构。

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(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 ;
}
	//析构函数会被处理成destructor
	~Student()
	{
		//Person::~Person();     //析构函数不要去显示调用,自己写不能保证先子后父
		cout<<"~Student()" <<endl;
	}
	//子类析构函数完成时,会自动调用父类析构函数,保证先析构子再析构父
protected :
	int _num ; //学号
};

void Test ()
{
	Student s1 ("jack", 18);
	Student s2 (s1);
	Student s3 ("rose", 17);
	s1 = s3 ;
}

五、继承与友元

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

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;
}
void main()
{
	Person p;
	Student s;
	Display(p, s);
}

可以看到虽然子类继承了父类,而父类的友元函数Display只能访问父类的成员,而子类的却不可以被访问(但是子类依然可以调用父类的成员)
在子类中也进行函数Display友元关系的声明就可以访问到子类的成员变量

六、继承与静态成员

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

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 ; // 研究科目
};

void TestPerson()
{
	Student s1 ;
	Student s2 ;
	Student s3 ;
	Graduate s4 ;
	//静态成员的继承,继承下来变量还是同一个,即_count还是同一个
	cout <<" 人数 :"<< Person ::_count << endl;
	Student ::_count = 0;
	cout <<" 人数 :"<< Person ::_count << endl;
}

创建构造了4个父类Person对象,因为_count是静态变量,不进行销毁,所以第一次输出的人数是4。
第二次输出人数时,Student类中对_count进行置零,输出“人数:0”。

七、复杂的菱形继承及菱形虚拟继承

单继承:一个子类只有一个直接父类时称这个继承关系为单继承
在这里插入图片描述
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
在这里插入图片描述
菱形继承:菱形继承是多继承的一种特殊情况。
在这里插入图片描述
  菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。
在这里插入图片描述

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 ; // 主修课程
};
void Test ()
{
	// 这样会有二义性无法明确知道访问的是哪一个
	Assistant a ;
	a._name = "peter";

	// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
	//数据冗余和二义性
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
}

  虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。

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 ; // 主修课程
};
void Test ()
{
	Assistant a ;
	a._name = "peter";
}

虚拟继承解决数据冗余和二义性的原理
  为了研究虚拟继承原理,我们给出了一个简化的菱形继承继承体系,再借助内存窗口观察对象成员的模型。

class A
{
public:
	int _a;
};

// class B : public A
class B : virtual public A
{
public:
	int _b;
};

// class C : public A
class C : virtual public A
{
public:
	int _c;
};

class D : public B, public C
{
public:
	int _d;
};

int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	
	return 0;
}

  下图是菱形继承的内存对象成员模型:这里可以看到数据冗余
在这里插入图片描述
  下图是菱形虚拟继承的内存对象成员模型:这里可以分析出D对象中将A放到的了对象组成的最下面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。
在这里插入图片描述

// 为什么D中B和C部分要去找属于自己的A?那么大家看看当下面的赋值发生时,d是
不是要去找出B/C成员中的A才能赋值过去?
D d;
B b = d;
C c = d;

下面是上面的Person关系菱形虚拟继承的原理解释:
在这里插入图片描述
  d中的继承父类B和C中都继承了A类,均对于A类中的成员变量_a进行了赋值,且都放在了不同的地址空间上面。

在这里插入图片描述

八、继承的总结和反思

  1. 很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设计出菱形继承。否则在复杂度及性能上都有问题。
  2. 多继承可以认为是C++的缺陷之一,很多后来的OO(面向对象)语言都没有多继承,如Java。
  3. 继承和组合
    (1)public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
    (2)组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
    (3)优先使用对象组合,而不是类继承。
    (4)继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。
    (5)对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。
    (6)实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用继承,可以用组合,就用组合。

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

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

相关文章

TLC549(8位A/D转换器)实现将输入的模拟电压显示到数码管上

代码&#xff1a; #include "reg51.h" #include "intrins.h" #define uchar unsigned char #define uint unsigned intsbit SDO P1^0; // 芯片的三个关键的输入数据端口&#xff0c;主要是靠外电压来提供的 sbit CS P1^1; sbit SCLK P1^2;sbit wei0 P…

Python办公自动化Day1

目录 文章声明⭐⭐⭐让我们开始今天的学习吧&#xff01;xlwt创建Excelxlrd读取Excelxlutils修改Excelxlwt设置样式常见的字体样式单元格宽高内容对齐方式设置单元格边框设置背景颜色样式整合起来的写法 文章声明⭐⭐⭐ 该文章为我&#xff08;有编程语言基础&#xff0c;非编…

【滑动窗口】之 长度最小的子数组

209. 长度最小的子数组 暴力循环法&#xff1a; public int minSubArrayLen(int target, int[] nums) {int min Integer.MAX_VALUE;for (int i 0; i < nums.length; i) {int sum nums[i];//只要有个元素 &#xff1e; 目标值 就返回最小长度1if (sum > target) {retur…

react中使用redux最简单最方便的方式,配合rematch简化操作,5分钟学会

react中使用状态管理的方式也很多&#xff0c;比如redux和mobx等&#xff0c;今天这一片就讲一下redux的入门到熟练使用&#xff0c;主要是要理解它redux的组成有哪些&#xff0c;到怎么创建&#xff0c;和组建中怎么使用三个问题。这里先放上官网文档&#xff0c;不理解的地方…

2.苍穹外卖-day02

苍穹外卖-day02 课程内容 新增员工 员工分页查询 启用禁用员工账号 编辑员工 导入分类模块功能代码 功能实现&#xff1a;员工管理、菜品分类管理。 员工管理效果&#xff1a; 菜品分类管理效果&#xff1a; 1. 新增员工 1.1 需求分析和设计 1.1.1 产品原型 一般在做需求分…

在Linux Docker中部署RStudio Server,实现高效远程访问

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、Cpolar杂谈 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 安装RStudio Server二. 本地访问三. Linux 安装cpolar四. 配置RStudio serv…

通过windows cng api 实现rsa非对称加密

参考&#xff1a; 1,使用 CNG 加密数据 - Win32 apps | Microsoft Learn 2,不记得了 &#xff08;下文通过cng api演示rsa加密&#xff0c;不做原理性介绍&#xff09; 相对于aes等对称加密算法&#xff0c;rsa加密算法不可逆性更强。非对称加密在通常情况下&#xff0c;使…

玩转大数据22:常见的关联规则挖掘算法

引言 关联规则挖掘是数据挖掘中的一种重要技术&#xff0c;主要用于发现数据集中项之间的有趣关系。关联规则挖掘在许多领域都有广泛的应用&#xff0c;如市场篮子分析、推荐系统等。常见的关联规则挖掘算法包括Apriori算法和FP-Growth算法。 一、Apriori算法 关联规则挖掘是…

Java中的抽象abstract

抽象abstract 什么是抽象类抽象类的注意事项、特点 使用好处常见应用场景&#xff1a;模版方法设计模式可以使用final关键字修饰模版方法 什么是抽象类 在Java中有一个关键字叫:abstract&#xff0c;它就是抽象的意思&#xff0c;可以用它修饰类、成员方法。abstract修饰类&am…

python读取Excel内容并展示成json

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 伙伴们&#xff0c;又是许久未曾见面了。最近也是在忙着加班&#xff0c;加上没有新技术的输入和产出&…

SpaceDesk如何连接平板/PC(生产力副屏)

1、下载安装 分为安卓端和PC端&#xff0c;两个设备都需要安装对应的软件。 SpaceDesk官网 https://link.zhihu.com/?targethttp%3A//spacedesk.net/ 需要魔法上网。安装过程比较简单&#xff0c;无脑下一步即可。 我已经把安装包准备好了&#xff0c;如果不想自己找&#…

公众号申请已达上限解决方法

一般可以申请多少个公众号&#xff1f;自2018年11月16日起&#xff0c;进行申请上限调整&#xff1a;1、同一个邮箱只能申请1个公众号&#xff1b;2、同一个手机号码可绑定5个公众号&#xff1b;3、同一身份证申请个人类型公众号数量上限为1个&#xff1b;4、同一企业、个体工商…

WebAssembly 的魅力:高效、安全、跨平台(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

ElasticSearch学习篇9_文本相似度计算方法现状以及基于改进的 Jaccard 算法代码实现

背景 XOP亿级别题库的试题召回以及搜题的举一反三业务场景都涉及使用文本相似搜索技术&#xff0c;学习此方面技术以便更好的服务于业务场景。 目前基于集合的Jaccard算法以及基于编辑距离的Levenshtein在计算文本相似度场景中有着各自的特点&#xff0c;为了优化具体的计算时…

windos/ubuntu20.4下UE4.27.2像素流送

windows/ubuntu20.4下UE4.27.2像素流送 像素流送技术可以将服务器端打包的虚幻引擎应用程序在客户端的浏览器上运行&#xff0c;用户可以通过浏览器操作虚幻引擎应用程序&#xff0c;客户端无需下载虚幻引擎&#xff0c;本文实现两台机器通过物理介质网线实现虚幻引擎应用程序…

Jenkins Pipeline脚本优化:为Kubernetes应用部署增加状态检测

引言 在软件部署的世界中&#xff0c;Jenkins已经成为自动化流程的代名词。不断变化的技术环境要求我们持续改进部署流程以满足现代应用部署的需要。在本篇博客中&#xff0c;作为一位资深运维工程师&#xff0c;我将分享如何将Jenkins Pipeline进化至不仅能支持部署应用直至R…

【电源专题】Buck电源上电震荡谁的错?

在文章:【电源专题】案例:Buck芯片上电瞬间波形震荡?从别的人案例中来学习软启参数中我们通过别人的文章了解到了Buck芯片上电瞬间波形震荡有几个方法可以解决,但主要还是围绕着软启动参数去学习。因为文章中无法知道编者所用的电源芯片和电路,所以无法进行分析。 最近我…

Mysql之约束下篇

Mysql之约束下篇 自增列(AUTO_INCREMENT)关键字特点和要求添加自增约束删除自增约束Mysql8.0新特性-自增变量的持久化 FOREIGN KEY 约束关键字主表和从表/父表和子表特点添加外键约束约束等级删除外键约束面试问题 DEFAULT约束作用关键字添加默认值约束删除默认值约束 CHECK约束…

Leetcode—445.两数相加II【中等】

2023每日刷题&#xff08;六十七&#xff09; Leetcode—445.两数相加II 实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2…

k8s启动docker容器Error: Could not find or load main class ${start-class}报错

前行提要&#xff1a; 今天部署采集点服务&#xff08;docker项目&#xff09;发现报这个错误。 提出假设&#xff1a; 1&#xff0c;配置文件错误&#xff08;工程需要配置的东西比较多&#xff09; 之后开始一一排查&#xff0c;发现配置有问题&#xff0c;但是不是这个错误…