C++ 类之间的纵向关系-继承

目录

继承的基本概念

定义

使用方法

 内存空间

继承下构造·析构执行的顺序

构造函数

析构函数

 继承的优点

继承方式

public

protected

private

隐藏

回顾函数重载

隐藏

父类指针指向子类对象

优点 

 出现的问题

 类成员函数指针

普通的函数指针

类成员函数指针

类成员函数与普通的函数的区别

举例 

解决问题


继承的基本概念

定义

被继承的类叫做基类(父类),继承的类叫派生类(子类),在子类类名后面加:继承方式 父类

class CFather {

};
class CSon :public CFather {

};

使用方法

通过继承关系,子列可以使用父类的成员。如果子类和父类有同名的成员,默认使用子类的成员,如果想要使用父类成员,需要在成员名前加上类名::用于显式的指定区分。

父类:

class CFather {
public:
	int m_fa;

	int m_money;



	void funFather() {
		cout << __FUNCTION__ << endl;
	}

	void fun() {
		cout << "CFather::fun" << endl;
	}
};

子类:

class CSon :public CFather {
public:
	int m_money;

	CSon() {
		m_fa = 1;
		CFather::m_money = 100;//父类的m_money
		CSon::m_money = 200;//子类的m_money
		m_money = 300; //默认使用的是子类的成员

	}
	void funSon() {
		cout << m_fa << endl;
		funFather();
	}

	void fun() {
		cout << "CSon::fun" << endl;
	}

};

测试:

创建一个子类对象,通过子类成员函数调用父类的成员属性,在子类中的构造函数中也可给父类的成员属性初始化值。当子类与父类有同名成员时,可以通过类名作用域来区分,默认使用的是当前类对象的成员。

int main()
{
	CSon son;

	son.funSon();

	cout << son.CFather::m_money << endl; //100
	cout << son.CSon::m_money << endl; //300

	son.fun();//默认子类
	son.CFather::fun();//父类
	son.CSon::fun();//子类

	return 0;
}

 内存空间

子类继承父类,相当于父类的成员包含到自己的类里,所以定义子类对象所占用的空间大小除了子类自身的成员,还包括父类的成员。成员在内存空间分布为:先父类成员后子类成员,而每个类中的成员分布与在类中声明的顺序一致。

 内存大小:

	cout << sizeof(CFather) << "    " << sizeof(CSon) << endl;  //8   12

 内存分配:

	CSon son;

	cout << &son << endl;//00D4FC6C
	cout << &son.m_fa << "    " << &son.CFather::m_money << "    " << &son.m_money << endl;//00D4FC6C    00D4FC70    00D4FC74

继承下构造·析构执行的顺序

构造函数

执行的顺序:父构造->子构造(函数体代码)

构造顺序说明:在子类创建对象的时候,会先跳转到子类的构造函数(注意这里并不是直接先执行父类的构造函数),先执行构造的初始化参数列表,初始化顺序看在类中声明的先后顺序(等同于成员在内存排布的先后顺序),由于父类在前,子类成员在后,所以先初始化父类成员,调用父类的构造函数。初始化完毕后,再回到子类初始化参数列表,按照顺序继续初始化其他成员,所有成员初始化完毕后,再执行子类自己的构造函数体代码。

  注意1:在初始化列表中会默认调用父类的无参构造初始化父类成员,如果父类只有带参数的构造,在子类的初始化列表中必须显示的指定父类构造进行初始化。这有点像之前说的组合关系形式。

  注意2:不能在子类构造函数的初始化参数列表中直接对父类中的成员属性初始化,更不能在子类构造函数的函数体代码中初始化(实际是赋值操作),因为如果在创建对象时要创建父类对象,那么将不会调用子类的构造函数,而父类中又没有构造函数。

默认调用父类无参构造:

class CFather {
public:
	int m_fa;
	CFather() :m_fa(3) {
		cout << __FUNCTION__ << endl;
	}

};

class CSon :public CFather {
public:
	int m_son;
	CSon():/*CFather(),编译器会默认调用父类的无参构造*/m_son(1)/*,m_fa(0)*/ {
		m_fa = 0;//赋值操作
		cout << __FUNCTION__ << endl;
	}
};

手动显示指定调用父类的构造:

class CFather {
public:
	int m_fa;
	CFather() :m_fa(3) {
		cout << __FUNCTION__ << endl;
	}

};

class CSon :public CFather {
public:
	int m_son;
	CSon() :CFather(),/* 手动显示指定调用父类的构造 */m_son(1) {
		cout << __FUNCTION__ << endl;
	}
};

父类中没有无参构造时,必须手动显式指定出来:

class CFather {
public:
	int m_fa;
	CFather() :m_fa(3) {
		cout << __FUNCTION__ << endl;
	}
};

class CSon :public CFather {
public:
	int m_son;
	CSon(int a) :CFather(a), m_son(1) {

	}
};

如果父类中存在多个构造函数,想使用带参数的构造函数,也需要手动指定,否则会调用无参的父类构造了

class CFather {
public:
	int m_fa;
	CFather() :m_fa(3) {
		cout << __FUNCTION__ << endl;
	}
	CFather(int a) :m_fa(a) {
		cout << __FUNCTION__ << endl;
	}
};

class CSon :public CFather {
public:
	int m_son;
	CSon(int a) :CFather(a), m_son(1) {

	}
};

析构函数

执行的顺序:子析构->父析构

析构顺序说明:子类对象的生命周期结束后,因为是子类所以自动调用子类析构,当析构执行完了,才会回收对象分配的空间(和构造的顺序相反),当然这个空间包含创建的父类成员,那么回收父类匿名对象前,会自动调用父类的析构,再回收父类对象本身。如果是new出来的子类对象,同理。

class CTest {
public:
	int m_tst;
public:
	CTest(int a) :m_tst(a) {
		cout << __FUNCTION__ << endl;
	}

	~CTest() {
		cout << __FUNCTION__ << endl;
	}

};

class CFather {
public:
	int m_fa;
public:
	CFather(int a) :m_fa(a) {
		cout << __FUNCTION__ << endl;
	}
	~CFather() {
		cout << __FUNCTION__ << endl;
	}
};

class CSon :public CFather {
public:
	CTest tst;
	int m_son;
public:
	CSon(int a) :tst(a + 2), CFather(a + 1), m_son(a) {
		cout << __FUNCTION__ << endl;
	}
	~CSon() {
		cout << __FUNCTION__ << endl;
	}


};
int main() {
	CSon son(1);





	return 0;
}

 上三行为构造函数,下三行为析构函数,构造函数和析构函数执行的顺序相反。

 继承的优点

  可以将一些功能比较相似的类中公共的成员,抽离出来形成一个类,即是一个父类,这些子类继承父类,可以使用父类成员,公共的成员在父类中只维护一份代码即可,如果要添加新的公共的成员,只需要在父类中添加一份即可。增加新的子类公共的成员不必再重复写了,增加了程序代码的复用性、扩展性、灵活性,减少代码冗余

class CPeople {
public:
	int m_money;
	CPeople():m_money(100){}
	void cost(int n) {
		cout << __FUNCTION__ << endl;
		m_money -= n;
	}

	void play() {
		cout << "溜溜弯" << endl;
	}

	void drink() {//如果增加公共的功能,属性,只需要在父类中增加一份即可
		cout << "喝饮料" << endl;

	}
};

class CWhite : public CPeople{

public:
	//CWhite():m_money(100){}

	void eat() {
		cout << "吃牛排" << endl;
	}


};

class CYellow : public CPeople {
public:

	void eat() {
		cout << "小烧烤" << endl;
	}

};

class CBlack : public CPeople {
public:

	void eat() {
		cout << "西瓜炸鸡" << endl;
	}

};

class CBlue : public CPeople {
public:

	void eat() {
		cout << "吃另一个阿凡达" << endl;
	}
};

继承方式

继承方式描述了父类成员在子类中的访问控制,即所能使用的一个范围。继承方式与访问修饰符共同决定了父类成员在子类中所表现的属性。

三种继承方式:public、protected、private

创建一个父类:

class CFather {
public:
	int m_pub;
protected:
	int m_pro;
private:
	int m_pri;
public:
	CFather():m_pub(10),m_pro(20),m_pri(30){}

	int GetPri() { return m_pri; }
};

public

父类            子类
public     ->   public
protected  ->   protected
private    ->   不可访问

