c++中如何创建c 内嵌对象象

& 鸡啄米:C++编程入门系列之十五(类与对象:类的组合)
鸡啄米:C++编程入门系列之十五(类与对象:类的组合)
&&&&&&&上一讲鸡啄米给大家讲了,让大家了解了类的创建和释放过程。这一节讲讲类的组合。&&&&&&&在我们对现实中的某些事物抽象成类时,可能会形成很复杂的类,为了更简洁的进行软件开发,我们经常把其中相对比较独立的部分拿出来定义成一个个简单的类,这些比较简单的类又可以分出更简单的类,最后由这些简单的类再组成我们想要的类。比如,我们想要创建一个计算机系统的类,首先计算机由硬件和软件组成,硬件又分为CPU、存储器等,软件分为系统软件和应用软件,如果我们直接创建这个类是不是很复杂?这时候我们就可以将CPU写一个类,存储器写一个类,其他硬件每个都写一个类,硬件类就是所有这些类的组合,软件也是一样,也能做成一个类的组合。计算机类又是硬件类和软件类的组合。&&&&&& 类的组合其实描述的就是在一个类里内嵌了其他类的对象作为成员的情况,它们之间的关系是一种包含与被包含的关系。简单说,一个类中有若干数据成员是其他类的对象。以前的教程中我们看到的类的数据成员都是基本数据类型的或自定义数据类型的,比如int、float类型的或结构体类型的,现在我们知道了,数据成员也可以是类类型的。&&&&&& 如果在一个类中内嵌了其他类的对象,那么创建这个类的对象时,其中的内嵌对象也会被自动创建。因为内嵌对象是组合类的对象的一部分,所以在构造组合类的对象时不但要对基本数据类型的成员进行初始化,还要对内嵌对象成员进行初始化。&&&&&& 组合类构造函数定义(注意不是声明)的一般形式为:&&&&&& 类名::类名(形参表):内嵌对象1(形参表),内嵌对象2(形参表),...&&&&& &{&&&&&&&&&&&&&&& &类的初始化&&&&& &}&&&&&& 其中,&内嵌对象1(形参表),内嵌对象2(形参表),...&成为初始化列表,可以用于完成对内嵌对象的初始化。其实,一般的数据成员也可以这样初始化,就是把这里的内嵌对象都换成一般的数据成员,后面的形参表换成用来的初始化一般数据成员的变量形参,比如,Point::Point(int xx, int yy):X(xx),Y(yy) { },这个定义应该怎么理解呢?就是我们在构造Point类的对象时传入实参初始化xx和yy,然后用xx的值初始化Point类的数据成员X,用yy的值初始化数据成员Y。&&&& &&声明一个组合类的对象时,不仅它自身的构造函数会被调用,还会调用其内嵌对象的构造函数。那么,这些构造函数的调用是什么顺序呢?首先,根据前面说的初始化列表,按照内嵌对象在组合类的声明中出现的次序,依次调用内嵌对象的构造函数,然后再执行本类的构造函数的函数体。比如下面例子中对于Distance类中的p1和p2就是先调用p1的构造函数,再调用p2的构造函数。因为Point p1,p2;是先声明的p1后声明的p2。最后才是执行Distance构造函数的函数体。&&&&& &如果声明组合类的对象时没有指定对象的初始值的话,就会自动调用无形参的构造函数,构造内嵌对象时也会对应的调用内嵌对象的无形参的构造函数。析构函数的执行顺序与构造函数正好相反。&&&&& &这里鸡啄米给大家一个类的组合的例子,其中,Distance类就是组合类,可以计算两个点的距离,它包含了Point类的两个对象p1和p2。&&&& & #include &iostream&&&&&&&&&&& & class Point&&&& & { &&&& & public:&&&&&&&&&&&&&&& &Point(int xx,int yy)&& { X= Y= } //构造函数&&&&&&&&&&&&&&& &Point(Point &p);&&&&&&&&&&&&&&&&&int GetX(void)&&&& { return X; }&&&&&&& //取X坐标&&&&&&&&&&&&&&& &int GetY(void)&&&& { return Y; } //取Y坐标&&&&& &private:&&&&&&&&&&&&&&& &int X,Y; //点的坐标&&&&& &};&&&&&& Point::Point(Point &p)&&&&&& {&&&&&&&&&&&&&&&& X = p.X;&&&&&&&&&&&&&&&&&Y = p.Y;&&&&&&&&&&&&&&&& cout && &Point拷贝构造函数被调用& &&&&&&&& }&&&& & class Distance&&&&& &{&&&&&& public:&&&&&&&&&&&&&&&&Distance(Point a,Point b); //构造函数&&&&&&&&&&&&&& &double GetDis()&& { }&&&&& &private:&&&&&&&&&&&&&&&&Point& p1,p2;&&&&&&&&&&&&& &&&&&&&&&&&&&&& // 距离&&&&& & };&&& &&& // 组合类的构造函数&&&&&& &Distance::Distance(Point a, Point b):p1(a),p2(b)&&&&&& &{&&&&&&&&&&&&&&& cout && &Distance构造函数被调用& &&&&&&&&&&&&&&&&& double x = double(p1.GetX() - p2.GetX());&&&&&&&&&&&&&&& double y = double(p1.GetY() - p2.GetY());&&&&&&&&&&&&&& &dist = sqrt(x*x + y*y);&&&&&&&&&&&&&& &&&&&&& &}&&&&& & int _tmain(int argc, _TCHAR* argv[])&&&&& & {&&&&&&&&&&&&&& Point myp1(1,1), myp2(4,5);&&&&&&&&&&&&&& Distance myd(myp1, myp2);&&&&&&&&&&&&&& cout && &The distance is:&;&&&&&&&&&&&&&& cout && myd.GetDis() &&&&&&&&&&&&&&&& return 0;&&&&&&& }&&&&&& 这段程序的运行结果是:&&&&&& Point拷贝构造函数被调用&&&&&& Point拷贝构造函数被调用&&&&&& Point拷贝构造函数被调用&&&&&& Point拷贝构造函数被调用&&&&& &Distance构造函数被调用&&&&&& The distance is:5&&&&& &Point类的构造函数是内联成员函数,内联成员函数在中已经讲过。&&&&&& 鸡啄米给大家分析下这个程序,首先生成两个Point类的对象,然后构造Distance类的对象myd,最后输出两点的距离。Point类的拷贝构造函数被调用了4次,而且都是在Distance类构造函数执行之前进行的,在Distance构造函数进行实参和形参的结合时,也就是传入myp1和myp2的值时调用了两次,在用传入的值初始化内嵌对象p1和p2时又调用了两次。两点的距离在Distance的构造函数中计算出来,存放在其私有数据成员dist中,只能通过公有成员函数GetDis()来访问。&&&&& &鸡啄米再跟大家说下类组合时的一种特殊情况,就是两个类可能相互包含,即类A中有类B类型的内嵌对象,类B中也有A类型的内嵌对象。我们知道,C++中,要使用一个类必须在使用前已经声明了该类,但是两个类互相包含时就肯定有一个类在定义之前就被引用了,这时候怎么办呢?就要用到前向引用声明了。前向引用声明是在引用没有定义的类之前对该类进行声明,这只是为程序声明一个代表该类的标识符,类的具体定义可以在程序的其他地方,简单说,就是声明下这个标识符是个类,它的定义你可以在别的地方找到。&&&&& &比如,类A的公有成员函数f的形参是类B的对象,同时类B的公有成员函数g的形参是类A的对象,这时就必须使用前向引用声明:&&&&& &class B;& //前向引用声明&&&&&& class A&&&&&& {& &&&&&& public:&&&&&&&&&&&&&&&&&& void f(B b);&&&&&& };&&&&&&&class B&&&&&& {& &&&&&& public:&&&&&&&&&&&&&&&&& void g(A a);&&&&&& };&&&&& &这段程序的第一行给出了类B的前向引用声明,说明B是一个类,它具有类的所有属性,具体的定义在其他地方。&&&&&& 今天鸡啄米讲了类的组合,这个知识还是很实用的,可以让我们的程序更灵活,不至于太庞杂让人摸不着头脑。面向对象是不是很强大啊?希望大家从思想上领会它。&
除非特别注明,文章均为原创
转载请标明本文地址:
作者:鸡啄米
&&( 22:9:51)&&( 21:4:52)&&( 22:35:5)&&( 23:2:28)&&( 21:37:49)&&( 20:40:25)&&( 20:25:52)&&( 20:51:25)&&( 20:15:8)&&( 21:7:46)
来支持一下,呵呵。鸡啄米 于
20:59:51 回复欢迎再来啊
加油!!!!学习知识了@!鸡啄米 于
23:19:38 回复谢谢,常来交流啊
觉得还是JAVA简单点嗯
有点难,但是讲的不错鸡啄米 于
21:00:24 回复谢谢,给我动力了
我大学也学了C++,感觉好难啊鸡啄米 于
20:04:52 回复那时候我也觉得有点难,呵呵
一定要跟楼主好好学学C++
不会应用呀
呵呵呵,来看看
以前学C++还没学到这里 继续学习新知识 楼主写得很好!鸡啄米 于
23:23:02 回复谢谢。欢迎常来交流
真心感觉不错,最近一段时间我都在认真的看看。虽然以前学过C++,但是都忘得差不多了。重新来看,有了很多不同的感觉。鸡啄米 于
23:29:48 回复其实即使是忘了,重新看也会有更多新的领悟,不同于没有接触过
写的很好哦。虽然这块我个人觉得很难,准备跳过去学其他的,但还是给个赞
Point类的拷贝构造函数被调用了4次,而且都是在Distance类构造函数执行之前进行的,在Distance构造函数进行实参和形参的结合时,也就是传入myp1和myp2的值时调用了两次,在用传入的值初始化内嵌对象p1和p2时又调用了两次。这里,还没太搞懂。只知道“Distance myd(myp1, myp2);”这里调用了两次,另外两次,真的没搞明白。“Point myp1(1,1), myp2(4,5);”这里难道也要调用吗?这里不是调用的是“构造函数”吗,怎么会是“拷贝构造函数”呢?ljj 于
10:31:00 回复把实参付给形参时有两次,把形参付给p1,p2时又两次,个人理解
同12楼,我也是那里糊涂,最后两次感觉调用的是构造函数。。。。。不是拷贝构造函数
Point(int xx,int yy)
{ X= Y=cout && &Point构造函数被调用& &&cout && &Point& && X &&&.& && Y &&} //构造函数Point::Point(Point &p){X = p.X;Y = p.Y;cout && &Point拷贝构造函数被调用& &&cout && &Point拷贝& && X &&&.& && Y &&}
int _tmain(int argc, _TCHAR* argv[])
int aa,bb,cc,//
cout && &input point A& &&//
cout && &input point B& &&//
Point myp1(aa,bb), myp2(cc,dd);
Distance myd(myp1, myp2);
cout && &The distance is:&;
cout && myd.GetDis() &&
}Point构造函数被调用Point11.33Point构造函数被调用Point77.89Point拷贝构造函数被调用Point拷贝77.89Point拷贝构造函数被调用Point拷贝11.33Point拷贝构造函数被调用Point拷贝11.33Point拷贝构造函数被调用Point拷贝77.89Distance构造函数被调用The distance is:86.5563输出如下:修改了下构造函数的提示信息 于
11:33:56 回复楼主原来的构造函数的输出是没差别的输出,不知道哪个输出是对应哪个点的构造函数。
VS2013里为什么必须要加#include &stdafx.h&的头文件才可以运行啊?
不啰嗦,调用组合类的构造函数时,首先(Point)myp1赋值给a,调用拷贝构造一次,同理,myp2赋值给b,这是第二次调用拷贝构造!然后!由于组合类创建对象,首先初始化组合类中的对象,即p2(b),把b赋值给p2,为什么是它?因为参数列表从右向左!又调用一次拷贝构造函数!同理,p1(a),又一次拷贝!!!所以一共4次!其他大概都容易理解的
完全随机文章C++面向对象 - 推酷
C++面向对象
此博文仅作为C++考研专业课的复习内容。
在对象被创建的时候将自动调用。
复制构造函数
形参是本类对象的引用。其作用是使用一个已经存在的对象,去初始化一个同类的新对象。
复制构造函数在以下三种情况会被调用。
当用类的一个对象去初始化该类的另一个对象。
Point a(1,2);
//两种写法都会调用复制构造函数,只是写法上不同。
Point b(a);//用对象a初始化b
Point c =////用对象a初始化b
如果函数的形参是类的对象,调用函数时,进行形参和实参结合时。
void foo(Point p){
cout&&p.getX()&&
int main(){
Point a(1,2);
注:值传递时调用复制构造函数,传引用时不会调用。所以传递比较大的对象时,传递引用效率会更高。
如果函数的返回值是类的对象,函数执行完成返回调用者时。
Point foo(){
Point a(1,2);
int main(){
注:a离开函数foo()时,a对象会消亡,此时会调用复制构造函数创建一个无名的临时对象存在于表达式b= foo()中。
析构函数是用来完成对象被删除前的一些清理工作,是在对象的生存期即将结束的时候被
析构函数的调用执行顺序与构造函数刚好相反。
当创建类的对象时,会先初始化类中的其他对象和基本数据类型的成员变量。构造函数按以下顺序调用:
调用内嵌对象的构造函数。
用顺序按照内嵌对象在组合类中定义的顺序
。注意,内嵌对象在构造函数的初始化列表中出现的顺序与内嵌对象构造函数的调用顺序均无关。
执行本类构造函数的函数体。
析构函数的调用执行顺序与构造函数刚好相反。析构函数的函数体执行完成后,内嵌对象的析构函数一一执行。
内嵌对象的析构函数调用顺序和他们在组合类中的类定义中出现的次序刚好相反。
类的拷贝构造函数编写。需要为内嵌成员对象的复制构造函数传递参数。
class Point{
Point(Point& p);
class Line{
Point p1,p2;
Line(Point& p1,Point& p2);
Line::Line(Point& xp1,Point& xp2):p1(xp1),p2(xp2){
//to-do something
前向引用申明
class B; //前向引用申明
void foo(B b);
void bar(A a);
当类发生循环依赖时,需要使用前向引用申明。但是即便是存在前向引用申明,但是无法直接定义类的对象。
类的静态成员
静态数据成员
static修饰。类属性是描述类的所有对象共同特征的数据项,对任何实例,他的属性值是相同的。
class Point{
void show_count(){
cout&&count&&
int Point::count = 0;
静态数据成员单独在类外初始化,需要专门为他们分配空间。
静态函数成员
静态成员函数可以直接访问该类的静态数据和函数成员。而访问非静态成员必须通过对象名。
static void foo(A a);
void A::foo(A a){
cout&&x; //对x的引用时错误的。
cout&&a.x; //正确。
友元关系提供了不同类或对象的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。
友元的特点
友元关系无法传递。
友元关系是单向的。
友元关系不能被继承。
友元函数是在类中用关键字friend修饰的非成员函数。可以是普通函数,也可以是其他类的成员函数。类的友元函数可以通过对象名访问类的私有和保护成员。
class Point{
Point(int x,int y);
int getX();
int getY();
friend float dist (Point &p1,Point &p2);
float dist(Point &p1,Point &p2){
double x = p1.x - p2.x;
double y = p1.y - p2.y;
return static_cast&float&(sqrt(x * x + y * y));
int main(){
Point p1(1,1),p2(4,5);
cout&&dist(p1,p2);
同友元函数一样,一个类也可以将另一个类声明为友元类。
若A类为B类的友元类,则A类的所有成员都是B类的友元函数,都可以访问B类的私有和保护成员
int getX();
friend class B; //B是A的友元类
void set(int i);
void B::set(int i){
a.x = //由于B是A的友元类,所以在B的成员函数中可以访问A类对象的私有成员。
共享数据的保护
数据成员值在对象的整个生存期间内不改变。
常对象必须进行初始化,而且不能被更新。
const A a(3,4);
用const修饰的类成员
常成员函数
类型说明符 函数名 (参数列表) const
void foo()
定义和申明时都要带const关键字。
常对象只能调用常成员函数。
无论是否通过常对象调用常成员函数,在函数调用期间,目的对象都被视为常对象,因此常成员函数不能更新目的对象的数据成员,也不能针对目的对象调用其非const修饰的函数。
void foo() const{
cout&&&foo const&&&
this-&bar();//error C2662: “A::bar”: 不能将“this”指针从“const A”转换为“A &
this-&x = 1;//error C3490: 由于正在通过常量对象访问“x”,因此无法对其进行修改
void bar(){
cout&&&bar&&&
const可以区分函数的重载。例如申明如下函数。
void print();
void print()
此时常对象会调用第二个,非常对象就近调用第一个。
常数据成员
常数据成员
只能通过构造函数的初始化列表来指定具体值
A(int i):a(i){
//a = //error
//static const int b = 10; //正确 int或enum的静态常量可在类中直接指定值。
const int A::b = 10;
所引用的对象无法更新。
需要注意的是:
非const的引用只能绑定到普通对象,但常引用可以绑定到常对象。
常引用对象,通过该引用访问时,只能把对象当做常对象。(不能修改数据成员和调用非const成员函数)
已发表评论数()
已收藏到推刊!
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
没有分页内容
图片无法显示
视频无法显示
与原文不一致捷配欢迎您!
微信扫一扫关注我们
当前位置:&>>&&>>&&>>&使用C++构建嵌入式开发框架
摘要:框架作为一种大粒度的重用技术在桌面软件开发中得到了广泛应用,而在嵌入式开发领域,目前还没有一套完整的标准框架可供使用。本文以通信领域的嵌入式软件开发为例,介绍使用C++语言,在ARM平台Nucleus plus操作系统下实现嵌入式开发框架EFC的方法和应用实例。框架 C++ ARM Nucleus MFC EFC 面向对象1 框架概述1.1 什么是框架国外著名的软件设计大师Ralph Johnson对面向对象技术进行了长期而深入的研究。在他的主页中,对框架进行了如下定义:A framework is a reusable design expressed as a set of abstract classes and the way their instances collaborate.It is a reusable design for all or
.(框架是整个系统或系统的一部分的可重用性设计,由一组抽象出来的类及其实例间的相互作用方式组成。)框架把一个系统有机地分解成一组相对独立的构件,并定义了各个构件间的接口和作用关系,符合软件工程中设计的模块化、独立化和信息隐藏等特征。框架提供了一个大粒度的重用技术,即不仅支持源代码级的重用,而且支持分析和设计以及体系结构的重用,因而被认为是一种最有前途的面向对象技术。框架必须是健壮的、可扩展的、灵活的,它要求基于开放或共享标准。框架的设计要力求做到完备性、灵活性、可扩展性、可理解性,同时抽象能用于不同的场合;用户能轻松地添加和修改功能,定制框架;用户和框架的交互清晰,文档齐全。框架设计的一个核心问题就是发现可重用的设计和“热点”,以保证框架具备充分的灵活性,使用户能在已有构件的基础上生成应用程序,实现“零代码编写”的理想目标。
1.2 如何设计框架目前框架的设计大都采用实践法。实践法是指从若干个具体的典型应用中,抽象出现似点来构建框架;框架反过来又应用于不同的问题,并在解决不同问题的过程中得到更新;在框架的设计和实现的两步中,不断反复,等到框架逐渐成熟时,需要修改和反复的内容就会越来越小。具体步骤为:分析问题域,确定所需框架,从一类应用而不是单个的程序去分析、比较各种不同的软件解决方案,寻求这些方案的共性和每个程度的唯一性特性。这些共性,尤其是那些经常被多个程序使用的部分将成为框架的基础。然后,定义框架体系结构并设计,包括设计用户与框架间的交互、给用户提供的最终工具等。框架的实现:包括框架核心类的实现、框架的测试、框架的试运行、框架的反复更新。框架的部署:包括文档的提供和分发过程、为用户提供技术支持、维护和更新框架。2 嵌入式框架EFC框架技术在桌面软件的开发中得到了广泛的应用,但在嵌入式开发领域,由于嵌入式开发的多样性及嵌入式操作系统的多样性,目前还没有一套完整的开发框架可供使用。因此,在嵌入式软件开发中常常是从底层做起,应用程序和RTOS密不可分。这样的开发方式不但效率不高,也不利于软件的移植。EFC(Embedded Foundation Classes)即嵌入式基础类库,是笔者借鉴Microsoft公司的MFC(微软基础类库―桌面系统框架库的工业标准)构建的一套在ARM平台Nucleus plus操作系统下的嵌入式开发框架。由于框架全部采用C++开发,没有和处理器相关的汇编代码,所以在其它硬件平台可不加修改地使用。如果更换不同的操作系统,则需要修改操作系统抽象层的部分代码;但由于EFC提供给上层应用程序的接口不变,所以应用程序不需要修改代码。图2 EFC静态结构图
就软件的层次来说,EFC是一个操作系统之上、应用程序之下的中间件,如图1所示。在EFC中有一个操作系统抽象层,对RTOS进行了抽象和封装,提供包括任务(task)、/O驱动()、(timer)、信号量(semaphore)、消息队列(quecue)、事件(event )、邮箱(mailBox)、管道(pipe)以及高级中断(HISR)等基本服务的封装。为上层应用程序提供更高级的统一编程接口,它样就使应用软件的开发与具体的软件平台无关,解决了嵌入式应用软件的移植问题。在图1中,各模块之间有交界表明模块之间有接口关系。EFC、应用程序以及RTOS都和硬件驱动有接口:EFC要使用一部分核心驱动(例如实时时钟的驱动、ARM和网口的驱动、I2C的驱动等);应用程序中调用的驱动是针对具体设备的;RTOS所需要的驱动就是系统的BSP部分。EFC的静态结构图(类图)如图2所示。类图是在UML(统一建模语言)中用类和它们之间的关系描述系统的一种图示。类用类名、类的属性以及操作来表示,在图中为简单起见,省略了属性和操作;类与类之间的关系使用不同的连线表示,图中带空心三角箭头的连线表示继承关系,两端带数字的连线表示关联关系。在类图中,类的属性/方法的可见性使用“+”、“-”及“#”表示:“+”表示公共的(public),“-”表示私有的(private),“#”表示受保护的(protected)。从图2中可以看出,CMessage、CRTApp、CDevice、Cboard及Cinterface都派生于公共的类CRTObject。CRTApp对象中有受保护的CMessage、CEventLog、Cuser及CDevice各一个。CDevice对象中有一个或多个CBoard对象,相应的每个CBorad对象中有0个到多个CxxxInterace对象。2.1 基本数据类型构建一个框架,需要一些基本的元素,这些元素要在框架的构造以及应用程序开发中大量使用。这些基本数据类型包括字符串类CString、集合类CArray、Clist及Cmap。CString包括一个长度可变的字符序列,提供使用非常直观方便的运算符(例如+,+=,=,==,!=)和一些Todouble()、Tolong()、Tohex()等);CArray是具有内建索元素很快的检索速度;Clist为其所存储的每一个元素,都提供了两个指针,分别指向位于其前和其后的元素,形成一个双向链表,这使得插入和删除操作十分快捷;CMap为其存储的每个数据都附带一个关键字,并以关键字所组成的一个hash表作为索引,从而使得元素搜索、增加和删除操作都具有很高的效率。2.2 RTOS的抽象和封装CRTObject是一个EFC中最基础的类,它不但是EFC中CRTApp、CDevice等类的基类,而且可以作为所有使用EFC的嵌入式开发人员定义新的类的超类。CRTObject类在EFC中主要承担RTOS抽象和封装任务。它提供了下面一些最基本的功能:*CRTObject对RTOS的常用对象进行了封装,提供包括Task、Driver、Timer、Event Group、Semaphore、Queue、Pipe、Mailbox等的创建、删除、查找等功能的成员函数。这些函数提供了一个简单有效的方法来使用RTOS的对象。使用这些函数能够保证对象创建与销毁的安全性,而不会造成泄漏。*CRTObject提供了对RTTI(Run-Time Type Information,运行时类型信息)的支持,在新的C++标准中,RTTI已经是C++的一个功能,但并不是所有的编译器都提供支持这些新特性,ADS1.2就不支持。所以在这里参考MFC,通过宏的方式为每个类定义一个CRuntimeClass类型的静态常量和相关的成员函数。CRuntimeClass结构保证了类型的静态常量和相关的成员函数。CRuntimeClass结构保存了类的名称、大小等信息,这样我们就能在程序运行时确定对象的具体类型。*CRTObject还提供了把类的成员函数作为任务及定时器的回调函数的功能。在Nucleus中,任务和定时器的回调函数只能是全局函数或者类的静态成员函数,这在面向对象的开发中很不方便。这里通过把成员函数指针和对象的this指针作为参数传递给RTOS,在RTOS调用公共回调函数时再取出来。通过函数指针的方式去调用类的成员函数,这样把有派生于CRTObject的类就可方便地使用成员函数作为任务、定时器等对象的回调函数。2.3 应用程序类CRTAppCRTApp类用来定义整个应用程序对象,提供系统初始化、管理其它对象以及运行应用程序的功能。任何使用EFC框架的应用程序有且只能有一个派生于此类的对象。CRTApp对象中包含了动态创建的CMessage、CEventLog、CDevice及Cuser对象。通过在Nucleus的入口函数Application_Initialize中创建系统初始化任务(回调函数为CRTApp类的成员函数InitTask),来把系统控制权交给CRTApp对象,在其中完成其它对象的创建、系统的配置以及初始化任务。2.4 文件系统在嵌入式设备中通常使用Flash来保存程序代码和数据,每片Flash一般由一定数量大小不等的扇区组成。它在读取方面与普通RAM存储器类似,可以实现随机的读取,但在写入操作上却有很大的不同。Flash中只有空白的单元才可以进行写入操作,要向非空的单元写入数据,需要先擦除整个扇区。所以程序中如果直接对Flash进行操作会很不方便。最好的办法就是在其上构造一个文件系统,文件系统提供简便、可靠的接口供上层使用,而把复杂的操作屏蔽在文件系统内部。这里文件系统包括内存文件系统和Flash文件系统。CFile是一个抽象类,只是定义文件系统的接口函数(例如Open、Read、Write、Seek、GetLength、Close等),具体的实现在CMemFile(内存文件)及CFlashFile(Flash文件)类中完成。2.5 设备管理在EFC中,设备管理由CDevice、CBoard、CInterface及其派生类完成。CDevice类代表整个设备,1个设备中包含1到多个CBoard对象,而每个CBoard对象中又包含0个到多个接口对象(CInterface类的派生类对象)。这样以来,嵌入式设备(仅限通信领域)都可由这几个类组合而成,大大简化了软件的设计。2.6 命令处理CMessage类是系统的命令处理模块,它直接派生于CRTObject类。它的功能主要是接收网管软件通过串口或网口发送未来的各种命令,完成对设备的配置管理、性能管理、告警管理、安全管理和维护管理等管理功能。CMessage类主要有表1所列的任务。表1 CMessage类中的任务任务名称任务处理函数说
明TCP监听任务TCPServerTask用于监听客户端的连接请求TCP响应任务TCPEchoTask对每客户端的连接都创建一响应任务串口任务UartTask通过串口对系统进行管理TFTP备分份任务TFTPClientPutTask备份应用程序和系统配置文件TFTP升级任务TFTPClientGetTask用于升级应用程序及修改用户配置文件2.7 其它模块CFlash类封装对Flash芯片的操作,主要包括读、写、擦除等操作。从图2可以看出,CEventLog和CFlashFile类中都包含CFlash对象;CEventLog类记录系统中的发生的事件以及系统运行过程中产生的告警信息。为了实现掉电保存功能,这些事件都保存在Flash芯片中;Cuser类用来对系统的用户进行管理,防止对系统非授权的访问。3 使用EFC的设计方案举例这里以在通信和工业自动化领域使用较多的为例,来说明使用FEC嵌入式开发框架的设计方案。串口服务器是一种可把多路异步RS232/RS485串行数据与通过以太网口传送的TCP/IP数据包进行相互转换,使传统的异步串行数据信息能通过Internet或Intranet传送或共享的设备。设每个串口对应TCP/IP的一个端口,则可画出图3所示的静态结构图(图中SerSvr是Server的简写)。从图3可以看出,共对五个类进行了派生。CSerSvrMessage类派生于CMessage类,用于通过网管对串口服务器进行管理(这里具体命令略);CserSvrDevice类派生于CDevice类,代表串口服务器设备;CserSvrDevice类对象中有一个或多个派生于CBoard类的CserSvrBoard类对象,而每个CserSvrBoard类对象拥有一个或多个派生于CasyInterface类的CSerSvrInterface类对象;ScerSvrApp类派生于CRTApp类,代表整个应用程序,并重载了虚函数OnCreateMessage()及OnCreateDevice(),用来在其中创建系统的CSerSvrMessage和CserSvrDevice类的实例来代替系统默认的CMessage和CDevice实例。图3 串口服务器系统软件静态结构图
串口类CSerSvrInterface的设计是整个设计的关键,在每个串口类中都有一个TCP监听任务TCPServerTask用来作为服务器去监听客户端的连接;一个TCP客户端任务TCPClientTask用来连接其它服务器。无论是通过TCPServerTask还是TCPClientTask建立连接后,就挂起这两个任务而启动另外两个任务TCPSendTask和TCPRecvTask,它们分别用来通过网口发送数据和接收数据。TCPSendTask每隔10ms(对波特率为115.2K的情况,10ms最多收到的字节数为115200/(8+2)/.2字节,所以串口的FIFO应大于116字节)把从串口读到的数据打包从网口发送出去;而TCPTecvTask使用阻塞方式读取网口数据。在读到数据后,根据串口发送缓冲区的情况慢慢通过串口往外发送,没发送完之前就不进行下一次从网口的数据读取。这样把串口类设计成一个完备的处理类,设备中每块板有多少串口就在CSerSvrBoard类的实例中有多少CSerSvrInterface类的实例。硬件模块化的结构简单地对应软件模块化的结构。结语本文讲述了在嵌入式软件开发中使用C++构建系统开发框架的方法,并给出了框架的模型和应用实例。可以看出,使用面向对象的框架技术对于提高开发效率、降低开发难度、规范开发模式、便于软件的移植和维护方面,具有传统面向过程的开发方法不可比拟的优势。
技术资料出处:流氓兔子
该文章仅供学习参考使用,版权归作者所有。
因本网站内容较多,未能及时联系上的作者,请按本网站显示的方式与我们联系。
【】【】【】【】
上一篇:下一篇:
本文已有(0)篇评论
发表技术资料评论,请使用文明用语
字符数不能超过255
暂且没有评论!
12345678910
12345678910
12345678910
12345678910
如图所示,这是ADI的客户Solar Edge推出的全新一代HDV5太阳能逆变器,相比较前一代,减少了16倍的磁器件,效率提高到99%,体积是原来的一半,重量是原来的一半,散热器件比原来少了2.5倍。、
[][][][][][][][][][]
IC热门型号}

我要回帖

更多关于 c 内嵌对象 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信