C++之继承及访问权限

目录

论述继承

protected权限

访问控制

权限继承

覆写

other

多重继承

再论构造函数


论述继承

这里直接举例来理解,其中Son继承Father的内容,对于Son我们称为派生类,也俗称子类,对于Father我们称为基类,也俗称父类,可以看到Son是以public的权限继承基类的,如果不写public的话,默认是以private的方式去继承基类

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

using namespace std;

class Father {
private:
	int money;

protected:
	int room_key;

public:
	void it_skill(void)
	{
		cout<<"father's it skill"<<endl;
	}

	int getMoney(void)
	{
		return money;
	}

	void setMoney(int money)
	{
		this->money = money;
	}
};

class Son : public Father {
private:
	int toy;
public:
	void play_game(void)
	{
		int m;
		cout<<"son paly game"<<endl;
		m = getMoney();
		m--;
		setMoney(m);
        room_key = 1;
	}
};

int main(int argc, char **argv)
{
	Son s;

	s.setMoney(10);
	cout << s.getMoney()<<endl;

	s.it_skill();
	s.play_game();
	
	return 0;
}

上述代码中,子类不能直接去访问父类的private成员,也是需要通过父类提供的public接口来访问,执行结果如下

10
father's it skill
son paly game

protected权限

在上述代码中,没有去提及room_key这个变量,因为这个变量是基类的protected成员,对于子类也就是派生类,可以访问父类的protected成员,而其他代码不行,例如在main函数中加入"s.room_key = 1;"就会出错,不能直接访问父类的protected成员,在个人的理解,子类继承父类的protected成员与子类的private成员作用差不多

int main(int argc, char **argv)
{
...
	s.room_key = 1; //会出错,不能直接访问父类的protected成员
	return 0;
}

访问控制

修改一下子类Son程序如下,对于父类,在子类中可以见到的成员(protected和public成员),子类可以通过using来修改它的权限,可以提升或者降低权限,因此下面注释的语句会导致出错,因为money是父类的私有成员,在子类中不可见,所以不能修改,由于room_key被这只为了private成员,因此在上述main函数中"s.room_key = 1;"就会出错,当然可以把room_key设置为public成员

class Son : public Father {
private:
	int toy;
	using Father::it_skill;
	using Father::room_key;
public:
	//using Father::money;
	
	void play_game(void)
	{
		int m;	
		cout<<"son paly game"<<endl;
    	m = getMoney();
		m--;
		setMoney(m);
		room_key = 1; 
	}
};

权限继承

对于Son类是以public的方法来继承Father,如果不写就是以private的方式去继承,这样我们还可以以protected或者private的形式去继承基类,下面列出一个表格,基类成员在派生类中的访问控制属性,举例父类中有public、protected和private成员,子类以protected权限去继承父类,其属性分别变为protected、protected和private成员

对上述代码中Son类分别以三种不同的权限去继承Father,其中"s_pro.it_skill();"和"s_pri.it_skill();"会出错,对于s_pro来说it_skill函数为protected成员,而对于s_pri来说it_skill函数为private成员

class Son_pub : public Father {
private:
	int toy;
public:
	
	void play_game(void)
	{
		int m;
		cout<<"son play game"<<endl;
		m = getMoney();
		m--;
		setMoney(m);
		room_key = 1; 
	}
};


class Son_pro : protected Father {
private:
	int toy;
public:
	
	void play_game(void)
	{
		int m;		
		cout<<"son play game"<<endl;
		m = getMoney();
		m--;
		setMoney(m);

		room_key = 1; 
	}
};


class Son_pri : private Father {
private:
	int toy;
public:
	
	void play_game(void)
	{
		int m;		
		cout<<"son play game"<<endl;
		m = getMoney();
		m--;
		setMoney(m);

		room_key = 1; 
	}
};


int main(int argc, char **argv)
{
	Son_pub s_pub;
	Son_pro s_pro;
	Son_pri s_pri;

	s_pub.play_game();
	s_pro.play_game();
	s_pri.play_game();


	s_pub.it_skill();
	//s_pro.it_skill();  // error   
	//s_pri.it_skill();	 // error

	return 0;
}

当然派生类也可以有派生类,对于上述代码进行延伸,在Father类中加入一个public成员,派生类中权限的转变看如下注释

class Father { 
...
public:
	int address;
...
};

...

class Grandson_pub : public Son_pub {

public:
	void test(void) {
		room_key = 1; /* address is protected */ 
		address  = 2; /* address is public */ 
	}
};

class Grandson_pro : public Son_pro {

public:
	void test(void) {
		room_key = 1; /* address is protected */ 
		address  = 2; /* address is protected */ 
	}
};

class Grandson_pri : public Son_pri {

public:
	void test(void) {
		//room_key = 1;  // error
		//address  = 2;  // error
	}
};