class CSon : public CFather {
public:
	void funPublic() {
		cout << m_pub << endl;  //公共的
		cout << m_pro << endl;  //保护的
		//cout << m_pri << endl;  //父类中私有的不可访问
		cout << GetPri() << endl;
	}

};

protected

父类            子类
public     ->   protected
protected  ->   protected
private    ->   不可访问

class CSon : protected CFather {
public:
	void funPublic() {
		cout << m_pub << endl;  //保护的
		cout << m_pro << endl;  //保护的
		//cout << m_pri << endl;  //父类中私有的不可访问
		cout << GetPri() << endl;
	}

};

private

父类            子类
public     ->   private
protected  ->   private
private    ->   不可访问

class CSon : private CFather {
public:
	void funPublic() {
		cout << m_pub << endl;  //私有的
		cout << m_pro << endl;  //私有的
		//cout << m_pri << endl;  //父类中私有的不可访问
		cout << GetPri() << endl;
	}

};

隐藏

回顾函数重载

   对应于函数重载而言,我们调用的时候,可以根据参数类型、参数个数,编译器自动匹配调用哪个函数。

  同样如果在一个类中存在两个同名函数(参数列表不同),那么也可以根据调用者传递的参数自动的区分执行哪个函数,因为也是一个函数重载的关系。

正常重载:

void fun() {
	cout << __FUNCTION__ << endl;
}

void fun(int a) {
	cout << __FUNCTION__ << "  " << a << endl;
}
	fun();
	fun(10);

 类内和类外的重载:

由于类中的成员函数是由对象调用的,所以两者也互不影响

class CSon : public CFather {
public:
	void fun() {
		cout << __FUNCTION__ << endl;
	}

	void fun(int a) {
		cout << __FUNCTION__ << "  " << a << endl;
	}
};
	CSon son;
	son.fun();
	son.fun(20);

隐藏

 对于父类和子类中,如果有同名的函数但是参数列表不同,则不能够自动区分匹配,因为他们之间的关系并不是函数重载的关系,作用域不同,必须使用类名作用域去区分该调用哪个函数。这种关系称之为隐藏

  简单来说就是子类和父类中同名的成员互为隐藏的关系,当子类的同名成员出现时,父类的成员被隐藏,要想使用需要显示指定父类类名作用域。

class CFather {
public:
	void fun(/* CFather* const this  */int a, int b) {
		cout << __FUNCTION__ << "  " << a << "  " << b << endl;
		cout << this << endl;
	}

};

class CSon : public CFather {
public:
	void fun() {
		cout << __FUNCTION__ << endl;
	}

	void fun(int a) {
		cout << __FUNCTION__ << "  " << a << endl;
	}
};
	CSon son;
	son.fun();
	son.fun(20);

	//son.fun(30, 40);  //fun优先匹配子类,子类无对应的,报错
	son.CFather::fun(30, 40);//匹配父类的

子类同名的成员注释掉后,父类成员显现:

class CFather {
public:
	void fun(/* CFather* const this  */int a, int b) {
		cout << __FUNCTION__ << "  " << a << "  " << b << endl;
		cout << this << endl;
	}

};

class CSon : public CFather {
public:
	//void fun() {
	//	cout << __FUNCTION__ << endl;
	//}

	//void fun(int a) {
	//	cout << __FUNCTION__ << "  " << a << endl;
	//}
};
	CSon son;
	son.fun(10, 20);  //子类同名的成员注释掉后,父类成员显现

测试:父类指针指向子类的地址和父类中函数成员自身的this指针指向的地址相同。

	CSon son;
	son.fun(10, 20); 
	
	CFather* const pthis = &son; 

	cout << pthis << endl;

父类指针指向子类对象

  刚才我们也说到,父类的指针可以不用强转直接指向子类对象,保证了子类对象能够成功调用父类函数,这么做的好处是:父类的指针可以统一多个类的类型,提高代码的复用性、扩展性。 

 那么我们还是用刚才举过的例子来测试:

class CPeople {
public:
	int m_money;
	CPeople():m_money(100){}
	void cost(int n) {
		cout << __FUNCTION__ << endl;
		m_money -= n;
	}

	void play() {
		cout << "溜溜弯" << endl;
	}

	void drink() {//如果增加公共的功能,属性,只需要在父类中增加一份即可
		cout << "喝饮料" << endl;

	}
};

