超详细的 C++中的封装继承和多态的知识总结<1.封装>

引言

  小伙伴们都知道C++面向对象难,可是大家都知道,这个才是C++和C的真正区别的地方,也是C++深受所有大厂喜爱的原因,它的原理更接近底层,它的逻辑更好,但是学习难度高,大家一定要坚持下来呀,本章呢对于C++有关的知识开始讲解封装继承和多态。好了啦废话不多说,跟着小杨一起开始吧!

  冲冲冲!!!!!!

封装

  • 封装的关键字是clas。
  • 从上一章当中,我们已经知道了,封装就是现实生活中的事物定义为类,将事物的数据抽象为属性,将事物的行为抽象为方法。

类的数据成员和成员函数

  • 数据成员
    • 类中的数据成员描述该类对象的属性,数据成员必须在类体中定义,其定义方式须与一般变量相同,但对于数据成员的访问要手访问控制权限的限定。
    • 数据成员的初始化与普通变量的初始化形式有所不同,不能使用圆括号(即对象方式初始化)可以使用=和{ }。
    • 多个数据成员之间不能重名,一个类是一个作用域。
  • 成员函数
    • 成员函数描述类对象的行为,即该类对象的所执行的操作。
    • 一个类的多个同名不同参数类型的成员函数可以重载。
    • 类的成员函数也可以是内联函数。
    • 函数成员参数也可以有默认值。
  • 成员的访问控制
    • private私有访问权限
      • 数据成员和成员函数只允许访问类本身的成员函数访问,对类的外部不可见。
    • protected保护访问权限
      • 数据成员和成员函数允许类本身及其派生类的成员函数访问
    • public共有访问权限
      • 数据成员和成员函数对外类外部可见,类内部也能访问

类的定义代码示例:

#include <iostream>

using namespace std;
/*
	class关键字定义类。
	类名是标识符,需要满足标识符规范,类名命名规范是大驼峰,
	每个单词首字母大写,其他字母小写。
	类名后一对花括号表示类的作用域,也称为类体,分号表示类定义结束。
	关键字private,protected,public称为访问控制机制。默认为private。
*/
class Rect
{
private:
	//	属性
	int m_length;
	int m_width;
public:
	/*
		类中的成员函数(方法)可以再类中直接定义,也可以只写函数声明,然后在类的外面写出函数定义。
	*/	
	//	方法
	void setLength(int length);
	//	函数成员可以内联
	inline void setWidth(int width = 0);
	int getArea();
	int getPerimeter();
};

void Rect::setLength(int length)
{
	m_length = length;
}

void Rect::setWidth(int width)
{
	m_width = width;
}

int Rect::getArea()
{
	return m_width * m_length;
}

int Rect::getPerimeter()
{
	return 2 * (m_width + m_length);
}

int main()
{
	//	定义(创建)Rect类的对象r。
	Rect r, r1;
	//	.操作符访问成员,可以访问数据成员或成员函数。
	r.setLength(2);
	r.setWidth(3);
	cout << r.getArea() << endl;
	cout << r.getPerimeter() << endl;	
	
	//	多个对象之间的属性互相独立。
	r1.setLength(1);
	//	函数成员参数可以有默认值
	r1.setWidth();
	cout << r1.getArea() << endl;
	cout << r1.getPerimeter() << endl;
	return 0;
}
  • 类的特殊成员
      在类中,除了一些简单的数据成员和成员函数外,还有一些具有特殊作用,和特殊规范的函数,这些类的特殊成员正是封装的厉害之处,也是满足各种各样要求的一大利器,让我们一起来看一下吧。

构造函数

  • 在创建对象时,利用特定的值构造对象,将对象初始化为一个特定状态。
  • 构造函数也是类的成员函数,除具有一般成员函数的特点外,还有以下特点:
    • 构造函数的函数名和类名相同
    • 不能定义构造函数的类型,因为构造函数没有返回值,也就没有返回值类型。
    • 构造函数不在程序中调用,在对象被创建时,被编译器调用,
    • 构造函数可以被重载
    • 如果类中没有构造函数,则在C++编译器中会默认自动生成一个无参的默认构造函数,一旦用户显示定义任何构造函数,默认构造函数将不再提供。
    • 无论任何方式创建对象,公祖奥函数都会被调用,如果找不到和参数匹配的构造函数,编译器会产生错误。
  • 初始化列表
    • 除了在构造函数体中为数据成员初始化,还可以使用初始化列表对数据成员初始化。
    • 类的常量成员需要初始化时,只能在初始化列表初始化。
  • 构造函数代码示例:
