嵌入式培训经验分享——面向对象语言C++

在学习C语言和C++的过程中,我们知道C语言是面向过程的编程语言,而C++是面向对象的编程语言,在学习面向对象的编程语言的时候,我们必须要学习的就是面向对象的三大特征点:封装、继承、多态。

1、封装

对象:凡是占据一定空间的事物,都称为对象,实物都可以称为对象,占据空间,想象的实物不能算作对象,因        
     为是我们想象的虚拟事物,不在显示世界占据空间。
     一切皆对象. 这个世界是因为对象相互交互而运行的,对象与对象需要交流、通信、相互配合。
     世界是面向对象的,编程要实现一个复杂的系统,最好是模拟现实世界的.
     
  类(class): 现实世界中动物可以算作很多类,比如猫类、犬类等等, 但是有一类东西是不占据空间的,         
     类是一个总结,概念,抽象的,不存在现实世界的.
     类不存在的,只是一个统称。所以我们具体到类中的某个对象,比如猫类中的橘猫就是存在的,可以称  
     为一个对象干活,对象是存在的。
     类,是一类(对象)统称.
     
     
     
  如何描述对象:
  class 橘猫{
    静态的属性
    (姓名、年龄、体重等等)
    动态的方法/行为/函数
    (会爬树、会咬人、会抓老鼠)
  }
   我们将橘猫拥有的特性写入一个类里面装起来,之后只有橘猫才可以使用这些属性和
   行为,这个过程我们就叫封装,这个类与结构体相似,区别在于可以写入函数等等。

封装的过程中有很多的知识点需要我们学习:

(1)类的定义和使用

#include <iostream>   // std::cout  std::cin

using namespace std;

/*
	类中有很多函数,每个函数都很长,那么class会很难看.
	怎么办
	1.函数的实现部分写在类外
	2.类内声明有该函数


*/
class cat {
public:
	//静态的属性 
	string name;
	string sex;
	int age;
	float weight;
	//动态的方法
	int doCatchMouse(int catchType);
	int doClimbTree(void);
	//类函数声明
	void init(string xname,string xsex,int xage,float xweight);
	void show(void);
};

int cat::doCatchMouse(int catchType) 
{
	cout<<"catchType="<<catchType<<endl; 
	return 1;
}

int cat::doClimbTree(void)
{
	cout<<"climbTree"<<endl;
}

void cat::init(string xname,string xsex,int xage,float xweight) 
{
	name=xname;
	sex=xsex;
	age=xage;
	weight=xweight;
	
}

void cat::show(void) 
{ 
	cout<<"name="<<name<<" sex="<<sex<<" age="<<age<<" weight="<<weight<<endl;   
}

int main()
{
	
	cat xiaohua;
	xiaohua.name="xioahua";
	xiaohua.sex="man";
	xiaohua.age=4;
	xiaohua.weight=3.4;
	int d = xiaohua.doCatchMouse(66);
	d = xiaohua.doClimbTree();
	xiaohua.show();
	
	cout<<"####### dahua############"<<endl;
	cat dahua;
	dahua.name="dahua";
	dahua.sex="woman";
	dahua.age=3;
	dahua.weight=4;
	dahua.show();
	
	cout<<"####### laohuahua############"<<endl;
	cat laohua;
	laohua.init("laohua","man",9,6.8);
	laohua.show();
	
	cout<<"###################### 构造函数 <<<<<<<<<<"<<endl;
	
	return 0;
}

(2)构造函数与析构函数
     需求: 每当我定义一个新的对象,我都希望对象中 成员被初始化一次,有一下方法:
       1) 自己写函数init----

#include<iostream>
using namespace std;


class cat{
public:
	string name;
	string sex;
	int age;
	double weight;
	