class CWhite : public CPeople{

public:
	//CWhite():m_money(100){}

	void eat() {
		cout << "吃牛排" << endl;
	}


};

class CYellow : public CPeople {
public:

	void eat() {
		cout << "小烧烤" << endl;
	}

};

class CBlack : public CPeople {
public:

	void eat() {
		cout << "西瓜炸鸡" << endl;
	}

};

优点 

 我们先来测试一下父类指针指向子类对象的好处:

当要创建一个家庭类时,如果已知家里有两个人,但是不明都是什么人,要是通过子类指针创建的话,就需要6个指针:

class CFamily {
public:
	//  2个人 需要6个指针
	CWhite* pW1;
	CWhite* pW2;
	//...
	//...

};

 但是如果通过父类指针创建,就可以 只创建两个指针,并且可以合并成数组。

class CFamily {
public:

	CPeople* arrPeo[2];

};

 出现的问题

   我们如果想要创建函数,然后在函数中通过子类指针调用子类对象中的成员函数,那么会出现代码冗余的现象:

void fun(CWhite *pw) {
	pw->cost(10);
	pw->eat();
	pw->play();
	pw->drink();
}

void fun(CYellow* pw) {
	pw->cost(10);
	pw->eat();
	pw->play();
	pw->drink();
}
	fun(new CWhite);

	fun(new CYellow);

 所以我们会想到用父类指针指向子类对象的成员函数,这样就可以统一多种子类类型,提高代码复用性,扩展性。

void fun(CPeople* pPeo) {
	pPeo->cost(10);
	//(CWhite*)pPeo->eat();  //不是一个通用的方法,待解决
	pPeo->play();
	pPeo->drink();
}
    fun(new CWhite);​   
 
    fun(new CYellow);

 不过这里会出现一个问题,就是不可以通过父类指针调用子类中不统一的函数,如果强转成子类指针也不是一个通用的方法,所以应该怎么解决呢?

所以要引入一个新的知识点:类成员函数指针

 类成员函数指针

调用函数的两种方式:通过函数名直接调用、通过函数指针间接调用。

通过函数指针调用的好处:真正的函数指针可以将实现同一功能的多个模块统一起来标识,使系统结构更加清晰,后期更容易维护。或者归纳为:便于分层设计、利于系统抽象、降低耦合度以及使接口与现实分开,提高代码的复用性、扩展性。

普通的函数指针

void fun() {
	cout << __FUNCTION__ << endl;
}
	void (*p_fun) () = &fun;
	(*p_fun)();

定义函数指针有时看起来比较繁琐、可读性差一些,我们可以用typedef进行优化。

	typedef void (*P_FUN) ();

	P_FUN p_fun2 = &fun;
	(*p_fun2)();

类成员函数指针

class CTest {
public:
	void fun(/* CTest * const this */) {
		cout << __FUNCTION__ << endl;
	}
};
	void (CTest::*p_fun3) () = &CTest::fun;
	(tst .* p_fun3)();  

	CTest* pTst = new CTest;
	(pTst->*p_fun3)();

类成员函数与普通的函数的区别

1.所属的作用域不同,类成员函数标识了所属的类,必须通过对象调用(虽然可以是空指针对象,但必须得有)。

2.类成员函数编译器会默认加上一个隐藏的参数:this指针,而普通函数没有。

所以定义类成员函数的指针与普通的函数指针肯定会有所区别:C++提供了三种运算符::*、.*、->*用于定义和使用类成员函数指针。

注意:

定义类成员函数指针,格式:  类名 整体操作符(::*) 指针名

.*  和 ->* 都是C++提供的整体操作符

如果创建的是正常的类对象,那么对象就用.*调用类成员函数指针 指向函数

如果创建的是指针类型的对象,那么对象就用->*调用类成员函数指针 指向函数

举例 

先定义一个父类和子类:

class CFather {
public:

};
class CSon : public CFather {
public:
	void fun() {
		cout << __FUNCTION__ << endl;
	}

};

 如果要通过父类指针指向并调用子类对象中特有的成员函数时,我们首先要创建一个类成员函数指针:

	CFather* pFa = new CSon;
	//pFa->fun();

	//-------------------------------
	void(CFather :: * p_fun)() = (void(CFather :: * )())&CSon::fun;
	(pFa->*p_fun)();

