C++三大特性之继承,详细介绍

news2024/10/6 22:20:24

eea5eba81baa42f0a333f323d0825d11.jpeg

阿尼亚全程陪伴大家学习~

前言

每个程序员在开发新系统时,都希望能够利用已有的软件资源,以缩短开发周期,提高开发效率。 为了提高软件的可重用性(reusability),C++提供了类的继承机制。

1.继承的概念

继承: 指在现有类的基础上建立一个新的类。现有类称为基类或父类,新建类称为派生类或子类。对于父类与子类,人们也常说子类继承了父类,或者父类派生了子类。

语法:class 子类(派生类):继承方式 父类(基类)

现在我们一起来看一下具体的实现

#include<iostream>
using namespace std;
//基础界面
class BasePage
{
	void left()
	{
		cout << "Java,C++,Python,C...." << endl;
	}
	void right()
	{
		cout << "右界面" << endl;
	}
	void head()
	{
		cout << "头部界面" << endl;
	}
	void bottom()
	{
		cout << "底部界面" << endl;
	}
};
//C语言界面
class C 
{
public:
	void left()
	{
		cout << "Java,C++,Python,C...." << endl;
	}
	void right()
	{
		cout << "右界面" << endl;
	}
	void head()
	{
		cout << "头部界面" << endl;
	}
	void bottom()
	{
		cout << "底部界面" << endl;
	}
	void Linux()
	{
		cout << "Linux" << endl;
	}
};
//C语言界面
class C :public BasePage
{
public:
	void Linux()
	{
		cout << "Linux" << endl;
	}
};
//Java界面
class Java :public BasePage
{
public:
	void JavaSE()
	{
		cout << "JavaSE" << endl;
	}
};

这可以看做一个编程语言学习的主界面,左界面是各种不同语言的分类,这些不同编程语言界面都有主界面的部分,但是他们也有自己独特的部分,主界面是基类,而具体的编程语言界面则是派生类,派生类都继承了父类(基类)所有的成员函数。

2.继承的三种方式

公共(public)继承、保护(protected)继承、私有(private)继承

57b776b97a5a42bf9c40a65b14817fb2.png

下面我们一一介绍

2.1公共继承

#include<iostream>
using namespace std;
class A
{
private:
	int _a = 1;
public:
	int _A = 10;
	void A_print()
	{
		cout << "_a=" << _a << endl;
		cout << "_A=" << _A << endl;
	}
};
class B:public A
{
private:
	int _b = 2;
public:
	int _B = 20;
	void B_print()
	{
		cout << "_b=" << _b << endl;
	}
};
int main()
{
	B b;
	b.B_print();
	b.A_print();
	return 0;
}

72cd08d60b8e425db7cd1497424a84ff.png

我们发现通过公共继承的方式来继承A类,A类中的打印函数也被继承了,所以B类的对象可以调用A类的成员函数,接下来我们修改一下代码,在B类中访问A类的成员变量

9a012076321d43a9a05e0e9aa25d89ee.png

93a2f026724b48519b3e5c6244457f70.png

编译器告诉我们_a不可以被访问因为他是A类中的私有成员,而_A可以被访问因为他是A类中的公有成员,那有没有什么方法能让_a也能被访问呢?

有。一种是把_a设置为public成员,另外一种是把_a设置为protected成员(最好的做法),这三种又有什么区别呢?(重要!!!)

*公有(public)成员:

公有成员可以从任何地方被访问,包括类的内部、类的派生类以及类的外部。
把_a设置为公有成员意味着任何地方的代码都可以直接访问它,这通常不是一个好的做法,因为它破坏了封装性(封装性意味着隐藏对象的内部状态以防止它们被外部代码直接访问)。

*私有(private)成员:

私有成员只能在类的内部被访问。
编译器告诉我们_a不可以被访问,因为它被声明为私有成员。这意味着你不能从类的外部或派生类中直接访问它。

*保护(protected)成员:

保护成员可以在类的内部和派生类中被访问,但不能在类的外部被访问。
把_a设置为保护成员意味着你可以在其派生类中访问它,但不能在类的外部直接访问它。这提供了一种在派生类中重用和扩展基类功能的方式,同时保持对外部世界的封装性。

能明白三者的差异,也就很容易理解三种继承方式的差别了