#include <iostream>

using namespace std;

class Rect
{
private:	
	int m_length;
	int m_width;
public:
	/*
		构造函数和类同名且没有返回类型。
		构造函数允许重载。
		没有定义构造函数时,编译器会提供默认的无参构造函数,当定义任何构造函数后,编译器不再提供默认构造函数。
	*/
	Rect();
	Rect(int length, int width);
	void setLength(int length);	
	inline void setWidth(int width = 0);
	int getArea();
	int getPerimeter();
};

Rect::Rect()
{
	cout << "Rect::Rect()" << endl;
}

Rect::Rect(int length, int width)
{
	cout << "Rect(int length, int width)" << endl;
	m_length = length;
	m_width = width;
}

void Rect::setLength(int length)
{
	m_length = length;
}

void Rect::setWidth(int width)
{
	m_width = width;
}

int Rect::getArea()
{
	return m_width * m_length;
}

int Rect::getPerimeter()
{
	return 2 * (m_width + m_length);
}

int main()
{	
	//	构造函数由编译器在创建对象时调用。
	Rect r(2, 3);

	Rect r1;
	
	return 0;
}

析构函数

  • 与构造函数相对的就是析构函数,在删除一个对象前被调用,释放该对象的内存空间及其他的一些清理工作。
  • 析构函数的特征
    • 析构函数的名字“~类名”
    • 析构函数没有参数,也不能指定返回类型,一个类只有一个析构函数。
    • 当一个类删除时,编译器会自动调用析构函数。
    • 如果没有显示定义,编译器将默认生成一个默认的析构函数,函数体空。
  • 析构函数代码示例:
#include <iostream>

using namespace std;

class Circle
{
private:
	const float PI = 3.1415926;
	int m_radius;
	char* m_name = NULL;

public:
	Circle(int radius);
	Circle(const char* name, int radius);
	//	声明析构函数
	~Circle();
	int getArea();
	void info();
};

//	常量必须使用初始化列表初始化
Circle::Circle(const char* name, int radius)
{
	int len = strlen(name) + 1;
	m_name = new char[len];
	strcpy_s(m_name, len, name);
	m_radius = radius;
}

Circle::Circle(int radius)
{	
	m_radius = radius;
}

Circle::~Circle()
{
	cout << "Circle::~Circle()";	
	if(m_name != NULL)
	{
		cout << m_name;
		delete[]m_name;
	}	
	cout << endl;
}

int Circle::getArea()
{
	return PI * m_radius * m_radius;
}

void Circle::info()
{
	cout << "m_name: " << m_name << " m_radius:" << m_radius << endl;
}

int main()
{
	//	动态分配的对象在使用delete删除时,调用析构函数
	Circle* p = new Circle("HHH", 2);
	delete p;

	//	编译器在栈中创建的对象,在编译器删除对象时调用析构函数,先创建的对象后删除。
	char c[20] = "test1";
	Circle circle(c, 1);
	cout << circle.getArea() << endl;
	circle.info();
	c[4] = '2';
	circle.info();

	Circle circle1(1);

	return 0;
}

拷贝构造函数

  • 拷贝构造函数可以实现用一个已存在的对象初始化新对象。
  • 拷贝构造函数的一般格式为:类名(const 类名& 形参名)
  • 拷贝构造函数(自定义)代码示例:
#include <iostream>

using namespace std;

//	自定义拷贝构造函数
class Circle
{
private:
	const float PI = 3.1415926;
	int m_radius;
	char* m_name = NULL;

public:	
	Circle(const char* name, int radius);
	/*
		拷贝构造函数的参数相对固定,常见方式为:
		(const 类名& 形参名)
	*/
	Circle(const Circle& c);
	~Circle();
	int getArea();
	void info();
};

