C++继承与派生

1.基本知识

类的继承是新类从已有类那里获得特性从已有的类产生新类的过程称为类的派生

已有类称为基类或父类,派生出的新类则称为派生类或子类;

继承的功能:

(1)使得基类与派生类之间建立起逻辑上的层次关系;(2)使得派生类获得其基类的属性和行为

例如:人与动物,动物就为基类,而人为派生类

分类:单继承:只有一个直接基类;多继承:有多个直接基类

比如:Base:基类;Derived:派生类

基类与派生类之间的关系:

(1)基类是对派生类的抽象;派生类是对基类的具体化,是基类的延续;

(2)派生类是基类的组合,多继承可以看作是多个单继承的简单组合;

(3)公有派生类的对象可以作为基类的对象处理

2.声明派生类

class <派生类名> : <继承方式> <基类名>

class Base
{
private:
	int a;
public:
	void inita(int x);
};

class Derived :public Base
{
private:
	int b;
public:
	void initb(int y);
};

3.成员的访问

3.1类的成员的访问说明符

private:私有,只能被类自身的成员和友元访问

public:公有,可以被任何普通函数和任何类的成员函数或子类访问

protected:保护,可以被类自身的成员和友元,派生类的成员函数访问;

3.2继承方式

public:公有继承;private:私有继承;protected:保护继承

基类中的私有成员在派生类中是隐藏的,只能在基类内部访问;

派生类从基类私有继承时,基类的公有和保护成员在派生类中都改为私有成员;

派生类从基类公有继承时,基类的公有和保护成员在派生类中仍为公有和保护成员;

派生类从基类保护继承时,基类的公有成员在派生类中改为保护成员,保护成员不变;

3.2.1私有继承

此时派生类的成员函数只能通过基类的公有或保护成员函数间接访问。——所以在设计基类时,通常都要为其私有成员提供能够访问他们的公有成员函数,以便派生类和外部函数能够间接访问;

class Base
{
private:
	int a;
public:
	void inita(int x)//这个inita就是派生类能够访问的基类的公有或保护成员函数
	{
		a = x;
	}
	int geta()
	{
		return a;
	}
};

class Derived :private Base//私有继承一般用的比较少
{
private:
	int b;
public:
	void initb(int y, int x)
	{
		b = y;
		inita(x);
	}
	int getb()
	{
		return b * geta();
	}
};

int main()
{
	Derived ob;
	ob.initb(5, 7);
	cout << ob.getb() << endl;
	return 0;
}

3.2.2公有继承

基类的成员函数可以直接访问他们,而外部函数只能通过派生类的对象间接访问;

class Base
{
private:
	int a;
public:
	void inita(int x)
	{
		a = x;
	}
	int geta()
	{
		return a;
	}
};

class Derived :public Base
{
private:
	int b;
public:
	void initb(int y)
	{
		b = y;
	}
	int getb()
	{
		return b * geta();
	}
};

int main()
{
	Derived ob;
	ob.inita(12);//直接调用
	ob.initb(5);
	cout << "the result of ob getb() is:" << ob.getb() << endl;
	return 0;
}

公有继承的注意事项:派生类以公有的方式继承自基类,并不是说派生类就可以访问基类的私有成员;继承自基类,仍然不改变基类成员的访问权限,因此基类中的私有成员依然是私有的不可访问

派生类中声明的成员名如果与基类中声明的成员名相同,则派生类中的成员起支配作用;

class Base
{
public:
	int a();
};

class Derived :public Base
{
	int a();
	int b();
};

void Derived::g()
{
	a();//此时被调用的函数是Derived::a(),而不是Base::a()
}

被调用的函数是Derived::a(),而不是Base::a()

上述结论也适用于派生类的对象的引用——Derived obj;   obj.a()此时即使基类与派生类中的函数同名,调用的也是Derived::a()