	void show() 
	{
		cout<<"name="<<name<<" sex="<<sex<<" age="<<age<<" weight="<<weight<<endl;
	}
	
	
	cat(): name("noname"),sex("man"),age(1),weight(0.1)   //函数 参数初始化列表
	{  
		//等同于  name="noname";sex="man";age=0;weight=0.1;   
	}
	/*
		初始化列表:
		1.只有构造函数可以,紧跟函数名后面   fun() : 变量名(值),变量名(值)..
		2.初始化列表先于{}执行.
	*/
	cat(string xname,string xsex,int xage):  name( xname),sex(xsex),age(xage)
	{
		//等同于 
		//name=xname;
		//sex=xsex;age=xage;
	}
	
};


int main()
{
	cat xiaohua;
	xiaohua.show();
	
	cat dahua   ( "dahua","man",3);
	dahua.show();
}


       2) 类/对象 提供了一个 专门的函数 来完成对象的初始化.
            称为----构造函数, 构建制造---对象产生的时候自动执行,我们也可以对这个函数进行重构                可以传递参数进去赋值,构造函数可以不止一个。 
 

#include<iostream>
#include <cstring>

using namespace std;


class cat {
public:
	string name;
	string sex;
	int age;
	char *desbuf;
	
	cat() { name="noname";sex="unkown";age=0;desbuf=new char[32];  }
	cat(string xname,string xsex,int xage)
		{ name=xname; sex=xsex; age=xage; desbuf=new char[32];}
	cat(string xname,string xsex,int xage,char *des)
		{ name=xname; sex=xsex; age=xage; desbuf=new char[32];strcpy(desbuf,des);}
		
	//拷贝构造函数,   参数是另一个对象,触发条件使用旧对象初始化新对象
	cat( cat &oldobj) {  cout<<"in copy structure"<<endl; 
			name="noname"; sex=oldobj.sex;age=oldobj.age;
			desbuf=new char[32]; strcpy(desbuf,oldobj.desbuf);
		}
	
	
	void show(){ cout<<"name="<<name<<" sex="<<sex<<" age="<<age<<" des:"<<desbuf<<endl;}
};

int main()
{
	
	cat xiaohua   ("xiaohua","man",3,"des,bo si mao");
	
	//cat dahua =  xiaohua;		//使用另外一个对象来初始化 新对象,效果等同于下一行
	cat dahua      (xiaohua);
	
	/* 他调用的是 拷贝构造函数 
		cat dahua =  xiaohua;   ---g++翻译为-->    cat dahua      (cat &obj);
		如果你不写,系统帮你制定一个,   这个函数使用简单的覆盖机制		-----浅拷贝
		这种 简单拷贝方法,有问题:


		strcpy(dahua.desbuf,"this is dahua");
		xiaohua.show();
		dahua.show();		发现两个对象使用的 同一片空间,不行
		
		我们要重写############################## 完成自己的拷贝			-----深拷贝
	*/
	
	dahua.name="dahua";
	strcpy(dahua.desbuf,"this is dahua");
	xiaohua.show();
	
}

  构造函数     对成员进行初始化
     1.和类名一致,没有返回值-不是void,    用于和普通函数区分
     2.当对象被构成/产生的时候,函数自动执行--系统调用的
     3.构造可以有多个---函数重载   还可以有默认值
       注:当你不写构造,编译器帮你指定一个 默认构造   对象名(){  }

析构函数:析构函数与构造函数不同,在一个函数中,析构函数有且只有一个,析构函数在程序或这个类的作用域结束的时候自动执行。在对数的情况下,析构函数都是用来释放空间的。


#include <iostream>
#include <unistd.h>

using namespace std;

class cat {
public:
	//静态的成员
	string name;
	string sex;
	int age;
	char*buf;
	