Circle::Circle(const char* name, int radius)
{
	int len = strlen(name) + 1;	  
	m_name = new char[len];  
	strcpy_s(m_name, len, name);  
	m_radius = radius;	
}
//	实现拷贝构造函数
Circle::Circle(const Circle& c)
{
	int len = strlen(c.m_name) + 1;
	m_name = new char[len];
	strcpy_s(m_name, len, c.m_name);
	m_radius = c.m_radius;	
}

Circle::~Circle()
{
	ccountout << "Circle::~Circle()";
	
	if (m_name != NULL)
	{
		cout << m_name;
		delete[]m_name;
	}
	cout << endl;
	
}

int Circle::getArea()
{
	return PI * m_radius * m_radius;
}

void Circle::info()
{
	cout << "m_name: " << m_name << " m_radius:" << m_radius << endl;	
}

int main()
{
	Circle circle1("C1", 2);
	circle1.info();

	Circle circle2(circle1);
	circle2.info();
	return 0;
}
  • 拷贝构造函数(默认)代码示例:
#include <iostream>

using namespace std;

//	使用默认拷贝构造函数
class Circle
{
private:
	const float PI = 3.1415926;
	int m_radius;
	char* m_name = NULL;

public:
	Circle(char* name, int radius);			
	void info();
};

Circle::Circle(char* name, int radius)
{
	m_name = name;
	m_radius = radius;
}


void Circle::info()
{
	cout << "m_name: " << m_name << " m_radius:" << m_radius << endl;
}

int main()
{
	char c[] = "C1";
	Circle circle1(c, 2);
	circle1.info();

	Circle circle2(circle1);
	circle2.info();
	return 0;
}

赋值函数

  • 用赋值语句将一个对象的值赋给了另一个已有同类对象时,将调用赋值函数。

  • 赋值函数的一般格式:类名&operate=(const类名&形参名)

  • 实现赋值函数时,一般要判断入参是否为对象本身,如果是对象本身,则不进行操作直接返回。

  • 赋值函数代码示例:

#include <iostream>

using namespace std;

class Circle
{
private:
	const float PI = 3.1415926;
	int m_radius;
	char* m_name = NULL;

public:
	Circle(const char* name, int radius);
	~Circle();	
	Circle& operator=(const Circle& c);
	void info();
};

Circle::Circle(const char* name, int radius)
{
	cout << "Circle::Circle(const char* name, int radius):" << name << endl;
	int len = strlen(name) + 1;
	m_name = new char[len];
	strcpy_s(m_name, len, name);
	m_radius = radius;
}

Circle::~Circle()
{
	count << "Circle::~Circle()";

	if (m_name != NULL)
	{
		cout << m_name;
		delete[]m_name;
	}
	cout << endl;
}

//	赋值函数的一般格式
Circle& Circle::operator=(const Circle& c)
{
	cout << "Circle::operator=(const Circle& c):" << " c.m_name: " << c.m_name << endl;
	//	赋值函数要判断实参是不是对象本身,如果是对象本身则直接返回。
	if (this != &c)
	{
		if (m_name != NULL)
		{
			delete[] m_name;			
		}
		int len = strlen(c.m_name) + 1;
		m_name = new char[len];
		strcpy_s(m_name, len, c.m_name);
		m_radius = c.m_radius;
	}	
	//	this是对象的固有指针,指向对象本身。
	return *this;
}

void Circle::info()
{
	cout << "m_name: " << m_name << " m_radius:" << m_radius << endl;
}

int main()
{
	Circle circle1("C11", 2);
	circle1.info();

	Circle circle2("C2", 3);
	//	调用赋值函数
	//	circle2 = circle1;
	circle2 = circle2;
	circle2.info();
	
	
	return 0;
}

继承

  有关继承的知识点并不是很多,但是继承也是很重要的一环。但是其繁琐的理解的难易程度也不小。

  • 子类继承父类的属性和方法,使得子类具备父类的特征。
  • 继承的类型
    • 单继承:子类只有一个父类(基类)
    • 多继承:子类有多于一个父类(基类)
    • 直接继承:顾名思义,就是他的继承来自他的父类
    • 间接继承:他的继承是通过继承的子类做了父类的继承。
  • 派生类不继承父类的构造方法(构造函数)每个子类都必须实现至少一个构造函数。
  • 方法的隐藏:子类中定义和父类方法名完全相同时,子类的函数屏蔽掉了预期同名的所有父类函数,这叫方法的隐藏。
  • 有关派生类继承的代码示例:
#include <iostream>

#include "Pet.h"
#include "Cat.h"
#include "Dog.h"
/*
	我们在对应的头文件里加上了每个派生类的构造函数,还有私人属性和是实现方法的声明,。
	同时我们在对应的cpp文件里对函数功能的具体实现。
*/
using namespace std;

int main()
{
	Pet pet("小强", 20);
	pet.info();
	pet.barking();
	pet.barking(3);
	pet.running();
	

	Cat cat("叮当", 10);
	cat.info();
	cat.barking();
	cat.barking(3);
	cat.running();

	Dog dog("旺财", 10);
	dog.info();
	dog.barking();
	//	dog.barking(3); 父类Pet类的barking(int n)方法被隐藏了。
	dog.running();
	dog.guardHouse();
	
	return 0;
}
  • 类的继承方式
    在这里插入图片描述
  • 默认的继承方式时private。
  • 派生类的构造和析构构成
    • 派生类构造函数的执行顺序
      • 调用基类的构造函数执行派生类的构造函数体。
    • 派生类的析构函数执行顺序
      • 执行子类的析构函数调用基类的析构函数
  • 派生类的构造和析构构成代码示例:
#include <iostream>
using namespace std;

class Animal
{
protected:
	int m_age;
public:
	Animal( age)
	{
		cout << "Animal(int age): " << endl;
		m_age = age;
	}
	~Animal()
	{
		cout << "~Animal() " << endl;
	}
	void info()
	{
		cout << "Animal: m_age: " << m_age << endl;
	}
};

class Tiger : public Animal
{
private:
	int m_weight;
public:
	Tiger(int age, int weight) : Animal(age)
	{
		cout << "Tiger(int age, int weight) : Animal(age): " << endl;
		m_weight = weight;
	}
	~Tiger()
	{
		cout << "~Tiger() " << endl;
	}
	void info()
	{
		cout << "Tiger: m_age: " << m_age << " m_weight: " << m_weight << endl;
	}
};

int main()
{
	Tiger tiger(2, 20);
	return 0;
}
  • 多继承简介
    • 一个派生类可以有很多基类,称之为多继承。
    • 多继承时,可以按照父类声明顺序构造父类,按照构造相反的顺序析构。
  • 多继承代码示例:
#include <iostream>

using namespace std;

//	基类BaseA
class BaseA
{
protected:
	int m_a;
	int m_c = 100;
public:
	BaseA(int a)
	{
		cout << "BaseA(int a)" << endl;
		m_a = a;
	}
	~BaseA()
	{
		cout << "~BaseA()" << endl;
	}
};

//	基类BaseB
class BaseB
{
protected:
	int m_b;
	int m_c = 200;
public:
	BaseB(int b)
	{
		cout << "BaseB(int b)" << endl;
		m_b = b;
	}
	~BaseB()
	{
		cout << "~BaseB()" << endl;
	}
};

class Derived : public BaseA, public BaseB
{
public:
	Derived(int a, int b) : BaseB(b), BaseA(a)
	{
		cout << "Derived(int a, int b) : BaseA(a), BaseB(b)" << endl;
	}
	~Derived()
	{
		cout << "~Derived()" << endl;
	}
	void info()
	{
		cout << "m_a: " << m_a << " m_b: " << m_b << endl;
		//	当多个父类中出现同名成员时,使用 类名::成员名 访问。
		cout << "BaseA::m_c: " << BaseA::m_c << " BaseB::m_c: " << BaseB::m_c << endl;	
	}
};


int main()
{
	Derived dervied(, 20);
	dervied.info();
	return 0;
}

结语

  由于篇幅有限,暂不能写完有关继承和多态的知识,剩下的就留着下一篇章,超详细的 C++中的封装继承和多态的知识总结<2.继承和多态>,小伙伴们,虽然没有写完,但是本章的内容也不少,小伙伴们一定要认真复习,这个理解起来挺费劲的,但是也是我们学习后续必不可少的内容。小伙伴们加油呀~
  冲冲冲!!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何做好一个企业家IP:塑造独特的个人品牌