int main(int argc, char **argv)
{
	Grandson_pub gs_pub;
	Grandson_pro gs_pro;

	gs_pub.address = 2;
	//gs_pro.address = 2;  // error

	return 0;
}

覆写

对于父类有自己技能it_skill函数,而子类可以覆写it_skill函数有自己专有的技能

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

using namespace std;

class Father {
private:
	int money;

protected:
	int room_key;
	
public:
	void it_skill(void)
	{
		cout<<"father's it skill"<<endl;
	}

	int getMoney(void)
	{
		return money;
	}

	void setMoney(int money)
	{
		this->money = money;
	}
};

class Son : public Father {
private:
	int toy;
	//using Father::it_skill;
public:
	using Father::room_key;
	//using Father::money;
	
	void play_game(void)
	{
		int m;
		
		cout<<"son paly game"<<endl;
    	m = getMoney();
		m--;
		setMoney(m);

		room_key = 1; 
	}

	/* 覆写 override */
	void it_skill(void)
	{
		cout<<"son's it skill"<<endl;
	}
	
};


int main(int argc, char **argv)
{
	Son s;
	s.setMoney(10);
	cout << s.getMoney()<<endl;
	s.it_skill();
	s.play_game();

	s.room_key = 1;	
	return 0;
}

执行结果如下

10
son's it skill
son paly game

other

如下有一个test_func函数,其中形参是基类Person的引用,对于派生类中有同名的printInfo函数,想调用基类的printInfo,可以通过"Person::printInfo"来调用,现测试基类和派生类分别调用test_func函数


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

using namespace std;

class Person {
private:
	char *name;
	int age;

public:
	int address;
	
	Person() {//cout <<"Pserson()"<<endl;
		name = NULL;
	}

	Person(char *name, int age) 
	{
		cout <<"Pserson(char*, int), name = "<<name<<", age= "<<age<<endl;
		this->age = age;

		this->name = new char[strlen(name) + 1];
		strcpy(this->name, name);
	}

	~Person()
	{
		cout << "~Person()"<<endl;
		if (this->name) {
			cout << "name = "<<name<<endl;
			delete this->name;
		}
	}

	void setName(char *name)
	{
		if (this->name) {
			delete this->name;
		}
		this->name = new char[strlen(name) + 1];
		strcpy(this->name, name);
	}
	int setAge(int a)
	{
		if (a < 0 || a > 150)
		{
			age = 0;
			return -1;
		}
		age = a;
		return 0;
	}
	void printInfo(void)
	{
		cout<<"name = "<<name<<", age = "<<age<<endl;
	}
};


class Student : public Person {
private:
	int grade;
	void setGrade(int grade) {this->grade = grade;}
	int getGrade(void) {return grade;}
public:
	void printInfo(void)
	{
		cout<<"Student ";
		Person::printInfo();
	}
};

void test_func(Person &p)
{
	p.printInfo();
}

int main(int argc, char **argv)
{
	Person p("lisi", 16);

	Student s;
	s.setName("zhangsan");
	s.setAge(16);

	test_func(p);
	test_func(s); /* Person &p = s里面的Person部分;     用的是p部分的打印函数
	                * p引用的是"s里面的Person部分"
	                */
	s.printInfo();

	return 0;
}

执行结果如下,对于"test_func(p);"执行的是基类的打印函数,这没有问题,对于派生类s调用的是其里面的Person部分

Pserson(char*, int), name = lisi, age= 16
name = lisi, age = 16                            //test_func(p);
name = zhangsan, age = 16                 //test_func(s);
Student name = zhangsan, age = 16    //s.printInfo();
~Person()
name = zhangsan
~Person()
name = lisi

内存分步图如下,对于Student,其中name、age和address继承与Person,name不可直接访问,address可访问 

 

多重继承

先看一段程序,对于两个家居都抽象为一个类,派生类都继承它们,可以看到在基类中有相同的函数,在子类使用的时候就产生了二义性,当然我们可以用"s.safa::weight();"来调用程序,不过这样很变扭

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

using namespace std;

class Sofa {
private:
	int weight;
public:
	void watchTV(void) { cout<<"watch TV"<<endl; }

	void setWeight(int weight) { this->weight = weight; }
	int getWeight(void) const { return weight; }
};

class Bed {
	private:
		int weight;
public:
	void sleep(void) { cout<<"sleep"<<endl; }
	void setWeight(int weight) { this->weight = weight; }
	int getWeight(void) const { return weight; }
};

class Sofabed : public Sofa, public Bed {  //继承两个类,如果不写public的话 默认是私有继承
};

int main(int argc, char **argv)
{
	Sofabed s;
	s.watchTV();
	s.sleep();

	//s.setWeight(100); /* error, 有二义性 */
	s.Sofa::setWeight(100);
	
	return 0;
}

解决办法,引入虚拟继承,修改程序,将共性的地方抽象为一个类,然后派生类虚拟继承

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

using namespace std;