	/*构造函数		对成员进行初始化
		1.和类名一致,没有返回值-不是void,  	用于和普通函数区分
		2.当对象被构成/产生的时候,函数自动执行--系统调用的
		3.构造可以有多个---函数重载		还可以有默认值
		
		当你不写构造,编译器帮你指定一个 默认构造    cat(){ 啥都不干 }
	*/
	cat() {cout<<"in structure cat()"<<endl; name="huahua";sex="woman";age=1;   buf=new char [128]; }  //默认构造
	cat(string xname,string xsex) {cout<<"in structure cat(xname,xsex)"<<endl;name=xname;sex=xsex; buf=new char [128];};
	cat(string xname,string xsex,int xage  ){cout<<"in structure cat(xname,xsex,xage)"<<endl;name=xname;sex=xsex; age=xage;  buf=new char [128];} 
	
	//void deinit() {delete []buf;} 
	/*
		析构:  当对象消失的时候,自动调用,用来释放一些空间
					1) delete p;   2)main函数退出--所有对象都析构  3)局部对象释放

			1.析构函数没有返回值,和类名一致,前面加了~
			2.析构函数只有一个
	*/
	~cat() { delete []buf;  cout<<"in ~cat();"<<"name="<<name<<endl; };

	//动态的方法
	void show(void){cout<<"name="<<name<<" sex="<<sex<<" age="<<age<<endl;}
	//void init(string name,string sex .....)
};

void do_show(void)
{
	cat xiaohua ("laolaohua","man");		//当函数退出的时候,析构被执行
}

int main()
{
	cat xiaohua;		//注意,当对象产生,系统帮你自动调用构造函数, 默认构造 
	cat dahua           ("dahua","man");
	cat laohua          ("laohua","woman",9);
 
	//指针访问
	cat *pt =   &xiaohua;
	pt->show();		//效果等于 xiaohua.show();  
 
	cout<<"##########new obj ###########"<<endl;
 	//######### new object-对象 ################
	cat *pt0 =  new cat ;  pt0->show(); // malloc(sizeof(struct cat));  默认构造
	cat *pt1 =  new cat   ("xiaohua1","woman") ;    //指定构造
	cat *pt2 =  new cat   ("xiaohua2","man", 3);
	
 	delete pt0;		// 释放整个对象      但是如果对象指针指向的空间,没有释放
	delete pt1;		// 一般在delete之前释放
	delete pt2;
	
	
	{	
		int a=7;
		cat zuhua  ("zuhua","man",5);  //当{}结束的时候,对象析构执行
	}
	//a=9;  非法,a的生命周期已经结束
	//zuhua.show();
	
	do_show();
	
	while(1){  sleep(1); };
	
	return 0;
}

(3)this 指针(本体指针)

    每一个对象中都隐藏了一个指针,this ,指向对象自己
    用途:
      1)类函数成员-可以获取对象本身    sizeof(*this)
      2)区分类成员和传参

#include<iostream>
using namespace std;

class cat{
	
public:
	string name,sex;
	int age,weight;
	
	
	void show() {cout<<"name "<<name<<"  this ptr="<<this<<"obj sz="<<sizeof( *this)<<endl;}
	cat(string name,string sex,int age,int weight){ this->name=name; this->sex=sex;this->age=age;this->weight=weight; }
private:
	//cat *this = currrent object;  隐藏的指针
};

int main()
{
	cat xiaohua ("xiaohua","woman",2,4);
	xiaohua.name = "xiaohua";
	xiaohua.show(); cout<<" &xiaohua="<<&xiaohua<<endl;

}

(4)static和const用法

static 修饰变量,只能在本文件中使用,修饰局部变量的时候可以延长生命周期。
static 修饰函数,限制函数只能在本文件中使用。

在上面修饰的类型不同,所以我们声明的时候也是不一样的。

修饰变量的时候我们需要在程序执行之前,提前声明初始化里面的静态变量。

但是在修饰的是函数的时候不需要声明,对象在没有声明的时候我们也可以调用该函数。
     static修饰的成员函数,  不依赖于对象而存在,一直都在, 所有的对象共享它.
     注意: static成员函数只能访问 static变量.

#include<iostream>
using namespace std;

class cat {
public:
	static int objcnt; // 静态成员变量,   它只有一份,所有对象共享它
						//它不依赖于对象而存在
 	string name,sex;
	int age;

