C++面向对象编程之继承

一、 封装和继承

1.1 封装的概念

 封装:通过访问控制属性对类类型中的属性和行为进行打包和限制.


1.2 继承:通过一种机制表达出类型之间的共性和特性的方式.

继承基本语法:
class 子类名 : 继承方式1 父类1,继承方式2 父类2...
{
  类的定义
};

继承方式: 

  public  公有方式

  private 私有方式

  protected 保护方式


1.3 继承的特性

(1) 一个子类类型的对象在任何时候都可以看作基类类型的对象(心里始终要记着).

(2) 一个父类类型的对象作为子类对象去使用,有可能引发段错误.

(3) 在子类中可以访问父类的公有和保护的成员,不可以访问父类中私有成员.

(4) 名字的隐藏,在子类可以定义一个与父类中同名的标识符,子类隐藏父类.(不能构成重载,不在同一作用域下,函数参数也不一定一致.)

(5) 如果非要调用父类中的同名函数,那么可以显示指定 父类::函数名;


1.4 继承方式和访问控制
 限定符    访控属性    本类   子类   外部   友元
---------------------------------------------------------
 public        公开的     ok       ok     ok       ok
 protected  保护的     ok       ok     no       ok
 private     私有的      ok       no    no      ok
---------------------------------------------------------


1.5继承方式对访控属性的影响
基类访控   公有子类  保护子类 私有子类
public   public         protected  private
protected   protected   protected  private
private     private   private       private

  注意:规则优先选择保护性最强的。一般都采用公开的继承方式,其他两种继承方式很少。


继承例子代码如下:

class CPerson
{
private:
	string m_name;
	int    m_age;
public:
	CPerson(const string& name,int age):m_name(name),m_age(age)
	{
		cout<<"CPerson类==》"<<"我是 "<<m_name<<"今年 "<<m_age<<"岁了"<<endl;
	}
	void eat(const string& str)
	{
		cout<<"这是父类的eat函数"<<endl;
	}
protected:
	string getName()
	{
		return m_name;
	}
	int getAge()
	{
		return m_age;
	}	
};


class CStudent : public CPerson
{
private:
	int m_num;
public:
	CStudent(const string& name,int age,int num):CPerson(name,age),m_num(num)
	{
		cout<<"我是学生, 我的学号是: "<<m_num<<endl;
	}
	void lean(const string& strlesson)
	{
		cout<<"我在学习 "<<strlesson<<endl;
	}
	void print(void)
	{
		cout<<"学习学号: "<<m_num<<endl;
	}
	void show(void)
	{
		cout<<"我是"<<getName()<<"今年"<<getAge()<<"学习学号"<<m_num<<endl;
	}
	void eat(const string& foot)
	{
		cout<<"这是子类中的eat函数"<<endl;
	}
};


class CTeacher : public CPerson
{
private:
	string m_job;
public:
	CTeacher(const string& name,int age,const string& job):CPerson(name,age),m_job(job)
	{
		cout<<"我是教师,我的职称是: "<<m_job<<endl;
	}
	void tech(const string& lesson)
	{
		cout<<"我在教"<<lesson<<endl;
	}
};




1.6子类的构造函数和析构函数

(1)在子类的构造函数中可以显示的指定其基类的构造方式(:基类(参数)),如果没有显示的指定,那么系统就会以无参的形式去构造基类的部分。