#include<iostream>
using namespace std;
class A
{
private:
	int _a = 1;
protected:
	int a = 100;
public:
	int _A = 10;
	void A_print()//A类打印函数
	{
		cout << "_a=" << _a << endl;
		cout << "_A=" << _A << endl;
		cout << "a=" << a << endl;
	}
};
class B:public A
{
private:
	int _b = 2;
public:
	void B_print()//B类打印函数
	{
		cout << "_b=" << _b << endl;

		//cout << "_a=" << _a << endl;//A类私有成员不能被访问
		cout << "_A=" << _A << endl;//A类公有成员可以被访问
		cout << "a=" << a << endl;//A类保护成员可以被访问

		A_print();//A类公有成员可以被访问
	}
};
int main()
{
	B b;
	b.B_print();
	cout << endl;

	cout << b._A << endl;//public继承方式,在类的外部能访问类public成员
	b.A_print();//public继承方式,在类的外部能访问类public成员
	
	//cout << b.a << endl;//在类的外部不能访问类protected成员
	return 0;
}

2.2保护继承

#include<iostream>
using namespace std;
class A
{
private:
	int _a = 1;
protected:
	int a = 100;
public:
	int _A = 10;
	void A_print()//A类打印函数
	{
		cout << "_a=" << _a << endl;
		cout << "_A=" << _A << endl;
		cout << "a=" << a << endl;
	}
};
class B:protected A
{
private:
	int _b = 2;
public:
	void B_print()//B类打印函数
	{
		cout << "_b=" << _b << endl;

		//cout << "_a=" << _a << endl;//A类私有成员不能被访问
		cout << "_A=" << _A << endl;//A类公有成员可以被访问
		cout << "a=" << a << endl;//A类保护成员可以被访问

		A_print();//A类公有成员可以被访问
	}
};
int main()
{
	B b;
	b.B_print();
	//b.A_print();//protected继承方式,在类的外部不能访问类成员
	return 0;
}

 

2e7f28a743fa4e6d8669fa810f6e06ac.png

2.3私有继承

#include<iostream>
using namespace std;
class A
{
private:
	int _a = 1;
protected:
	int a = 100;
public:
	int _A = 10;
	void A_print()//A类打印函数
	{
		cout << "_a=" << _a << endl;
		cout << "_A=" << _A << endl;
		cout << "a=" << a << endl;
	}
};
class B:private A
{
private:
	int _b = 2;
public:
	void B_print()//B类打印函数
	{
		cout << "_b=" << _b << endl;

		//cout << "_a=" << _a << endl;//A类私有成员不能被访问
		cout << "_A=" << _A << endl;//A类公有成员可以被访问
		cout << "a=" << a << endl;//A类保护成员可以被访问

		//A_print();//A类公有成员可以被访问
	}
};
int main()
{
	B b;
	b.B_print();
	cout << endl;
	//cout << b._A << endl;//private继承方式,在类的外部不能访问类public成员
	//cout << b.a << endl;private继承方式,在类的外部不能访问类protected成员
	//b.A_print();//private继承方式,在类的外部不能访问类public成员
	return 0;
}

d7137cceb740440b8e3327a29eff7c2f.png

总结

在类的内部,派生类无论是以哪种方式继承基类,都不能访问基类的private成员,而基类的public成员、protected成员可以被派生类访问

在类的外部,首先需要明确的是无论是基类还是派生类的private成员和protected成员都是不能直接被访问的。而public继承的方式可以通过派生类的对象访问基类的public成员,而private继承的方式和protected继承的方式中,却不可以通过派生类的对象访问基类的public成员(可以这么理解此时基类的public成员分别成了派生类的私有成员和保护成员)

下图辅助理解

229a0b6d10214a0293e3da56fc85412c.png

61b3089280d344f7b93ebc88013aeebc.png

3.继承的对象模型

718f57d5558447ff9c44c839bc476a4c.png

我们发现B类的大小是16个字节,但是我们之前继承方式当中不是说基类的私有成员派生类不能访问吗?

其实父类中所有非静态成员都会被子类继承,父类中的私有成员属性其实是被编译器隐藏了,因此访问不到,但是确实被继承了下来,下面我们来验证一下

首先我们先打开这个工具(开发人员命令提示符)

613ae8d3be7d47468f5014d2bd15da21.png

指令输入步骤

c5e6a59d0159496aa4ac63dc2c785674.png