	cat(){ objcnt++;}
	cat(string name,int age){this->name=name;this->age=age; objcnt++;};
	
	//static void show() {cout<<"name="<<name<<"sex="<<sex<<" staticcnt="<<objcnt<<endl;}
	//静态成员函数,只能访问静态成员变量 ----因为对象可能不存在
	
	static int getCnt(){           return objcnt;}   //静态成员函数:    类成员可以直接访问
};										 			 //静态函数,不依赖于对象而存在,所有对象共享它



int cat::objcnt = 0;		//定义并初始化---这个时候才分配了空间

int main()
{
	cat laohua("laohua",4);
	laohua.show();
	
	
	cout<<"static fun = "<<  cat::getCnt() <<endl;   //静态函数访问方法1: 不依赖于对象而是用
	
	cat xiaohua ;
	cat xiaoqiang("xiaoqiang",3);
	
	/*如何访问静态成员呢
		1.所有对象共享它,可以使用 对象.objcnt
		2.   类名::静态成员  方式;  
		3.类函数可以直接访问
	*/
	
	cout<<"obj cnt:"<<xiaoqiang.objcnt<<endl;  
	cout<<"obj cnt:"<<xiaoqiang.getCnt()<<endl;		静态函数访问方法2: 对象共享它,则对象也可以访问
	
	cout<<"objcnt="<<cat::objcnt<<endl;  //不依赖对象
	
}

const在前面我们讲过一些C++的const与C语言的const的区别,直接看一下用法吧

#include<iostream>
using namespace std;

class person {
public:
	const int id;		//只允许初始化一次,以后再也不会改动了
	const string sex;	//在哪里初始化--####只能在构造 ###
	string name;
	int age;

	person(string xsex,int xid,string xname) : id(xid),sex(xsex)
	{  
		cout<<"in person(sex,id,name)"<<endl;
		//this->sex=sex;  this->id=id;    报错,因为程序不允许改动const常量
		//								  const int aa=4; 以前 定义的时候赋值,编译器搞定的
		//										aa=8;	程序动态改变,不允许的
		//初始化const成员只能在 构造函数的 初始化列表中实现,而且要求 每个构造必须初始化所有的const
		this->name=xname;
	}
	
	//person() {} 非法,编译器要求所有的构造 必须初始化所有 const成员
#if 0	
	void set_sex(string sex)
	{
		this->sex=sex;  //编译器报错,不允许的操作
	}
#endif
	void show()  const
	{ 
		cout<<"id="<<id <<" sex="<<sex<<" name="<<name<<endl;   
		//age = 2;  非法,const函数不能试图修改任何成员
	}

	void set_name(string name){	this->name=name;  }

	/*修饰函数
		有时候我们期望 某个函数不要改动任何东西,为了彻底的保证 他不改动任何成员
		我们使用const修改该函数.   编译器一旦发现const函数试图改变任何成员,就报错.
		
		返回值 函数名(参数) const { 函数体 ;}
	*/
	
};


int main()
{
	person  xiaowang("man",22070401,"xiaowang");
	 
	xiaowang.show();
}

(5)public 和 private

在类的定义中主要的作用是一个限制权限的功能。

#include<iostream>
using namespace std;
  class house {
  public:
     string chair;
     string pc;
     string book;
  
     void speak(void){ }
  
  private:
     string bed;
     string wazi;
     void toliet(){  cout<<wazi<<endl; }
     void sleep() { cout<<bed<<endl; cout<<book<<endl;  }
  };
 
int main()
{
  house xiaomingjia;
  cout<<xiaomingjia.chair <<endl;  //公有成员,可以在类外使用
  xiaomingjia.speak();
  
  cout<<xiaomingjia.wazi<<endl;   //私有成员,不允许在类外使用
 
}
 
  //类的公有成员 可以在类内部使用,也可以在类外使用.
  //类的私有成员 只能在类内使用

2、继承

#include<iostream>
using namespace std;