这里要注意的是:我们创建的是父类成员函数指针,指向的是子类成员函数的地址,由于两边的类型不同,所以要将右边的子类成员函数地址强转为父类成员函数指针类型。(子类转父类是统一的过程,而父类转子类则是发散的过程)。

这么写代码过于复杂,可以通过typedef来简化一下:

	typedef void(CFather ::* P_FUN)();
	P_FUN p_fun2 = (P_FUN)&CSon::fun;
	(pFa->*p_fun2)();

解决问题

在学过类成员函数指针后,我们就可以来解决之前父类指针不能调用子类对象的特有成员函数的问题了。

我们先用typedef来创建一个父类的类成员函数指针类型:

typedef void(CPeople::* P_FUN)();

然后在函数中就可以多创建一个类成员函数指针的形参,这样传入子类的类成员函数地址后,就可以通过父类指针调用类成员函数指针指向子类成员的函数了。

void fun(CPeople* pPeo,P_FUN p_fun) {
	pPeo->cost(10);
	//(CWhite*)pPeo->eat();  //不是一个通用的方法,待解决
	(pPeo->*p_fun)();
	pPeo->play();
	pPeo->drink();
}
int main() {
	fun(new CYellow, (P_FUN)&CYellow::eat);
	fun(new CBlack, (P_FUN)&CBlack::eat);

	return 0;
}

注意别忘了传入实参时要将子类成员函数的地址强转为父类的类成员函数指针类型。

这里父类指针给人的感觉就是:同一根指针在不同的调用情况它具有了不同子类的形态,那就是一个多态

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

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

相关文章

RK3568平台开发系列讲解(调试篇)debugfs 分析手段

🚀返回专栏总目录 文章目录 一、enable debugfs二、debugfs API三、使用示例沉淀、分享、成长,让自己和他人都能有所收获!😄 📢Linux 上有一些典型的问题分析手段,从这些基本的分析方法入手,你可以一步步判断出问题根因。这些分析手段,可以简单地归纳为下图: 从这…

6、索引的数据结构

3.3 常见的索引概念 索引按照物理实现方式&#xff0c;索引可以分为 2 种&#xff1a;聚簇和非聚簇索引 1、聚簇索引 5、索引的代价 空间上的代价 每建立一个索引都要为它建立一棵B树&#xff0c;每一棵B树的每一个节点都是一个数据页&#xff0c;一个页默认会占用 16KB 的存…

(排序11)排序的时间复杂度,空间复杂度,稳定性总结

图片总结 内排序时间复杂度总结 内部排序&#xff1a;数据元素全部放在内存中的排序。. 在内排序当中比较快的有希尔排序&#xff0c;堆排序&#xff0c;快速排序&#xff0c;归并排序&#xff0c;这四个排序的时间复杂度都是O(n*logn)。其中希尔排序的时间复杂度更加准确的来…

62. 不同路径

一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的路径&#xff1f; 示例 1…

