详谈继承

继承,详指类的继承,被继承的类称为基类,另一个类称为派生类,派生类继承了基类的特性,继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。

类继承的形式如下所示,

class A
{
public:
    void FunTest1()
    { }
private:
    int _a;
};

class B:public A
{
public:
    void FunTest2()
    { }
private:
    int _b;
};
在此段代码中,类B 公有继承了 类A的成员函数及成员变量;
那么,类继承的继承权限与基类各成员的访问权限有什么联系呢?经过一系列的验证,可得以下结论:

继承方式 \ 基类成员的访问权限            public成员          protected 成员              private 成员
public 继承public
   
protected   可以继承,但访问不了 
protected 继承protected protected 可以继承,但访问不了 
private继承privateprivate可以继承,但访问不了
验证代码如下所示:
class A
{
public:
	A()
	{
		cout<<"A()"<<endl;
	}
	
	~A()
	{
		cout<<"~A()"<<endl;
	}

	void Show1()
	{
		cout<<"_a1="<<_a1<<endl;
		cout<<"_a2="<<_a2<<endl;
		cout<<"_a3="<<_a3<<endl;
	}
public:
	int _a1;
protected:
	int _a2;
private:
	int _a3;
};

class B:public A
{
public:
	B()
	{
		cout<<"B()"<<endl;
	}

	~B()
	{
		cout<<"~B()"<<endl;
	}

	void Show2()
	{
		cout<<"_b1="<<_b1<<endl;
		cout<<"_b2="<<_b2<<endl;
		cout<<"_b3="<<_b3<<endl;
	}
public:
	int _b1;
protected:
	int _b2;
private:
	int _b3;
};

void FunTest()
{
	B b;
	b._a1=10;
}

int main()
{
	FunTest();
	system("pause");
	return 0;
}
在继承方式中,

(1)基类的private成员在派生类中是不能被访问的,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。

(2)public继承是一个接口继承,保持is-a原则,每个基类可用的成员对派生类也可用,因为每个派生类对象也都是一个基类对象。

对象模型,即指对象中各个成员的布局形式;以下为例:

class A
{
public:
void FunTest()
{}
int _a1;
protected:
int _a2;
private:
int _a3;
};

class B:public A
{
public:
void FunTest(int)
{}
int _b;
};


(3)protected/private是一个实现继承,基类的部分成员并非完全成为子类接口的一部分,是has-a的关系原则,protected/private的实现效果与下例相似。

class Time;
class Date
{
public:
int _year;
int _month;
int _date;
Time t;
}
(4)使用关键字class时默认的继承方式为private,使用关键字struct时默认的继承方式为public,在写继承关系时,最好显式给出继承方式,除此之外,实际运用中,一般都使用public继承;

派生类的默认成员函数

在继承关系中,若没有显式定义派生类的六个默认成员函数,编译系统则会默认合成这六个成员函数。



继承关系中构造函数与析构函数的调用顺序

构造函数:调用派生类构造函数——>在派生类构造函数的初始化列表中调用基类构造函数——>派生类构造函数体

说明:

(1)若基类没有缺省构造函数,派生类必须在初始化列表中显式给出基类名和参数列表;

(2)若基类没有定义构造函数,则派生类也可以不用定义,全部使用缺省构造函数;

(3)若基类定义了带有形参表的构造函数,派生类就一定定义构造函数,且在派生类的初始化列表中必须显式调用基类构造函数;

class A
{
public:
	A(int a)
	:_a(a)
	{}
private:
	int _a;
};

class B:public A
{
public:
	B()
	:A(12)
	,_b(0)
	{}
private:
	int _b;
};

void FunTest()
{
	B b;	
}
析构函数:派生类析构函数——>派生类包含成员对象析构函数(调用顺序和成员对象在类中声明的顺序相反)——>基类析构函数(调用顺序和基类在派生列表中声明顺序相反)
说明:对象先创建后清理(析构)

继承体系中的作用域

(1)在继承体系中,基类和派生类是两个不同的作用域;

(2)基类和派生类有同名成员(同名隐藏),派生类成员将屏蔽基类对成员的直接访问,且对于基类与派生类中的同名函数,满足“函数仅同名即隐藏,与函数的参数列表无关”;

class A
{
public:
	void FunTest()
	{}
	int _a;
};

class B:public A
{
public:
	void FunTest(int)
	{}
	int _a;
	int _b;
};

void FunTest()
{
	B b;
	b.A::_a=1;
	b._a=10;
	b.A::FunTest();
	b.FunTest(2);
}
(3)在实际的继承体系中最好不要定义同名的成员;

继承与转换(public继承)

(1)派生类对象可以给基类对象赋值,基类对象不能给派生类对象赋值;

(2)基类的指针或引用可以指向派生类对象,派生类的指针或引用不可以指向基类对象(可通过强制类型转换完成);

class A
{
public:
	A(int a)
	:_a(a)
	{}
private:
	int _a;
};

class B:public A
{
public:
	B()
	:A(12)
	,_b(0)
	{}
private:
	int _b;
};

int main()
{
	A a;
	B b;
	a=b;
	A&a1 = b;
	A*a2 = &b;
	B&b1 =(B&)a;
	B*b2 =(B*)&a; 
	system("pause");
	return 0;
}
那么,有哪些函数可以继承,有哪些函数不可以继承呢?

