C++ 继承与多态

介绍

继承机制

是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员保持原有类特性的基础上进行扩展,增加功能;这样产生新的类,称派生类;继承呈现了面向对象程序设计的层次结构;体现了由简单到复杂的认识过程

多态性

是考虑在不同层次的类中,以及在统一类中,统么的成员函数之间的关系问题。函数的重载,运算符的重载,属于编译时的多态性;以虚基类为基础的运行时的多态性是面向对象程序设计的标志性特征;体现了类推和比喻的思想方法

早期绑定:编译时就确定了调用关系
晚绑定:程序运行的过程中才能确定调用关系

继承的概念与定义

层次概念是计算机的重要概念

通过继承(inheritance)的机制课对类(class)分层,提供类型/子类型的关系

C++通过类派生(class derivation)的机制来支持继承;被继承的类称为基类(base class)或超类(superclass),新产生的类为派生类(derived class)或子类(subclass);基类的派生类的集合称作类继承层次结构(hierarchy)

由基类派生出派生类的定义的一般形式为:

class 派生类名:访问限定符 基类名1,访问限定符 积累名2,…访问限定符 基类型n
{
private:
成员表1; //派生类增加或替代的私有成员
public:
成员表2; //派生类增加或替代的共有成员
protected:
成员表3; //派生类增加或替代的保护成员
}

class Object
{
public:
	int value;
	int num;
public:
	Object(int x = 0,int y = 0) :value(x), num(y)
	{
		cout << "create Object:" << this << endl;
	}
};
class Base :public Object
{
public:
	int sum;
	int fib;
public:
	Base(int a = 0, int b = 0) :sum(a), fib(b)
	{
		cout << "create Base:" << this << endl;
	}
};

int main()
{
	Base base;
}

在我们创建base对象时,首先到达派生类的构造函数,但是并不构造派生类对象,而是先构建Object对象,随后在构建派生类对象
在这里插入图片描述
也就是说base有三个成员,分别是:隐藏基对象、sum、fib
在这里插入图片描述
在这里插入图片描述
再看下面的例子

class A
{
private:
	int ax;
protected:
	int ay;
public:
	int az;
public:
	A()
	{
		ax = ay = az = 0;
	}
};
class B :public A
{
private:
	int bx;
protected:
	int by;
public:
	int bz;
public:
	B()
	{
		bx = by = bz = 1;
	}
	void fun()
	{
		ax = 10; //error
		ay = 20;
		az = 30;
	}
};

int main()
{
	B b;
	b.fun();
}

在这里插入图片描述
由于ax属于基类的私有成员,所以在b.fun()ax = 10;,是错误的,也就是非自己的私有无法访问

若我们将上面的共有继承更改为私有继承在这里插入图片描述
所以,无论任何继承关系,子类对象的方法可以访问隐藏基类中的公有属性保护属性

当我们添加私有属性的有名A类对象,,更加体现了保护属性对继承对象具名对象的差别

在这里插入图片描述
我们接着看下面,在main函数在的访问
在这里插入图片描述
如果我们将其改为私有继承关系,那么我们将无法从外部继续访问 b.az = 100

三层关系

class A
{
private:
	int ax;
protected:
	int ay;
public:
	int az;
};
class B :private A
{
private:
	int bx;
protected:
	int by;
public:
	int bz;
};
class C :public B
{
private:
	int cx;
protected:
	int cy;
public:
	int cz;
};

int main()
{
	C c;
}

在这里插入图片描述
可以看到其中的嵌套关系,C中包含B,B中包含A,无论是私有继承、公有继承或保护继承,所产生对象的内存分布是相同的

并且B私有继承A,继而B对象可以访问ay,az;而C公有继承B,C是无法去访问B对象中的私有属性的,也就是无法访问私有继承的A基类

隐藏基类同名成员

class A
{
protected:
	int ax;
public:
	A() :ax(0) {}
	void fun(int x)
	{
		cout << "A:fun" << value << endl;
	}
};
class B : public A
{
private:
	int ax;
public:
	B():ax(10){}
	void fun()
	{
		ax = 100;
	}
};
int main()
{
	B b;
	b.fun();
	// b.fun(10);  错误!!!
}

若类中的成员与隐藏基类的成员重名,也就是当我们B的对象访问ax时,会根据就近原则去访问B类的ax
在这里插入图片描述
不仅仅是成员变量,包括成员方法同样会有同名隐藏,若想要调用被隐藏的成员则需要加上作用域A::ax = 10; A::fun(10);

派生类与基类

在任何需要基类对象的地方都可以用公有派生类的对象来代替,这条规则称为赋值兼容规则,它包括以下情况:

C++面向对象编程中一条重要的规则是:公有继承意味着“是一个”,一定牢记这条规则

  • 派生类的对象可以赋值给基类的对象,这时是把派生类对象中从对应基类中继承来的隐藏对象赋值给基类对象,反过来不行,因为派生类的新成员无值可赋
  • 可以将一个派生类的对象的地址赋给其基类的指针变量,但只能通过这个指针访问派生类中由基类继承来的隐藏对象,不能访问派生类中的新成员,同样也不能反过来做
  • 派生类对象可以初始化基类的引用,引用是别名,但这个别名只能包含派生类对象中的由基类继承来的隐藏对象