class animal{		//父类
public:
	string kind;
	int weight;
	
	animal(){ cout<<"in animal( )"<<endl; }
	animal(string kind,int weight,string sex,int age) 
	{   cout<<"in animal( ...)"<<endl;
		this->kind=kind; this->weight=weight;this->sex=sex;this->age=age;
	}
	
	
	~animal() {cout<<"~animal()"<<endl;}
	
	void eat(void){cout<<"animal eat()"<<endl;}
	void run(void){cout<<"animal run()"<<endl; }
	
	void set_sex(string sex) {this->sex=sex;}
	string get_sex() {return sex;}
	void set_age(int age) {this->age=age;}
	int get_age(void){return age;}
private:	//限制类外访问,包括限制 子对象的直接访问
	string sex;
	int age;
	
	void la(void){cout<<"animal la()"<<endl;}
};

/*
	class 子类:父类 {
		父类的东西
		子类的新东西
	}

*/
class cat: public  animal{    
	//已经拥有了animal的##所有东西##,只是父亲的private部分不可以直接访问 
	//然后我们只需要添加子类特有的即可
public:
	string name;
	/*
	####构造函数:
		子类的构造函数在执行的时候,首先去寻找并执行父类的构造函数 (初始化父亲的那一部分)
		之后,再执行子类的部分(初始化子类的部分)
		子类构造,如何执行调用父亲的哪一个构造.......		如果不指定,调用父类的默认构造
			子类构造(参数1,参数2,参数3....):父亲构造(arg1,arg2,....)
			{
				只用来初始化子类特有的
			}
	*/
	cat() {}   //会call father's default structure
	cat(string name,int age,string sex) : animal("cat type" ,5,sex,age)
	{
		cout<<"in cat(...)"<<endl;
		this->name=name;		 
	}
	
	/*
		析构函数:  当子对象被销毁,执行子对象的析构, 在子对象析构执行完毕后,会自动调用父对象的析构
	*/
	~cat() 
	{  
		cout<<"~cat()"<<endl; 
		//调用父亲的析构,不需要写,g++自动帮你添加代码
	}
	
	void show(){ cout<<"name="<<name<<" sex="<<get_sex()<<" age="<<get_age()<<endl;}
	
	void catchMouse(void){cout<<"cat catchmouse" <<endl;}
	void climbTree(void){cout<<"cat climbtree"<<endl;}
private:
	void jump(void){cout<<"cat jump"<<endl;}
	string color;
};

int main()
{
	//cat xiaohua;
	cat xiaohua("xiaoxiaohua",3,"woman");    /* 当子类对象产生,立即触发子类构造函数, 
											子对象构造函数,首先寻找父类构造函数并执行,之后在执行自己的构造函数*/
	xiaohua.show();
	cout<<"####### father class private show##########"<<endl;
	/* xiaohua.sex = "man";  非法的操作
		父类有私有成员, 父类希望这些private都只能在父类中使用.
		子类是不能够直接访问的( 子类中有这些东西 ),如果子类想访问,
		只能通过父类提供的public 方法
	*/
	xiaohua.set_sex("man");
	cout<<"xiaohua sex="<<xiaohua.get_sex()<<endl;
	
	cout<<"########## public member show########"<<endl;
	xiaohua.kind = "cat type";
	xiaohua.weight=3;
	xiaohua.name="xiaohua";
	xiaohua.eat();
	xiaohua.catchMouse();
	
	//main退出之后,进程结束,释放所有空间(包括 xiaohua, ),执行xiaohua的析构函数( ~cat()  )
}

继承是指子类可以继承父类的公共特性和方法,在子类继承了父类之后,子类同样可以在类中添加一些自己特有的东西。继承可分为公共继承和私有继承以及保护继承三种方式。

3、多态

多态即有那种状态,这里指的是一个函数在继承的类中可以被改写,在父类中存在一种实现的方法,同样在子类中也可以重新编写该函数,改变此函数的实现方法。但是当我们在我们称此函数为虚函数。