最终呈现结果

ca3ffc87bc904cbcab43d77949454264.png

经过验证是不是更可靠了呢

4.派生类的构造函数和析构函数

4.1构造函数

派生类不继承基类的构造函数,在声明派生类时一般应定义自己的构造函数db9b43127fe74ab58bb63cad00b80c4a.png

注意

派生类构造函数的总参数表中的参数,应当包括调用基类构造函数所需的参数 派生类构造函数的执行过程是,先调用基类构造函数初始化基类成员,然后对新增成员初始化

#include<iostream>
#include<string>
using namespace std;

class Person//基类
{
protected:
	string _name;//姓名
	char _sex;//性别
	int _age;//年龄
public:
	//Person构造函数
	Person(string name,char sex,int age)
	:_name(name)
	,_sex(sex)
	,_age(age){}
	void PersonPrint()
	{
		cout << "name:" << _name << endl;
		cout << "sex:" << _sex << endl;
		cout << "age:" << _age << endl;
	}
};
//派生类
class Student :public Person
{
private:
	int _num;//学号
public:
	//Student构造函数
	Student(string name,char sex,int age,int num)
	:Person(name,sex,age),_num(num)
	{}
	void StudentPrint()
	{
		PersonPrint();
		cout << "num:" << _num << endl;
	}
};
int main()
{
	Student s("liming", 'M', 18, 1001);
	s.StudentPrint();
	return 0;
}

0e4eca5fe1384ebfac822be5744e520e.png

4.1.1有子对象的派生类的构造函数

在学习结构体时,我们讲到一个结构体的成员还可以是个结构体变量。 派生类也可以有子对象(类类型的成员变量)

d022ad16d4204a1784e9c59e36e682c3.png

#include<iostream>
#include<string>
using namespace std;

class Person//基类
{
protected:
	string _name;//姓名
	char _sex;//性别
	int _age;//年龄
public:
	//Person构造函数
	Person(string name,char sex,int age)
	:_name(name)
	,_sex(sex)
	,_age(age){}
	void PersonPrint()
	{
		cout << "name:" << _name << endl;
		cout << "sex:" << _sex << endl;
		cout << "age:" << _age << endl;
	}
};
//派生类
class Student :public Person
{
private:
	Person _teacher;//班主任(子对象)
	int _num;//学号
public:
	//Student构造函数
	Student(string name,char sex,int age,int num,const Person& teacher)
	:Person(name,sex,age),_num(num),_teacher(teacher)
	{}
	void StudentPrint()
	{
		PersonPrint();//打印学生的信息
		cout << "num:" << _num << endl;
		_teacher.PersonPrint();//打印老师的信息
	}
};
int main()
{
	Person teacher("zhaoli", 'F', 38);//班主任
	Student s("liming", 'M', 18, 1001, teacher);//学生
	s.StudentPrint();
	return 0;
}

5f8537b22c4d4a9685933a5698270833.png

基类构造函数和子对象的书写顺序可以任意

这里有子对象的派生类的构造函数还有另外两种写法

写法一

这种不如引用传参更安全,效率高

e5f8dd2afd1d4aee805a8f580180ecd6.png

写法二

这种写法相对比较麻烦,写的形参更多了

919d454d00094125ad877a5ca1a79b77.png

4.1.2派生类构造函数的执行顺序

结论:

先调用基类构造函数,对基类数据成员初始化

再调用子对象类的构造函数,对子对象的数据成员初始化

最后执行派生类构造函数体中的语句,对派生类新增数据成员初始化

验证

4a11cf824b454f17b58aa4f654f5c23b.png

4.2析构函数

1.派生类不继承基类的析构函数,在声明派生类时,应当定义自己的析构函数

2.派生类的析构函数只对新增成员进行清理工作,基类、子对象的清理工作仍由它们各自的析构函数负责。

3.在执行派生类的析构函数时,系统会自动调用基类的析构函数和子对象的析构函数,分别对基类和子对象进行清理

4.析构函数的执行顺序与构造函数正好相反 先执行派生类自己的析构函数,对派生类新增成员进行清理; 然后调用子对象的析构函数,对子对象进行清理; 最后调用基类的析构函数,对基类进行清理。

析构顺序验证

3c98b196292840a9b1421176e8e4185f.png