在当今数字化时代&#xff0c;个人品牌的力量愈发凸显&#xff0c;对于企业家而言&#xff0c;一个强大的IP&#xff08;Intellectual Property&#xff0c;即知识产权或个人品牌&#xff09;不仅有助于提升个人影响力&#xff0c;还能为企业的发展注入强大动力。那么&#xff…

Flutter【组件】点击类型表单项

简介 flutter 点击表单项组件&#xff0c;适合用户输入表单的场景。 点击表单项组件是一个用户界面元素&#xff0c;通常用于表单或设置界面中&#xff0c;以便用户可以点击它们来选择或更改某些设置或输入内容。这类组件通常由一个标签和一个可点击区域组成&#xff0c;并且…

【后端面试题】【中间件】【NoSQL】ElasticSearch索引机制和高性能的面试思路

Elasticsearch的索引机制 Elasticsearch使用的是倒排索引&#xff0c;所谓的倒排索引是相对于正排索引而言的。 在一般的文件系统中&#xff0c;索引是文档映射到关键字&#xff0c;而倒排索引则相反&#xff0c;是从关键字映射到文档。 如果没有倒排索引的话&#xff0c;想找…

基于51单片机的篮球计时器Proteus仿真

文章目录 一、篮球计时器1.题目要求2.思路3.仿真图3.1 未仿真时3.2 仿真开始3.3 A队进分3.4 B队进分3.5 比赛结束 4.仿真程序4.1 主函数4.2 时间显示4.3 比分显示4.4 按键扫描 二、总结 一、篮球计时器 1.题目要求 以51单片机为核心&#xff0c;设计并制作篮球计时器 基本功…

数据结构:期末考 第六次测试(总复习)

一、 单选题 &#xff08;共50题&#xff0c;100分&#xff09; 1、表长为n的顺序存储的线性表&#xff0c;当在任何位置上插入或删除一个元素的概率相等时&#xff0c;插入一个元素所需移动元素的平均个数为&#xff08; D &#xff09;.&#xff08;2.0&#xff09; A、 &am…

基于matlab的可乐标签模板匹配

1 建模思路 1.图像预处理&#xff1a; 如果目标图像和模板图像是彩色的&#xff08;即RGB图像&#xff09;&#xff0c;则将它们转换为灰度图像&#xff0c;以便在单通道上进行匹配。使用rgb2gray函数进行灰度化。 2.获取模板大小&#xff1a; 使用size函数获取模板图像的高…

骁龙相机拍照流程分析

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 1.deliverInputEvent 拍照点击事件处理 2.submitRequestList Camera 提交拍照请求 3.createCaptureRequest 拍照请求帧数 骁龙相机通过binder 数据传输…

2006-2020上市公司研发投入金额数据集

2006-2020上市公司研发投入金额数据集https://download.csdn.net/download/a519573917/89501035 目录 上市公司研发投入与企业绩效的关系研究 一、引言 二、文献综述 三、研究设计 四、实证结果与分析 &#xff08;一&#xff09;描述性统计分析 &#xff08;二&#xf…

人工智能在肿瘤:分子亚型分类领域的最新研究进展|顶刊速递·24-07-01

小罗碎碎念 今日推文主题&#xff1a;人工智能在肿瘤/分子亚型分类中的应用 小罗观点 前两天有一位复旦的师兄私聊问了我一些问题&#xff0c;我看完以后觉得大家可能对于“分类”的概念有点不太熟悉&#xff0c;所以我决定写这篇推文系统的梳理一下“分类”和“回归”。 这俩都…

CleanMyMacX2024免费且强大的mac电脑系统优化工具

如果你的Mac电脑出现了存储空间不足、运行缓慢、电池电量消耗过快等问题&#xff0c;那么CleanMyMacX这款软件或许能为你提供解决方案。作为一款强大的系统优化工具&#xff0c;它能够帮助用户清理垃圾文件、优化内存和电池使用&#xff0c;从而提升Mac的性能表现&#xff0c;让…

09_计算机网络模型