#include<iostream>
using namespace std;

class animal{
public:
	/*父类的run函数要允许子类覆盖它: 即子类也要实现run
		如何允许子类覆盖掉该函数呢?
		使用virtual修饰函数, 该函数为一个 虚函数,允许子类覆盖.
	*/
	virtual void run() {cout<<"animal run"<<endl;}
	void bark(){cout<<"animal bark"<<endl;}
private:
	string kind;
};
	
class cat:public animal {
public:
	void run() {cout<<"cat run"<<endl;}   //会覆盖掉 父类的 虚函数
	void catchMouse(){cout<<"cat catchMouse"<<endl;}
private:
	string name;
};
		
	

class dog:public animal {
public:
	void run(){cout<<"dog run"<<endl;}
	void watchdog(){ cout<<"watch dog"<<endl;}
private:
	void goupao(){ }
};

int main()
{
	dog xiaowang;
	xiaowang.run();		// dog run
	
	cat xiaohua;
	xiaohua.run();		//  打印cat run;
	
	animal *pt = &xiaowang;
	pt->run();  //打印结果是  子类的run
	/*
		原理是:  
			子类的 run函数覆盖了父类的run(父类的run"消失")了.
	*/
	
}

我们在继承的时候可能会遇到子类函数名与父类函数名相同的情况: 

#include<iostream>
using namespace std;

/*
	定义一个人类为 基类,   包含姓名,年龄,性别ID,工作 娱乐方法;    定义子类,
		实现律师(增加属性方法)     完成工作 娱乐等方法
		码农子类(增加属性方法),  完成工作 娱乐等方法
		
		
		完成构造和析构,至少三种(父类构造)	

 

*/

class person{
public:
	virtual void work()  =0;		//我们发现父类的虚函数 几乎不会被使用,可以不写,直接给0
	virtual void funy()  =0;		//这样的函数,称为   纯虚函数:虚函数没有函数体/实现 
									//拥有纯虚函数的类,是不完整的( 因为函数没有完成),这种类 称为 虚基类
	person() { }					// person xiaowang; 非法,因为虚基类 不完整,大小都确定的.
									// person * p;  合法的.  虚基类可以定义指针,因为 指针大小固定
	person(string xname,string xsex,int xage):name(xname),age(xage),sex(xsex)
	{  }
	
	virtual ~person() {  cout<<"~person()"<<endl;}  /*
		虚析构:  	如果基类某个函数是虚函数,那么他的子孙的该函数都是虚函数---虚函数继承性
					同样,如果 析构 是虚函数,那么 子孙的析构 都是虚函数
			*/
	
private:
	string name;
	int age;
	string sex;
};

class lawer:public person {
public:
	void work() { cout<<" da guan si "<<endl;}
	void funy() { cout<<" bei law book"<<endl;}

	lawer(string name,int workid,int age,string sex):person(name,sex,age )
	{
		this->workid=workid;
	}
	
	~lawer() { cout<<"~lawer()"<<endl;  /*隐藏代码:调用父类的析构 去初始化父类部分*/   }
private:
	int workid;

};

class coder :public person {
public:
	void work() { cout<<" coding "<<endl;}
	void funy() { cout<<" shua github "<<endl;}
	
	~coder() {   cout<<"~coder()"<<endl;  /*隐藏代码:调用父类的析构 去初始化父类部分*/  }
private:
	string workage;
};



int main()
{
	person *p = new  lawer("wanglushi",123789,39,"man");
	p->work();
	p->funy();
	
	delete p;  /* 问题: p是父类指针,虽然指向子类对象,但是只能访问父类的部分,所以这里调用的是父类的析构
					子类部分没法释放,怎么办
						基类的析构改为 虚函数 
						
					这是一个很常用的方法:   
						如果可以,所有的析构 都要写成虚函数.
				*/
	
	p = new coder ;
	p->work();
	p->funy();
	
	delete p;
	
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值