class Furniture {
private:
	int weight;
public:
	void setWeight(int weight) { this->weight = weight; }
	int getWeight(void) const { return weight; }
};

class Sofa : virtual public Furniture {  
private:
	int a;
public:
	void watchTV(void) { cout<<"watch TV"<<endl; }
};

class Bed : virtual public Furniture {
private:
	int b;
public:
	void sleep(void) { cout<<"sleep"<<endl; }
};

class Sofabed : public Sofa, public Bed {
private:
	int c;
};

int main(int argc, char **argv)
{
	Sofabed s;   
	s.watchTV();
	s.sleep();

	s.setWeight(100);
	
	return 0;
}

执行结果如下

watch TV
sleep

s内存分部如下,对于Sofa和Bed都是虚拟继承Furniture,因此其weight共用一块内存,所以在s中只有一个weight

 

再论构造函数

在这之前可以看一下之前的文章:入门之构造函数

一个类可能有一个或者多个基类,这些基类还可能是虚拟基类,并且自己的类本身也还有对象成员,现来论述这些对象按照什么顺序来调用构造函数,首先理论构造顺序:

  1. 虚拟基类构造函数:按继续顺序,只执行一次
  2. 非虚拟基类构造函数:按继承顺序
  3. 类的对象成员(按声明的顺序)
  4. 类自己的构造函数

直接贴上例子

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

using namespace std;

class Furniture {
private:
	int weight;
public:
	void setWeight(int weight) { this->weight = weight; }
	int getWeight(void) const { return weight; }
public:
	Furniture() { cout <<"Furniture()"<<endl; }
};

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

class Sofa : virtual public Furniture , virtual public Vertification3C{
private:
	int a;
public:
	void watchTV(void) { cout<<"watch TV"<<endl; }
public:
	Sofa() { cout <<"Sofa()"<<endl; }
};

class Bed : virtual public Furniture, virtual public Vertification3C {
private:
	int b;
public:
	void sleep(void) { cout<<"sleep"<<endl; }
public:
	Bed() { cout <<"Bed()"<<endl; }
};

class Sofabed : public Sofa, public Bed {
private:
	int c;
public:
	Sofabed() { cout <<"Sofabed()"<<endl; }
};

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

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

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


class LeftRightSofabed : public Sofabed, public LeftRightCom {
private:
	Date date;
	Type type;

public:
	LeftRightSofabed() { cout <<"LeftRightSofabed()"<<endl; }
	
};

int main(int argc, char **argv)
{
	LeftRightSofabed s;
	return 0;
}

执行结果如下

Furniture()
Vertification3C()
Sofa()
Bed()
Sofabed()
LeftRightCom()
Date()
Type()
LeftRightSofabed()

如图一步步解析,对于虚拟基类,构造函数只执行一次

上述虚拟基类恰好在最上方,先将"LeftRightCom()"设置为虚拟基类,

class LeftRightSofabed : public Sofabed, virtual public LeftRightCom { //设置为虚拟基类
private:
	Date date;
	Type type;

public:
	LeftRightSofabed() { cout <<"LeftRightSofabed()"<<endl; }
	
};

执行如果如下,虚拟基类构造函数先运行 

Furniture()
Vertification3C()
LeftRightCom()
Sofa()
Bed()
Sofabed()
Date()
Type()
LeftRightSofabed()

 修改程序在构造的过程中同时赋值,对与基类赋值可以直接写类名,对于类中对象成员如date,则以成员名字来赋值

class Sofabed : public Sofa, public Bed {
private:
	int c;
public:
	Sofabed() { cout <<"Sofabed()"<<endl; }
	Sofabed(char *abc) { cout <<"Sofabed(char *abc)"<<endl; }
};

class LeftRightCom {
public:
	LeftRightCom() { cout <<"LeftRightCom()"<<endl; }
	LeftRightCom(char *abc) { cout <<"LeftRightCom(char *abc)"<<endl; }
};

class Date {
public:
	Date() { cout <<"Date()"<<endl; }
	Date(char *abc) { cout <<"Date(char *abc)"<<endl; }
};

class Type {
public:
	Type() { cout <<"Type()"<<endl; }
	Type(char *abc) { cout <<"Type(char *abc)"<<endl; }
};


class LeftRightSofabed : public Sofabed, virtual public LeftRightCom {
private:
	Date date;
	Type type;

public:
	LeftRightSofabed() { cout <<"LeftRightSofabed()"<<endl; }
	LeftRightSofabed(char *str1, char *str2, char *str3) : Sofabed(str1), LeftRightCom(str2), date(str3) { cout <<"LeftRightSofabed()"<<endl; }
																							
};

执行结果如下,因此此初始化顺序无关,跟定义的初始化有关,不会影响构造函数的顺序

Furniture()
Vertification3C()
LeftRightCom(char *abc)
Sofa()
Bed()
Sofabed(char *abc)
Date(char *abc)
Type()
LeftRightSofabed()

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值