class Object
{
private:
	int value;
public:
	Object(int x=0):value(x)
	{
		cout << "Create Object:" << this << endl;
	}
	~Object()
	{
		cout << "Destroy Object:" << this << endl;
	}
};
class Base :public Object
{
private:
	int num;
public:
	Base(int x) :num(x), Object(x + 10)
	{
		cout << "Create Base:" << this << endl;
	}
	~Base()
	{
		cout << "Destroy Base:" << this << endl;
	}
};
int main()
{
	Object obja(100);
	Base base(10);
	
	obja = base;
	Object* op = &base;
	Object& obj = base;
	
	return 0;
}

在这里插入图片描述
obja = base; 将派生类赋值给基类,会产生切片效应,赋值兼容性;且必须是公有继承

Object* op = &base; op仅能base空间中的隐藏Object类,op的类型会进行约束,仅能看到Object类型大小的地址;也就是说只能识别出value,而无法识别出num,引用与指针同理

接着看下面的例子

int main()
{
	Base base(10);
	return 0;
}

可以看到,首先构建基类对象,再进行构建派生类对象
在这里插入图片描述
对基类进行拷贝构造:

class Object
{
private:
	int value;
public:
	Object(int x=0):value(x)
	{
		cout << "Create Object:" << this << endl;
	}
	~Object()
	{
		cout << "Destroy Object:" << this << endl;
	}
	Object(const Object& obj) :value(obj.value)
	{
		cout << "Copy Create Object:" << this << endl;
	}
};
class Base :public Object
{
private:
	int num;
public:
	Base(int x) :num(x), Object(x + 10)
	{
		cout << "Create Base:" << this << endl;
	}
	~Base()
	{
		cout << "Destroy Base:" << this << endl;
	}
	Base(const Base& base) :num(base.num)
	{
		cout << "Copy Create Base:" << this << endl;
	}
};
int main()
{
	Base base(10);
	Base base2(base);
	return 0;
}

拷贝构造不具有继承性

我们可以看到,对于base2,其子类型是调用拷贝构造,而父类型是调用构造,若我们将父类型的构造函数修改为无默认值,则编译无法通过
在这里插入图片描述
若想要去调用父类的拷贝构造进行拷贝:

Base(const Base& base) :num(base.num),Object(base)
{
	cout << "Copy Create Base:" << this << endl;
}

通过赋值兼容性规则,将子类型给到父类型的引用,从而去调用拷贝构造
在这里插入图片描述
同样赋值语句也不具有继承性

class Object
{
private:
	int value;
public:
	Object(int x=0):value(x)
	{
		cout << "Create Object:" << this << endl;
	}
	~Object()
	{
		cout << "Destroy Object:" << this << endl;
	}
	Object(const Object& obj) :value(obj.value)
	{
		cout << "Copy Create Object:" << this << endl;
	}
	Object& operator=(const Object& obj)
	{
		if (this != &obj)
		{
			value = obj.value;
		}
		cout << "Object::operator=()" << endl;
		return *this;
	}
};
class Base :public Object
{
private:
	int num;
public:
	Base(int x) :num(x), Object(x + 10)
	{
		cout << "Create Base:" << this << endl;
	}
	~Base()
	{
		cout << "Destroy Base:" << this << endl;
	}
	Base(const Base& base) :num(base.num),Object(base)
	{
		cout << "Copy Create Base:" << this << endl;
	}
	Base& operator=(const Base& base)
	{
		if (this != &base)
		{
			num = base.num;
		}
		cout << "Base::operator=():" << endl;
		return *this;
	}
};
int main()
{
	Base basea(10);
	Base baseb(20);
	basea = baseb;
	return 0;
}

在这里插入图片描述
在这里插入图片描述

可以看到,我们仅赋值了num,而没有赋值value;这是因为无法再子类中调动夫类的赋值函数

在这里只需要下面的操作:

	Base& operator=(const Base& base)
	{
		if (this != &base)
		{
			*(Object*)this = base;
			num = base.num;
		}
		cout << "Base::operator=():" << endl;
		return *this;
	}

将this指针强转为父类型,这样就可以调用父类方法

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
设计一个图书管理系统,可以采用面向对象的思想,使用C++语言来实现,以下是一个简单的设计思路: 1. 首先,创建一个基类 Book,包含一些共同的属性和方法,例如书名、作者、出版社、价格等属性,以及借阅、归还等方法。 2. 然后,创建派生类 FictionBook 和 NonFictionBook,分别表示小说类和非小说类图书。这两个派生类继承了基类的属性和方法,并可以添加自己特有的属性和方法。 3. 接着,创建一个管理图书的类 BookManager,用于添加、删除和查询图书信息等操作。这个类中可以包含一个 Book 类型的数组或链表,用于存储所有的图书信息。 4. 最后,实现多态性,通过虚函数实现不同类型的图书的借阅和归还操作,将这些函数定义为虚函数,让不同类型的图书派生类去实现。 下面是一个简单的类图: ``` +------------------------+ | Book | +------------------------+ | -title: string | | -author: string | | -publisher: string | | -price: double | +------------------------+ | +borrow() | | +return() | +------------------------+ /\ || || +------------------------+ | FictionBook | +------------------------+ | -genre: string | | -rating: int | +------------------------+ | +borrow() | | +return() | +------------------------+ /\ || || +------------------------+ | NonFictionBook | +------------------------+ | -subject: string | | -level: string | +------------------------+ | +borrow() | | +return() | +------------------------+ /\ || || +------------------------+ | BookManager | +------------------------+ | -books: Book[] | +------------------------+ | +addBook() | | +removeBook() | | +searchBook() | +------------------------+ ``` 这样设计的好处是,可以扩展新的图书类型,同时也方便管理和查询图书信息。同时,使用多态性可以让代码更加灵活,同时也更加易于维护。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值