接口继承与实现继承不同。在公有继承下,派生类总是继承基类接口
什么意思呢,请看下面的例子!
Shape类中包含纯虚函数、虚函数和非虚函数,这些函数由不同的类进行公有继承
// class sample 1class Shape { // 抽象类public: virtual void draw() const = 0; // 纯虚函数 virtual void error(const std::string& msg) { // 虚函数 std::cout << "Shape::error()" << std::endl; } int objectID() const { // 非虚函数 std::cout << "Shape::objectID()" << std::endl; return 0; }};void Shape::draw() const{ std::cout << "Shape::draw()" << std::endl;}class Rectangle: public Shape { virtual void draw() const { // 继承抽象类,须实现 std::cout << "Rectangle::draw()" << std::endl; } virtual void error(const std::string& msg) { // 实现自己的error接口 std::cout << "Rectangle::error()" << std::endl; }};class Ellipse: public Shape { virtual void draw() const { // 继承抽象类,须实现 std::cout << "Ellipse::draw()" << std::endl; }};对于基类的纯虚函数来说有如下两个特性:
由此可以看出声明一个纯虚拟函数的目的是使 派生类仅继承一个函数的接口
使用时需注意:
如下:
// main sample 1int main(){ // Shape *s1 = new Shape; // 错误,抽象类不能有实例 Shape *s2 = new Rectangle; // 正确 s2->draw(); // 正确,调用Rectangle::draw() Shape *s3 = new Ellipse; // 正确 s3->draw(); // 正确,Ellipse::draw() s2->Shape::draw(); // 正确,调用Shape::draw() s2->Shape::draw(); // 正确, 调用Shape::draw() delete s1; delete s2; return 0;}运行结果如下:
Rectangle::draw()Ellipse::draw()Shape::draw()Shape::draw()看上去派生类可以调用基类的纯虚函数没有什么用处,但是可以为虚函数(非纯虚函数)提供安全使用的机制,如下:
// main sample 2int main(){ Shape *s2 = new Rectangle; // 正确 s2->error("s2"); // 正确,调用Rectangle::error() Shape *s3 = new Ellipse; // 正确 s3->error("s2"); // 正确,Shape::error() delete s1; delete s2; return 0;}输出结果:
Rectangle::error()Shape::error()咋一看这里没有什么问题,但实际上这里隐藏了一个巨大的风险,如果这里Shape类的draw函数是虚函数(非纯虚)且实现为二维平面图形的画法,如果有一个3维的正方体公有继承Shape,单独实现draw自然是没有问题,但是如果忘了实现而直接调用呢,则可以正常运行,但是画出来的结果可能千差万别,这便是由于缺省实现导致的
解决办法有多种,常用安全的解决办法如下:
// class sample 2class Shape { // 抽象类public: virtual void draw() const = 0; // 纯虚函数};void Shape::draw() const{ std::cout << "Shape::draw()" << std::endl;}class Rectangle: public Shape { virtual void draw() const { Shape::draw(); // 复用父类实现 }};class Ellipse: public Shape { virtual void draw() const { Shape::draw(); // 复用父类实现 }};class Cubic: public Shape { virtual void draw() const { // 自身实现,缺省则会报错 std::cout << "Cubic::draw()" << std::endl; }};对于非虚函数来说,声明一个非虚拟函数的目的是使派生类既继承一个函数的接口,又继承一个强制的实现。
// main sample 3 use class sample 1int main(){ Shape *s2 = new Rectangle; // 正确 s2->objectID(); // 正确,调用Shape::objectID() Shape *s3 = new Ellipse; // 正确 s3->objectID(); // 正确,Shape::objectID() delete s1; delete s2; return 0;}输出结果:
Shape::objectID()Shape::objectID()对于派生类实现基类同名函数时,则在派生类中所有该基类的同名函数均不可见,所以派生类绝不应该重定义基类的非虚函数。
备注:
欢迎大家多多指教,喜欢的大家可以点击关注本账号,后续将不定期更新C++相关的内容
| 留言与评论(共有 0 条评论) “” |