目录 OSI/RM七层模型 OSI/RM七层模型 各层介绍及硬件设备 传输介质 TCP/IP协议簇 网络层协议 传输层协议 应用层协议 完整URL的组成 IP地址表示与计算 分类地址格式 子网划分和超网聚合 无分类编址 特殊含义的IP地址 IPv6协议 过渡技术 OSI/RM七层模型 OSI/RM七…

使用 Vue 实现包含单选框的弹窗功能(附Demo)

目录 前言1. Vue22. Vue3 前言 如果在弹窗中单独增设一些选项或者少部分的数据&#xff0c;可用如下的方式 &#xff08;不用单独创建专门的表单样式&#xff09; 如果单纯可以通过基本的按钮传输给后端&#xff0c;可用如下知识点 对于弹窗的基本知识推荐阅读&#xff1a; …

2024年06月CCF-GESP编程能力等级认证Scratch图形化编程四级真题解析

本文收录于《Scratch等级认证CCF-GESP图形化真题解析》专栏,专栏总目录:点这里,订阅后可阅读专栏内所有文章。 一、单选题(共 10 题,每题 2 分,共 30 分) 第1题 小杨父母带他到某培训机构给他报名参加 CCF 组织的 GESP 认证考试的第 1 级,那他可以选择的认证语言有几…

数据资产铸就市场竞争优势:运用先进的数据分析技术,精准把握市场脉搏,构建独特的竞争优势,助力企业实现市场领先地位,赢得持续成功

目录 一、引言 二、数据资产的重要性 三、先进数据分析技术的应用 1、大数据分析技术 2、人工智能与机器学习 3、数据可视化技术 四、精准把握市场脉搏 1、深入了解客户需求 2、预测市场趋势 3、优化资源配置 五、构建独特的竞争优势 1、定制化产品和服务 2、精准营…

zerotier-one自建根服务器方法四

一、简介 前面几篇文章已经写完了安装配置服务器&#xff0c;今天写一下客户端如何连接自建的服务器。 二、准备工作 准备一个有公网IP的云主机。 要稳定性、安全性、不差钱的可以使用阿里、腾讯等大厂的云服务器。 本人穷屌丝一枚&#xff0c;所以我用的是免费的“三丰云…

Firefox 编译指南2024 Windows10-使用Git 管理您的Firefox(五)

1. 引言 在现代软件开发中&#xff0c;版本控制系统&#xff08;VCS&#xff09;是不可或缺的工具&#xff0c;它不仅帮助开发者有效管理代码的变化&#xff0c;还支持团队协作与项目管理。Mercurial 是一个高效且易用的分布式版本控制系统&#xff0c;其设计目标是简洁、快速…

【代码随想录】【算法训练营】【第53天】 [739]每日温度 [496]下一个更大元素I [503]下一个更大元素II

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day 48&#xff0c;周六&#xff0c;不能再坚持~ 题目详情 [739] 每日温度 题目描述 739 每日温度 解题思路 前提&#xff1a; 思路&#xff1a; 重点&#xff1a; 代码实现 C语言 [496] 下一…

算法题型归类整理及同类题型解法思路总结(持续更新)

1、最优路线 通用思路 1、递归 #案例1-最优路测路线 题目描述 评估一个网络的信号质量&#xff0c;其中一个做法是将网络划分为栅格&#xff0c;然后对每个栅格的信号质量计算。 路测的时候&#xff0c;希望选择一条信号最好的路线&#xff08;彼此相连的栅格集合&#x…

Unity开箱即用的UGUI面板的拖拽移动功能

文章目录 &#x1f449;一、背景&#x1f449;二、效果图&#x1f449;三、原理&#x1f449;四、核心代码&#x1f449;五&#xff0c;总结 &#x1f449;一、背景 之前做PC项目时常常有面板拖拽移动的需求&#xff0c;今天总结封装一下&#xff0c;做成一个随时随地可复用的…

Linux 安装 Redis 教程

优质博文&#xff1a;IT-BLOG-CN 一、准备工作 配置gcc&#xff1a;安装Redis前需要配置gcc&#xff1a; yum install gcc如果配置gcc出现依赖包问题&#xff0c;在安装时提示需要的依赖包版本和本地版本不一致&#xff0c;本地版本过高&#xff0c;出现如下问题&#xff1a…
最新文章