抽象类和接口

什么是抽象类

  • 用来表示现实世界中的抽象概念
  • 是一种只能定义类型,而不能产生对象的类
  • 只能被子类继承,且抽象类的相关成员函数没有完整的体现,用来被子类重写.

比如图形(Shape)类, 就是一个抽象的概念,因为我们无法计算这个“图形”的面积,所以它的成员函数area()是空的。

而继承它的子类(矩形,圆形,三角形等)就可以去重写area()成员函数. 里面通过数学公式,计算出面积.

参考图形(Shape)类,代码如下:

class Shape
{
public:
double area()
    {
return ;
    }
};

既然Shape是个抽象的类,那就根本没有该类的对象,我们该如何避免他人使用Shape类创建对象呢?

答:

在C++中,通过纯虚函数来避免

  • 纯虚函数只需要声明函数名,不用实现函数内容.通过子类去实现
  • 当类中有纯虚函数时,该类就无法创建对象,因为纯虚函数里没有具体内容,所以这个类便成为了抽象类.
  • 如果子类没有实现存虚函数,则子类也会成为抽象类

纯虚函数

纯虚函数需要在声明函数名前面加上virtual,在最后面加个=0;

比如:

class Shape
{
public:
virtual double area()=; //不需要实现函数内容
};

接口

当类满足下面条件,则称为接口

  • 类中没有定义任何成员变量
  • 所有的成员函数都是公有的,并且都是纯虚函数
  • 接口是一种特殊的抽象类

举个例子

比如我们的蓝牙,可以打开,关闭,收发数据

网卡也一样,可以打开,关闭,收发数据.

类似的还有串口等等

这些类都拥有同样的行为,只是内容不同,所以它们的父类Channel只需要构造纯虚函数,所以便被称为接口,该父类代码如下:

class Channel{
public
virtual bool open()=;
virtual bool close()=;
virtual bool send(char* buf,int len)=;
virtual bool recv(char* buf,int len)=;
};

多重继承

  • 一个类可以继承于多个父类
  • 子类拥有所有父类的成员变量和函数
  • 子类对象可以当做任意父类对象使用

例如:

class Derived : public BaseA,
public BaseB,
public BaseC
{
//... ...
}

多重继承的问题1

多个不同的父类指针指向同一个多重继承的子类时,可能拥有不同地址

比如:

#include <iostream> 

using namespace std;

class BaseA
{
int ma;
public:
BaseA(int a)
{
ma = a;
}
int getA()
{
return ma;
}
}; class BaseB
{
int mb;
public:
BaseB(int b)
{
mb = b;
}
int getB()
{
return mb;
}
}; class Derived : public BaseA, public BaseB
{
int mc;
public:
Derived(int a, int b, int c) : BaseA(a), BaseB(b)
{
mc = c;
}
}; int main()
{
Derived d(, , );
BaseA* pa = &d;
BaseB* pb = &d; if((void *)pa==(void *)pb)
{
cout<<"true"<<endl;
}
else
{
cout<<"false"<<endl;
} cout << "&d= " << &d << endl;
cout << "pa= " << pa << endl;
cout << "pb= " << pb << endl;
}

运行打印:

false
&d= 0x28fefc
pa= 0x28fefc
pb= 0x28ff00

为什么,pa指针和pb指针都指向d对象,它们的地址却有所不同?

这是因为Derived d对象地址里依次存了两个不同的父类成员变量值,如下图所示:

从上图看到,其实pa和pb还是位于d对象地址里,只是指向的位置不同而已.所以在多重继承里,最好不要使用等号直接判断两个指针对象是否相等.

多重继承的问题2

多重继承可能产生冗余的成员

比如:

老师teacher类,学生student类都继承于people类

有些老师,为了工作还要考博士学位,既是老师又是学生,所以同时继承于老师teacher类,学生student类,则该类的成员便会拥有两个people类成员,从而产生冗余

在工程中,如何正确使用多重继承

  • 只继承一个父类多个接口
  • 由于接口只有存虚函数,从而避免了冗余的成员
  • 在父类中提供equal()成员函数,
  • 通过equal()成员函数来判断指针是否指向当前对象,使用dynamic_cast强制转换

例如:

#include <iostream>
using namespace std; class Base
{
protected:
int mi;
public:
Base(int i)
{
mi = i;
} int getI()
{
return mi;
} bool equal(Base* obj)
{
return (this == obj);
}
}; class Interface1
{
public:
virtual void add(int i) = ;
virtual void minus(int i) = ;
}; class Interface2
{
public:
virtual void multiply(int i) = ;
virtual void divide(int i) = ;
}; class Derived : public Base, public Interface1, public Interface2
{
public:
Derived(int i) : Base(i)
{
}
void add(int i)
{
mi += i;
}
void minus(int i)
{
mi -= i;
}
void multiply(int i)
{
mi *= i;
}
void divide(int i)
{
if( i != )
{
mi /= i;
}
}
}; int main()
{
Derived d();
Derived* p = &d;
Interface1* pInt1 = &d;
Interface2* pInt2 = &d; cout << "p->getI() = " << p->getI() << endl; //
pInt1->add();
pInt2->divide();
pInt1->minus();
pInt2->multiply(); cout << "p->getI() = " << p->getI() << endl; // cout << "pInt1 == p : " << p->equal(dynamic_cast<Base*>(pInt1)) << endl;    
cout << "pInt2 == p : " << p->equal(dynamic_cast<Base*>(pInt2)) << endl;
cout << "&d == p : " << p->equal(dynamic_cast<Base*>(&d)) << endl; return ;
}