我们发现Person类只构造了两次,居然析构了三次,这是为什么呢?

其实是因为默认的Person类拷贝构造函数,现在我们显示写一下他的拷贝构造函数来验证一下

#include<iostream>
#include<string>
using namespace std;
class Person//基类
{
protected:
	string _name;//姓名
	char _sex;//性别
	int _age;//年龄
public:
	void PersonPrint()
	{
		cout << "name:" << _name << endl;
		cout << "sex:" << _sex << endl;
		cout << "age:" << _age << endl;
	}
	//Person构造函数
	Person(string name,char sex,int age) :_name(name), _sex(sex), _age(age)
	{
		cout << "Person构造" << endl;
	}
	//Person拷贝构造
	Person(const Person& p)
	{
		_name = p._name;
		_sex = p._sex;
		_age = p._age;
		cout << "Person拷贝构造" << endl;
	}
	~Person()
	{
		cout << "~Person析构" << endl;
	}
};
class Student :public Person
{
private:
	Person _teacher;//班主任(子对象)
	int _num;//学号
public:
	//Student构造函数
	Student(string name, char sex, int age, int num, const Person& teacher):Person(name, sex, age), _num(num), _teacher(teacher)
	{
		cout << "Student构造" << endl;
	}
	void StudentPrint()
	{
		PersonPrint();//打印学生的信息
		cout << "num:" << _num << endl;
		_teacher.PersonPrint();//打印老师的信息
	}
	~Student()
	{
		cout << "~Student析构" << endl;
	}
};
int main()
{
	Person teacher("zhaoli", 'F', 38);//班主任
	cout << "*******************************" << endl;
	Student s("liming", 'M', 18, 1001, teacher);//学生
	//s.StudentPrint();
	return 0;
}

c0dbabeb58454ec597c6c888bc388ff3.png

那么谁是拷贝构造的呢?

第一个构造的是Person类的班主任对象,第二个则是调用基类的构造,第三个构造的是子对象是拷贝构造

5.同名成员的处理

假如在A类(基类)和B类(派生类)中有同名数据成员m,同名函数print(),那在类外面访问他们的时候会如果我们想调用A类中的函数print()和访问A类数据成员m,可以通过创建一个A类的对象,通过对象来访问。

但是既然A类被B类通过公共继承的方式继承了,那么A类的公有数据成员m和函数print(),也被继承了,但是B类(派生类)中有同名数据成员m,同名函数print()。此时如果我们建一个B类的对象,访问数据成员m和调用函数print(),会产生二义性吗(冲突)

一起来看一下

4f8b69051908420a8dd0f250449d3442.png

证明了同名成员也被继承

3d01202794044f4db0de8503b6f8be8e.png

我们发现B类的对象访问的都是B类中的数据成员m、函数print(),A类的数据成员m、函数print()并没有被访问,也没有产生二义性,其实派生类会隐藏基类的同名成员,那我们怎么样才能通过派生类的对象,访问基类成员和成员函数呢?

其实很简单只需要加类名::即可

eef60cdeb43240a3aa4f6e57f49053ee.png

总结: 
1.子类对象可以直接访问到子类中同名成员
2.子类对象加作用域可以访问到父类同名成员
3.当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数

补充

继承同名静态成员处理方式


问:继承中同名的静态成员在子类对象上如何进行访问?


静态成员和非静态成员出现同名,处理方式一致
·访问子类同名成员直接访问即可
·访问父类同名成员需要加作用域

6.多继承

6.1语法

C++允许一个类继承多个类
语法:class子类:继承方式 父类1,继承方式 父类2......
多继承可能会引发父类中有同名成员出现,需要加作用域区分

0694e9b2077c4bb39a5f2eb3791f1850.png

6.2多继承派生类的构造函数

2389e86628e247aca239d1d149b5a01d.png

注意:

⑴初始化表中基类构造函数的排列顺序任意;

⑵派生类D的构造函数的执行顺序是先调用基类的构造函数,再执行派生类构造函数的函数体;

⑶调用基类的构造函数的顺序是按照声明派生类时基类出现的顺序。

示例:

032aa8f72e29449a84869b1e7b1c72a9.png7182b9776b0b446a957b5de5a82d9aad.png

7.菱形继承

如图,先声明A类,然后由它派生出B1类、B2类,D类同时继承了B1类、B2类

