类的public继承(is-a关系)及代码编写规范
子类继承父类的方式有三种:public, protected, private
其中public继承表示的是is-a(is a kind of)关系。表示:通过子类产生的对象也一定是一个父类对象。能在父类上表现的行为,也必然能在子类上表现。父类出现的地方,必然能用子类来替换(里氏替换原则(LSP))。
父类表示一种更泛化的概念,子类表示一种更特化的概念
子类遮蔽父类的普通成员函数
如果父类和子类中有相同函数签名的普通成员函数,则子类中的函数会遮蔽掉父类中的函数。如下:
namespace demo5 {
class Human {
public:
virtual ~Human() {};
void eat() {
std::cout << "人类吃" << std::endl;
}
};
class Man :public Human {
public:
void eat() {
std::cout << "男人吃" << std::endl;
}
};
void mainFunc() {
Man man;
man.eat(); //父类的eat被子类遮蔽,调用的的是子类的eat
man.Human::eat(); //强制调用父类被遮蔽的函数
}
}
int main()
{
demo5::mainFunc();
}
运行结果如下:
注:对于public继承,不建议也不应该让子类遮蔽父类的普通成员函数。既然在父类中是普通成员函数,则代表在子类中不会有不同行为。
父类的纯虚函数接口
纯虚函数的表现如下:
- 纯虚函数没有函数体,没有行为,需要子类定义具体行为。
- 纯虚函数就是interface,需要子类必须实现。
- 纯虚函数所在的类就是抽象类,不能生成该类的对象。
namespace demo5 {
class Human {
public:
virtual ~Human() {};
public:
virtual void work() = 0;
void eat() {
std::cout << "人类吃" << std::endl;
}
};
class Man : public Human {
public:
virtual void work() {
std::cout << "重体力活" << std::endl;
}
};
class Woman : public Human {
public:
virtual void work() {
std::cout << "轻体力活" << std::endl;
}
};
void mainFunc2() {
Woman woman;
woman.work();
}
}
int main()
{
demo5::mainFunc2();
}
父类的虚函数接口
虚函数接口,让子类继承父类的成员函数的接口及实现,表现如下:
- 子类可以提供自己的新实现。
- 子类不提供自己的实现,则会使用父类的实现
- 可以同时使用子类和父类的实现。
namespace demo6 {
class Human {
public:
virtual ~Human() {};
public:
virtual void avlf() {
std::cout << "人类75岁" << std::endl;
}
};
class Man : public Human {
public:
virtual void avlf() {
std::cout << "男人70岁" << std::endl;
}
};
class Woman : public Human {
public:
virtual void avlf() {
//调用父类
Human::avlf();
//子类自身的实现
std::cout << "女人80岁" << std::endl;
}
};
void mainFunc() {
Woman woman;
Man man;
woman.avlf();
man.avlf();
}
}
int main()
{
demo6::mainFunc();
}
为纯虚函数制定实现体
有这样一种需求:
- 强制让子类实现该函数。
- 需要一些公共行为放在父类的该函数中。
也就是结合纯虚函数和虚函数的功能。是不是有些骚?其实是可以实现。纯虚函数也可以有自己的函数体:
namespace demo7 {
class Human {
public:
virtual ~Human() {};
public:
virtual void work() = 0;
};
void Human::work()
{
std::cout << "人类干活" << std::endl;
}
class Man : public Human {
public:
virtual void work()
{
Human::work();
std::cout << "男人:重体力活" << std::endl;
}
};
class Woman : public Human {
public:
virtual void work()
{
Human::work();
std::cout << "女人:轻体力活" << std::endl;
}
};
void mainFunc() {
Woman woman;
Man man;
woman.work();
man.work();
}
}
int main()
{
demo7::mainFunc();
}
运行结果如下:
类的public继承综合范例
--省略了
public继承关系下的代码编写规范
综上:
- 父类的普通成员函数,子类不应该去覆盖。如果需要覆盖,则需要在父类中将该普通成员函数修改为虚函数。
- 父类的纯虚函数,实际就是一个接口,子类必须要去实现该接口。且包含纯虚函数的父类为抽象类,不能实例化。
- 父类的普通虚函数(非纯虚函数),不但定义一个接口,而且还允许有自己的实现。子类可以继承该实现,也可以覆盖该实现,也会混用该实现
- 非必要使用public继承时,就不要使用public继承,必须要满足is-a关系。
类的private继承
private继承,不是is-a关系,是一种组合关系。确切的说是组合关系中的is-implemented-in-terms-of关系(根据...实现出...)。
一般优先考虑使用普通的组合关系,只有一些特殊情况和必要情况下,比如一些protected成员,private成员,虚函数等时才考虑使用private继承,来表示这种组合关系。
网友评论