运行打印:

p->getI() =
p->getI() =
pInt1 == p :
pInt2 == p :
&d== p :

可以发现,使用dynamic_cast转换,判断出来的地址就是相等的.

p->equal(dynamic_cast<Base*>(pInt1))为例,我们编译时,编译器就会去检查pInt1所在的地址,然后找到是d对象,通过d对象找到Base父类,从而去修正pInt1指针的地址.

24.C++- 抽象类(存虚函数)、接口、多重继承的更多相关文章

  1. C++的抽象类、虚函数、虚基类和java的抽象类和接口

    简单整理如下: C++虚函数 == java普通函数 C++纯虚函数 == java抽象函数 C++抽象类 == java抽象类 C++虚基类(全都是纯虚函数) == java接口

  2. C++中如何实现像Java中接口功能--C++抽象类(纯虚函数,虚函数)

    在Java中定义个接口,之后可以定义不同的类来实现接口,如果有个函数的参数为这个接口的话,就可以对各自的类做出不同的响应. 如: interface animal { public void info ...

  3. c++派生类中构造函数和析构函数执行顺序、判断对象类型、抽象类、虚函数

    一. 代码: 1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #include&l ...

  4. C++ 虚函数和多重继承的内存布局初探

    C++ 对象的内存布局 一切以事实说话: 代码: 1: #include <stdio.h> 2:  3: class A { 4: public: 5: int a; 6: int b; ...

  5. C++ 纯虚函数接口,标准 C 导出 DLL 函数的用法

    CMakeLists.txt project(virtual) # 创建工程 virtual add_library(virtual SHARED virtual.cpp) # 创建动态连接库 lib ...

  6. C++解析(24):抽象类和接口、多重继承

    0.目录 1.抽象类和接口 1.1 抽象类 1.2 纯虚函数 1.3 接口 2.被遗弃的多重继承 2.1 C++中的多重继承 2.2 多重继承的问题一 2.3 多重继承的问题二 2.4 多重继承的问题 ...

  7. 虚函数的使用 以及虚函数与重载的关系, 空虚函数的作用,纯虚函数->抽象类,基类虚析构函数使释放对象更彻底

    为了访问公有派生类的特定成员,可以通过讲基类指针显示转换为派生类指针. 也可以将基类的非静态成员函数定义为虚函数(在函数前加上virtual) #include<iostream> usi ...

  8. C#虚函数和接口的区别

    接口只能声明不能实现,虚函数可以. 接口:对外提供可以访问的函数叫接口.虚函数不需要被强制重写,其本身含有实现部分. 抽象类:指派了派生类必须实现的函数(纯虚函数),不然编译不通过. 虚函数的限制:  ...

  9. C++:纯虚函数与抽象类

    5.4.3 纯虚函数和抽象类 纯虚函数是一个在基类中说明的虚函数,它在该基类中没有定义,但是要求在派生类中根据需要对它进行定义,或仍然说明为纯虚函数. 声明纯虚函数的一般格式是: virtual 函数 ...

随机推荐

  1. MacOS中升级openssl

    MacOS中升级openssl   ➜  ~ brew instal openssl 使用情况中始终发现,openssl并没有真正升级   在/usr/local/Cellar/openssl/目录中 ...

  2. git团队协作

    hi,team,我们目前使用的是git做项目管理,它是非常优秀的版本控制工具,使用好可以极大提高我们团队开发效率.但是,出现不必要的冲突和代码丢失就要费时解决这些可避免的问题. git开发流程 这个流 ...

  3. npm打包前端项目太慢问题分析以及暂时解决方案

    npm build 打包前端项目实际上是执行 node build/build.js,但是随着项目的依赖包越来越多,项目打包时间不断延长,为了改善这个问题,需要从node入手 暂时解决方案:扩大nod ...

  4. 防反编译的加壳工具-Virbox Protector

    通过Virbox Protector可快速对您的软件进行加壳,可防调试,防挂钩,防反编译. 首先,你要有一个云平台(www.sense.com.cn)的帐号,登录后,只需将你的dll或者exe拖入到加 ...

  5. Jquery入门(初学者易懂)

    一.定义 jQuery是一个快速.简洁的JavaScript框架,是继Prototype之后又一个优秀的JavaScript代码库(或JavaScript框架).jQuery设计的宗旨是"w ...

  6. Problems at works

    1.ssh无法连接服务器:因部署ftp服务,误将/var目录的所有者和所属组改成了ftp,以致于SSH无法二次连接: 2.集群服务器的hadoop的datanode节点死亡,在对应节点拉起即可.若无法 ...

  7. 笔记:Hibernate 持久化类标注说明

    持久化类标注 标注 @Entity:注解声明该类是一个Hibernate的持久化类 标注 @Table:指定该类映射的表 参数 name:指定映射数据库表的名称 参数 uniqueConstraint ...

  8. 总结的Javascript插件

    1.很好用的弹窗 https://limonte.github.io/sweetalert2/ https://github.com/limonte/sweetalert2 import './unt ...

  9. python基础学习二 数据结构之list及相关基本操作

    list是py内置的一种数据类型,list就是列表的意思,list就是一种有序的数据集合,可以随时增加和删除list的元素. 生活中,比如我们要列出全班同学的名字,就可以用list来表示 >&g ...

  10. c++ --> sizeof()使用小结

    sizeof()使用小结 特性0:sizeof是运算符,不是函数 sizeof最基本特性,后面的很多特性都是受到这个特性的影响,正因为sizeof不是函数,因此不把它所要求得长度的对象叫做参数,习惯上 ...