(2)子类的析构函数会自动的调用父类的析构函数,但是基类的析构函数不会自动调用子类的析构函数。( 这个是指子类对象析构的时候哦

(3)如果父类指针指向子类对象,那么delete指针时,被调用的仅仅是父类的析构函数,子类的析构函数不会被调用,为了调用子类的析构函数,采用虚析构


class A
{
public:
	A()
	{
		cout<<"A is create"<<endl;
	}
	virtual ~A()
	{
		cout<<"A is delete"<<endl;
	}
};


class B :public A
{
public:
	B()
	{
		cout<<"B is create"<<endl;
	}
	~B()
	{
		cout<<"B is delete"<<endl;
	}
};


B* pb = new B();

delete pb;

pb = NULL;

调用输出顺序为:

A is create

<span style="font-family: Arial, Helvetica, sans-serif;">B is create</span>
<span style="font-family: Arial, Helvetica, sans-serif;">B is delete</span>
<pre name="code" class="cpp"><pre name="code" class="cpp"><span style="font-family: Arial, Helvetica, sans-serif;">A is delete</span>

 
 

可以看构造和析构的顺序严格相反



1.7子类的拷贝构造和拷贝赋值函数

 (1)如果在子类中需要自定义拷贝构造函数,那么需要在拷贝构造函数中显式的指明调用父类的拷贝构造函数,拷贝赋值也是一样。

class Teacher;  //类的前项声明

class Person
{
private:
	string m_name;
	int    m_age;
public:
	Person(const string& name,int age):m_name(name),m_age(age)
	{
		cout<<"Person类==》"<<"我是 "<<m_name<<"今年 "<<m_age<<"岁了"<<endl;
	}
	Person(const Person& p):m_name(p.m_name),m_age(p.m_age)
	{
		cout<<"Person类中的自定义拷贝构造函数被调用"<<endl;
	}
	void eat(const string& str)
	{
		cout<<"这是父类的eat函数"<<endl;
	}
protected:
	string getName()
	{
		return m_name;
	}
	int getAge()
	{
		return m_age;
	}
};


class Teacher : public Person
{
private:
	string m_job;
public:
	Teacher(const string& name,int age,const string& job):Person(name,age),m_job(job)
	{
		cout<<"我是教师,我的职称是: "<<m_job<<endl;
	}
	Teacher(const Teacher& t):Person(t),m_job(t.m_job) //<span style="color:#ff6666;">子类类型对象可以作为父类类型对象使用哦,显示调用其拷贝构造函数</span>
	{
		cout<<"Teacher类中的自定义的拷贝构造函数被调用"<<endl;
	}
	void tech(const string& lesson)
	{
		cout<<"我在教"<<lesson<<endl;
	}
};




二、多重继承(钻石继承)


如: 钻石继承

   员工类(姓名)
/ \
  领导类  销售人员类
 \   /
销售人员领导类(保证只有一个名字.)


 钻石继承注意:

(1)在对公共继承的继承方式之前加virtual关键字.(虚继承)

(2)可能需要在最终子类中显示指明公共基类的构造方式(可能缺省构造也可以实现)

(3)目标:公共基类在最后子类中只有一份,以避免由不同继承路径访问公共基类时发生数据不一致.

注意:在多继承中,如果多个父类都有函数名相同,参数表不同的函数,继承到子类中不会构成重载.

说明对钻石继承我在下面运行的结果我使用一个图示表示:

<span style="white-space:pre">	</span>DD dd(100);
 	cout<<"m_data = "<<dd.GetValue()<<endl;

	dd.SetValue(1000);

 	cout<<"m_data = "<<dd.GetValue()<<endl;
<span style="white-space:pre">	</span>没有使用虚继承时输出结果如下:100,100
<span style="white-space:pre">	</span>使用虚继承时输出结果如下:100,1000



class AA
{
public:
	int m_data;
public:
	AA(int data):m_data(data)
	{
		cout<<"AA is create"<<endl;
	}
	~AA()
	{
		cout<<"AA is delete"<<endl;
	}
};


class BB : virtual public AA
{
public:
	BB(int n):AA(n)
	{
		cout<<"BB is create"<<endl;
	}
	void SetValue(int nValue)
	{
		m_data = nValue;
	}
	void show()
	{
		cout<<"B类中show函数"<<endl;
	}
	~BB()
	{
		cout<<"BB is delete"<<endl;
	}


};


class CC : virtual public AA
{
public:
	CC(int n):AA(n)
	{
		cout<<"CC is create"<<endl;
	}
	int GetValue()
	{
		return m_data;
	}
	void show(int i)
	{
		cout<<"C类中的一个show函数"<<endl;
	}
	~CC()
	{
		cout<<"CC is delete"<<endl;
	}
};


class DD : public BB,public CC
{
public:
	DD(int n):CC(n),BB(n),AA(n)/*显示指定调用AA的构造*/
	{
		cout<<"DD is create"<<endl;
	}
	~DD()
	{
		cout<<"DD is delete"<<endl;
	}
	void show(char c,int n)
	{
		cout<<"DD 类中的show函数"<<endl;
	}
};

int main(void)
{
 	DD dd(100);
 	cout<<"m_data = "<<dd.GetValue()<<endl;

	dd.SetValue(1000);

 	cout<<"m_data = "<<dd.GetValue()<<endl;


	cout<<"-------------------------------------"<<endl;

	dd.BB::show();	//指定调用B类中的show函数.

	dd.show('c',10);

	cout<<"--------------------------------"<<endl;
 
 	Teacher t1("weikangc",25,"教授");
 	t1.eat("包子");
 	t1.tech("Java");
 
	cout<<"---------------------------------"<<endl;
 	Teacher t2(t1);
	t2.tech("C++");


 	B b;
 	A& a = b;
	
	A* pA = new B;
	delete pA;
	pA = NULL;

 	B* pB = new B;
 	delete pB;
 	pB = NULL;






	CStudent s1("weikangc",25,6888);
	s1.lean("C++");
	s1.CPerson::eat("拉面");
	s1.show();
	//s1.getName(); //保护类的成员不可以在类的外部使用.
	//s1.getAge();
 
	cout<<"-------------------------------------"<<endl;
 
	CTeacher c1("WeiKangC",28,"Java讲师");
	c1.eat("包子");
	c1.tech("Java");
 
	cout<<"--------------------------------------"<<endl;
 
	CPerson* person = new CStudent("程伟康",25,99999);
	person->eat("羊肉汤");
	//person->lean("C/C++,JAVA"); //编译报错,没有此函数,编译器只关注数据类型,发现此类型中没有该函数.
	CStudent* pS = static_cast<CStudent*>(person);
	pS->lean("C/C++");
	pS->print();
	delete person;
	person = NULL;
 
	cout<<"---------------------------------------"<<endl;
 
	CStudent* ppS = static_cast<CStudent*>(new CPerson("伟康程",26));
	ppS->lean("Win32");
	ppS->CPerson::eat("炒鸡");
	ppS->print(); //缺少一个成员比较危险.
	delete ppS;
	ppS = NULL;

	return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值