微电网两阶段鲁棒优化经济调度方法(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【网络编程】TCP

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;Java EE初阶&#x1f447; 目 录 &#x1f52e;一. TCP流套接字编程&#x1f4bf;二. TCP中的长短连接&#x1f4c0;三. 写一个 TCP 版本的 回显服务器-客户端 &#x1f52e;一. TCP流套接字编程 ServerSock…

网络视频监控如何入门?如何安装和配置、设备选择和实时监控?

网络视频监控是一种先进的安全技术&#xff0c;它可以通过互联网连接到远程视频服务器&#xff0c;使用户可以随时随地监控所关注的地点。本文将介绍网络视频监控的基础入门知识&#xff0c;包括安装和配置、设备选择和实时监控等方面。 一、安装和配置 在进行网络视频监控前&…

Opencv+Python笔记(四)图像的形态学处理

1.腐蚀与膨胀 膨胀用来处理缺陷问题&#xff0c;把缺陷填补掉&#xff0c;提高亮区面积&#xff1b; 腐蚀用来处理毛刺问题&#xff0c;把毛刺腐蚀掉&#xff0c;降低亮区面积。 腐蚀操作可以消除噪点&#xff0c;同时消除部分边界值&#xff0c;导致目标图像整体缩小。 膨胀…

C#+asp.net基于web的大学社团管理信息系统

本系统的模块是分为用户模块和管理员模块&#xff0c;管理员负责学生管理模块、社团管理模块、公告管理模块、留言管理模块、加入社团管理模块、活动管理模块、管理员管理模块。社团管理员则负责预约管理模块、活动报名的审核等。。 系统具有三类系统用户分别是&#xff1a;系统…

用于测试FDIA在现实约束下可行性的FDIA建模框架(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 信息通信技术的发展和智能设备的引入使电力系统逐渐演变为电力信息物理系统&#xff0c;而信息层与物理层之间的深度耦合也加剧…

【测试面试】吐血整理,大厂测试开发岗面试题(1~4面),拿下年40w...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 自动化测试面试题&…

【文件系统和系统日志分析】

目录 一、inode和block概述block&#xff08;块&#xff09;inode&#xff08;索引节点&#xff09; 二、inode内容三、inode的号码3.1、查看inode号码的方法 四、inode的大小磁盘分区后的结构访问文件的简单流程 五、删除乱码文件六、inode节点耗尽故障处理6.1、模拟inode节点…

SSM整合的基本思路梳理

SSM整合的简单思路流程 基本思路 我在整合的时候一般习惯从MyBatis开始向上构建&#xff0c;也就是在开始一个项目的时候先将DAO层搭建起来&#xff0c;再向上整合Spring以及SpringMVC。按照这个流程&#xff0c;可以做出一个比较简单的大致流程作为参考&#xff0c;帮助我们…

[MySQL]基本数据类型及表的基本操作

一、常用的数据类型 1.1 数据库表的列类型 数值 1 2 3.14 tinyint 十分小的数据 1个字节smallint 较小的数据 2个字节mediumint 中等大小的数据 3个字节int 标准的整数 4个字节big 较大的数据 8个字节float 浮点数 4个字节double 浮点数 小数 8个字节&#xff08;精度问题&am…

JSON的用法和说明

JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式。 JSON建构于两种结构&#xff1a; "名称/值"对的集合。理解为对象 值的有序列表。理解为数组 JSON具有以下这些形式&#xff1a; 对象是一个无序的“ ’名称/值‘ 对”集合。一个…

【排序】快速排序(递归和非递归)

快速排序 前言图解大致思路对于hoare版本对于挖坑法对于前后指针法 实现方法递归非递归 快排的优化&#xff08;基于递归的优化&#xff09;三数取中法小区间优化 时间复杂度和空间复杂度 前言 快速排序&#xff0c;听名字就比较霸道&#xff0c;效率根名字一样&#xff0c;非…

永久免费内网穿透不限制速度

市面上的免费内网穿透大都有格式各样的限制&#xff0c;什么限制流量啊&#xff0c;每个月要签到打卡啊&#xff0c;还有更改域名地址等&#xff0c;只有神卓互联内网穿透是永久免费没有限制的&#xff0c;白嫖也可以。 这篇文章分享了3个方案&#xff0c;按照性能和综合指标排…

项目驱动的编写

驱动代码直接使用nfs传输,设备树直接在开发板中修改设备树文件 1、修改好设备树&#xff0c;在内核顶层make dtbs &#xff0c;然后替代tftp目录中的设备树文件 2、使用内核源码编译生成驱动程序&#xff0c;然后传送到开发板中&#xff0c;使用insmod动态加载 LCD驱动 1、初始…

从零学习SDK(7)如何打包SDK

打包SDK的目的是为了方便将SDK提供给其他开发者或用户使用&#xff0c;以及保证SDK的兼容性和安全性。打包SDK可以有以下几个好处&#xff1a; 减少依赖&#xff1a;打包SDK可以将SDK所需的库、资源、文档等打包成一个文件或者一个目录&#xff0c;这样就不需要用户再去安装或…

ArduPilot开源飞控系统之简单介绍

ArduPilot开源飞控系统之简单介绍 1. 源由2. 了解&阅读2.1 ArduPilot历史2.2 关于GPLv32.3 ArduPilot系统组成2.4 ArduPilot代码结构 3. 后续4. 参考资料 ArduPilot是一个可信赖的自动驾驶系统&#xff0c;为人们带来便利。为此&#xff0c;提供了一套全面的工具&#xff0…