(此时,要使用基类中的同名成员,要加作用域运算符限定)——obj.Base::a()才会调用基类的

3.2.3保护继承

派生类的所有成员在类的外部都无法访问它们;

class Base
{
private:
	int a;
protected:
	int b;
public:
	int c;
	void setab(int x, int y)
	{
		a = x;
		b = y;
	}
	int geta()
	{
		return a;
	}
};

class Derived :protected Base
{
private:
	int c;
public:
	void setabc(int m, int n, int l)
	{
		setab(m, n);
		b = m;//可以访问protect继承的成员
		c = l;
	}
	int getc()
	{
		c = c + b * geta();
		return c;
	}
};

int main()
{
	Derived ob;
	//ob.setabc();//非法:不能通过类外对象访问从基类保护继承来的成员
	ob.setabc(12, 12, 5);
	cout << "the result of obgetc()is:" << ob.getc() << endl;
	return 0;
}

关键就是:不能通过类外对象访问从基类保护继承来的成员;

3.2.4特殊方法的继承——派生类的构造函数和析构函数

在C++中,基类成员的初始化工作由基类的构造函数完成,而派生类的初始化工作由派生类的构造函数完成;

(1)构建原则

基类和派生类都需要调用构造函数来实现初始化成员,这就产生了派生类构造函数和析构函数。

构建时要遵循的原则:

a.基类的构造函数和析构函数不能被派生类继承;如何实现?

b.如果基类没有定义构造函数,派生类也可以不定义构造函数,全都采用默认的构造函数

c.如果基类定义了带有形参表的构造函数,派生类就必须定义新的构造函数,提供一个将参数传递给基类构造函数的途径

d.如果派生类的基类也是派生类,则每个派生类只需负责其直接基类的构造,不负责间接基类的构造(只服从于直接领导)

e.派生类是否要定义析构函数与所属的基类无关

(2)派生类构造函数的创建

派生类的构造函数需要用合适的初值作为参数,隐含调用基类的构造函数和新增对象成员的构造函数来初始化各自的成员,再用新加的语句对新增数据成员进行初始化。派生类构造函数声明的一般形式:

<构造函数名>(参数总表):基类名(参数表),对象成员名1(参数表1).....对象成员名n(参数表n)

class Base
{
	int i;
public:
	Base(int n)
	{
		;
	}
};

class Derived :public Base
{
	int j;
	Base ob;
public:
	Derived(int m) :Base(m), ob(m)//调用基类的构造函数和新增对象成员的构造函数来初始化各自的数据成员;
	{
		;
	}
};
(3)派生类析构函数的构建

派生类析构函数功能与基类析构函数的功能一样;

析构函数不能被继承,如果需要析构函数,则需要在派生类中重新定义。并且派生类的析构函数也没有数据类型和参数;

class Base
{
	int i;
public:
	Base(int n)
	{
		cout << "constructing Base class\n";
		i = n;
	}
	~Base()
	{
		cout << "destructing Base class\n";
	}
	void showi()
	{
		cout << i << endl;
	}
};

class Derived :public Base
{
private:
	int j;
	Base ob;//基类对象作为派生类对象成员
public:
	Derived(int n) :Base(n), ob(n)//派生类构造函数
		//那其实从这个例子可以看出,在调用派生类的构造函数的时候就自动调用了基类的构造函数,以及为新建立的对象也进行了赋值
	{
		cout << "constructing Derived class" << endl;
		j = 2 * n;
	}
	~Derived()
	{
		cout << "destructing Derived class\n" << endl;
	}
	void showj()
	{
		cout << j << endl;
	}
};

int main()
{
	Derived ob(10);
	ob.showi();
	ob.showj();
	return 0;
}

可以看出派生类的析构函数的构建与普通类的析构函数的构建没有区别;

并且经过上述例子我们可以看出:通过派生类的构造函数,我们同时“调用”了基类中的构造函数,使得基类中的成员也成功初始化!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值