友元函数:友元函数不是基类的成员函数,所以友元函数不可继承,即基类友元不能访问派生类的私有或保护成员;

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

class Person
{
public:
	Person()
	{++_c;}
protected:
	string _name;
public:
	static int _c;   //基类与派生类公用,只有一个
};
int Person::_c=0;
class Student:public Person
{
protected:
	int _num;
};

class Graduate:public Person
{
protected:
	string _Course;
};
	
void FunTest()
{
	Student s1;
	Student s2;
	Student s3;
	Graduate s4;
	cout<<""<<Person::_c<<endl;   //4
	Student::_c=0;
	cout<<""<<Person::_c<<endl;   //0
}
单继承&多继承&菱形继承

单继承示意图:


多继承示意图:


class Base1
{
public:
 int _b1;
};

class Base2
{
public:
 int _b2;
};

class Deriverd:public Base1,public Base2
{
public:
 int _d;
}
以上面代码为例,多继承的派生类对象模型为:


访问时,则有:

Deriverd d;
Base1& b1=d; // b1=&d;
Base2& b2=d; // b2=(B2*)[(int)&d +sizeof(Base1)];

菱形继承示意图:



class B
{
public:
 int _b;
};

class C1:public B
{
public:
 int _c1;
};

class C2:public B
{
public:
 int _c2;
};

class Deriverd:public C1,public C2
{
public:
 int _d;
};
以上面代码为例,菱形继承的派生类对象模型为:


菱形继承的问题?

对于派生类Deriverd 所创建的对象d,若d 直接访问基类B的成员_b,则出现指代不明确的问题,为了解决这个问题,出现了虚继承——解决了菱形继承的二义性和数据冗余的问题;

菱形虚拟继承示意图


class B
{
public:
 int _b;
};

class C1:virtual public B
{
public:
 int _c1;
};

class C2:virtual public B
{
public:
 int _c2;
};

class Deriverd:public C1,public C2
{
public:
 int _d;
};
int main()
{ 
 Deriverd d;
 d._b=1;
 d._c1=2;
 d._c2=3;
 d._d=4;
 return 0;
}
以上面代码为例,菱形虚拟继承的派生类的对象模型为:


菱形虚拟继承的原理是:通过加入指向偏移量表格的指针,将基类B原保存2份的数据成员保存1份,解决了菱形继承的二义性和数据冗余的问题。














### PLC的详细解析 #### 工作原理 可编程逻辑控制器(PLC)是一种专为工业环境设计的数字计算机,用于自动化控制。其核心工作原理基于循环扫描的方式执行用户程序。在每次扫描周期中,PLC会完成输入采样、程序执行和输出刷新三个阶段[^1]。 - **输入采样**:PLC读取所有输入信号的状态,并将这些状态存储到输入映像寄存器中。 - **程序执行**:根据输入映像寄存器中的数据,按照用户编写的程序进行逻辑运算或算术运算。 - **输出刷新**:将程序执行的结果写入输出锁存器,从而改变外部设备的状态。 这种循环扫描机制确保了PLC能够实时响应外部事件并作出相应的控制动作。 #### 应用场景 PLC广泛应用于各种工业自动化领域,包括但不限于: - **制造业**:如汽车装配线、食品加工生产线等,用于控制机械臂、传送带以及其他生产机械。 - **过程控制**:在化工、石油精炼等行业中,用来监控和调节温度、压力、流量等参数。 - **楼宇自动化**:用于暖通空调系统(HVAC)、电梯控制系统以及安全系统等。 - **能源管理**:在电力分配系统中实现负载平衡和故障检测等功能。 #### 编程方法 PLC的编程通常采用几种标准化语言,最常见的是梯形图(Ladder Diagram, LD)、功能块图(Function Block Diagram, FBD)、结构化文本(Structured Text, ST)和指令列表(Instruction List, IL)。每种语言都有其特点和适用场合。 - **梯形图**是最直观的一种图形化编程语言,它模仿继电器逻辑电路的设计方式,易于理解和学习。 - **功能块图**允许开发者使用预定义的功能块来构建复杂的控制逻辑,支持模块化设计。 - **结构化文本**是一种高级文本编程语言,适合处理复杂的数据操作和算法实现。 - **指令列表**类似于汇编语言,提供了对硬件直接访问的能力,但相对难以编写和维护。 下面是一个简单的梯形图示例,展示了如何通过两个按钮控制一个指示灯: ```plaintext |----[ ]----( )--| I0.0 Q0.0 |----[ ]----( )--| I0.1 Q0.0 ``` 在这个例子中,如果任一按钮(I0.0 或 I0.1)被按下,则指示灯(Q0.0)会被点亮。 此外,现代PLC还支持更复杂的编程技术,比如面向对象的方法,其中可以创建可重用的功能块(FBs),例如`FB_RobotCtrl`、`FB_Vision` 和 `FB_Safety`,它们分别用于机器人控制、视觉通信处理和安全联锁逻辑。全局数据块(如`DATA_DB.gxw`)则用来保存配方信息和运行时参数,使得不同功能块之间能够共享数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值