当菱形继承,两个父类拥有相同数据,需要加以作用域区分

 

c0c0dcd56a1c4e959608ee01693d3b55.png

2f312277cb71472a9f1cfd226f1f48fb.png

da2652fcb68d4381bca5f6444867e01e.png

A类中的数据成员a这份数据我们知道只有有一份就可以,菱形继承导致数据有两份,造成了资源浪费,该如何解决呢?

利用虚继承可以解决,在继承方式之前 加上关键字virtual即可,此时的基类称为虚基类

f198952b5063472793091b605fc54f5e.png

 

 

 

 

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

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

相关文章

储能逆变器测试负载箱是如何实现的

储能逆变器测试负载箱是专门用于模拟各种负载条件的设备&#xff0c;主要用于对储能逆变器进行性能测试和评估。它可以根据实际需求&#xff0c;模拟不同的负载类型、负载大小和负载变化率&#xff0c;从而为储能逆变器的设计和优化提供准确的数据支持。那么&#xff0c;储能逆…

关于gitee上传成功没有绿点

今天上传完代码以后&#xff0c;打开gitee看了一下&#xff0c;但是看到昨天和今天都没有小绿点&#xff0c;仔细思考了一番&#xff0c;是仓库满了&#xff1f;不对啊&#xff0c;如果满了的话&#xff0c;上传就会失败啊&#xff0c;那这是什么问题呢&#xff1f; 原来是因为…

20240607在Toybrick的TB-RK3588开发板的Android12下适配IMX415摄像头和ov50c40

20240607在Toybrick的TB-RK3588开发板的Android12下适配IMX415摄像头和ov50c40 2024/6/7 11:42 【4K/8K摄像头发热量巨大&#xff0c;请做好散热措施&#xff0c;最好使用散热片鼓风机模式&#xff01;】 结论&#xff1a;欢迎您入坑。 Toybrick的TB-RK3588开发板的技术支持不…

AlaSQL.js:用SQL解锁JavaScript数据操作的魔法

简介 先附上 github 地址 https://github.com/AlaSQL/alasql AlaSQL.js - JavaScript SQL database for browser and Node.js. Handles both traditional relational tables and nested JSON data (NoSQL). Export, store, and import data from localStorage, IndexedDB, or …

从零开始学JAVA

一、编写Hello world程序 public class JavaMain1 {//主程序执行入口&#xff0c;main方法public static void main(String[] args){System.out.println("Hello world!");} } 运行结果 Hello world! java编写主程序常见错误&#xff1a; 1、System ---首字母没有…

vue2+elementui,动态生成的表单校验

话不多,先上一段视频,看看是不是你们需要的效果 elementui动态生成表单校验 附上代码 <template><div class"home"><div class"home-box"><!-- <menuHtml></menuHtml> --><div class"home-div"><…

使用2个手机文件恢复工具,轻松找回文件

在这个智能手机横行的时代&#xff0c;手机已经成为我们生活中不可或缺的一部分。然而&#xff0c;就像生活中的一切事物一样&#xff0c;手机也有可能出现意外&#xff0c;比如文件丢失。这就像是你在超市购物&#xff0c;结果发现钱包不见了&#xff0c;那种感觉真是让人抓狂…

CTFHUB-技能树-web-web前置技能-HTTP协议全

目录 1.请求方式 2.302跳转 3.Cookie 4.基础认证 5.响应包源码 1.请求方式 curl -v -X http://challenge-3022c877a8dcedeb.sandbox.ctfhub.com:10800/index.php 2.302跳转 参考链接&#xff1a;http://t.csdnimg.cn/aqdNG 301——永久性重定向。该状态码表示请求的资源已…

攻防世界---misc---津门杯2021-m1

1、题目描述&#xff0c;下载附件是一张bmp格式的图片 2、直觉告诉我这和图片的颜色通道有关 3、于是我就尝试用stegslove打开图片 4、将颜色通道都改为0&#xff0c;点击preview 5、然后发现一串base64编码 6、解码得flag flag{l5DGqF1pPzOb2LU919LMaBYS5B1G01FD}

MyEclipse新手使用介绍

目录 1.MyEclipse诞生背景 2.作用 3.版本历史 4.优缺点 5.应用场景 6.如何使用 6.1.下载与安装 6.2.MyEclipse 菜单及其菜单项 7.创建和发布一个 Java 程序 7.1.创建 Java 程序 7.2.发布 Java 程序 8.示例 8.1. Hello World 示例 8.2. 简单Spring Boot 应用 8.3…

行心科技中禄松波携手,开启智能健康新时代

在2024年第34届健博会暨中国大健康产业文化节的盛大舞台上&#xff0c;广州市行心信息科技有限公司&#xff08;以下简称“行心科技”&#xff09;与浙江中禄松波生物工程有限公司&#xff08;以下简称“中禄松波”&#xff09;宣布达成战略合作&#xff0c;共同推动医康养产业…

【JavaEE精炼宝库】多线程(4)深度理解死锁、内存可见性、volatile关键字、wait、notify

目录 一、死锁 1.1 出现死锁的常见场景&#xff1a; 1.2 产生死锁的后果&#xff1a; 1.3 如何避免死锁&#xff1a; 二、内存可见性 2.1 由内存可见性产生的经典案例&#xff1a; 2.2 volatile 关键字&#xff1a; 2.2.1 volatile 用法&#xff1a; 2.2.2 volatile 不…

2024 年最新商家转账到零钱功能申请问题集中解答

鉴于诸多商户在申请商家转账到零钱时受到过时、错误经验文章的误导&#xff0c;基于我们数千次成功开通商家转账到零钱功能的丰富经验&#xff0c;特整理此篇文章&#xff0c;以期对新商户开通微信支付的商家转账到零钱功能提供有益帮助。以下将针对商家转账到零钱功能申请前、…

阿里云sls 采集日志安装记录

参考阿里云给的安装文档 阿里云安装Logtail组件 注意这里&#xff0c;选择地域&#xff0c;是中国地域选中国&#xff0c;海外选海外即可 按照文档继续下去 修改配置文件./alibaba-cloud-log-all/values.yaml 所有的操作完成后&#xff0c;去控制台配置 以上操作的前提是…

多目标应用:MOHHO多目标哈里斯鹰优化算法求解无人机三维路径规划(MATLAB代码)

详细介绍 多目标应用&#xff1a;MOHHO多目标哈里斯鹰优化算法求解无人机三维路径规划&#xff08;MATLAB代码&#xff09;-CSDN博客 一次运行结果 完整MATLAB代码

工会考试基础知识题库分享(附答案解析)

单选题 1、国家机关在组织起草或者修改直接涉及职工切身利益的法律、法规、规章时&#xff0c;( )工会意见。 A、可以听取 B、应当听取 C、必须听取 D、应当吸收 [答案]B 【解析】国家机关在组织起草或者修改直接涉及职工自身利益的法律、法规、规章时&#xff0c;应当听取工…

正宇软件助力青岛打造智慧政协平台,引领新时代政协工作创新

在当前数字化、智能化浪潮的推动下&#xff0c;青岛市政协紧跟时代步伐&#xff0c;以“百舸争流奋楫先”的精神&#xff0c;不断开拓创新&#xff0c;推动政协工作高质量发展。5月30日&#xff0c;人民政协报报道了青岛市政协在推动高质量发展、加强思想政治引领、凝聚合作共识…

电赛报告书写

一、总体要求 &#xff08;1&#xff09;摘要&#xff1a;一页&#xff0c;小于300字 &#xff08;2&#xff09;正文&#xff1a;不超过8页 &#xff08;3&#xff09;附录&#xff1a;可以没有&#xff0c;但是不能超过2页 二、摘要书写 摘要要小于等于300字&#xff0c…

ARM公司发展历程

Arm从1990年成立前开始&#xff0c;历经漫长岁月树立各项公司里程碑及产品成就&#xff0c;一步步成为全球最普及的运算平台。 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; Acorn 时期 1978年&#xff0c;Chris Curry和Hermann Hauser共同创立了Acorn…

如何使用SeaFile文件共享服务器结合内网穿透将家中电脑变成个人云盘

文章目录 1. 前言2. SeaFile云盘设置2.1 Owncould的安装环境设置2.2 SeaFile下载安装2.3 SeaFile的配置 3. cpolar内网穿透3.1 Cpolar下载安装3.2 Cpolar的注册3.3 Cpolar云端设置3.4 Cpolar本地设置 4.公网访问测试5.结语 1. 前言 本文主要为大家介绍&#xff0c;如何使用两个…