如何通过示例使用 C++ 继承和抽象类
如果您正在使用任何面向对象的编程语言(例如,C++),以下是您在学习阶段应该了解的一些重要概念。
根据以下概念,在本文中,您将使用示例 C++ 代码片段和随附的解释来了解继承和抽象类的基础知识。
- 类和对象
- 构造函数和析构函数
- 数据成员和方法
- 静态变量
- 友谊
- 重载运算符
- 虚拟方法
- 封装
- 多态性
- 抽象类
- 继承
在开始之前,您应该了解 CChild 和 CParent 之间的关系。
在基础知识中,CChild 类从 CParent 继承了一些方法和数据。正如我们所知,一个类是由数据和方法组成的。这些方法和数据是该类的成员。
如果我们采用这种范式,人类思维就会变得更合乎逻辑、更自然。这种方法使我们能够拥有一个类,该类可以被实例化以创建对象。对象将具有描述它的属性和使用该数据做某事的方法,最好是合理的。
有时,人们也会看到等级,并将其作为不同的术语分开。CChild 位于比 CParent 更低的层次结构级别。
在继承链的顶端,应该有一个抽象类。您不应该尝试实例化抽象类,但您可以拥有该抽象类类型的指针。
设计好的 OOP 解决方案变得很复杂,有些人使用 UML 来应对一些艰巨的任务,以开发可维护且实际适用的好的解决方案。问题是如何向与您一起工作的人展示您的解决方案。
如果你看一个几何图形,它可能是正方形的基础。但是几何图形也可能是圆的基础。然后你可以有:三角形、五边形等。你应该扩大你以前的工作并继续开发更多的可能性。
让我们举一个正方形和几何图形的例子。在这种情况下,几何图形将是派生图形的基类。在这种情况下,square 派生自基类,即图形。
你可以在 C++ 中拥有三种类型的继承:
- 民众
- 受保护
- 私人的
公共继承是最常见的。写的方法是:
class CParenent
{
...
}
然后,您创建子项,如下所示:
class CChild : [public|protected|private] CParent
{
...
}
如下所示,可以有多个类作为父类。
class CChild: public CParent1, public CParent2
{
...
}
在这种情况下,您有两个类作为父类,它们都是创建类 CChild 的基础,这一次,它们都使用公共继承。
有可能有两个以上的父类,并且每个父类都可以像基类一样使用:公共、私有或受保护的方式。
在某些语言中,您不可能有两个父级,因为在某些情况下可能会产生歧义,从而无法确定数据属于哪个类。
在 C++ 中,您可以使用虚拟继承来解决这个问题。
此外,还有虚方法和析构函数。当您使用指向父类的指针来访问子对象时,虚拟方法和析构函数可以解决问题。
如果不使用虚方法,将无法达到预期的方法。当您使用运算符 new 创建对象时,或者您更喜欢以动态方式说时,虚拟析构函数很重要。
我们需要一个抽象类,它是从抽象类发展而来的其他类的基础。
具有没有主体的方法的类称为抽象类。您可以将类中的这种方法视为没有目的的方法,除了为通过指针间接访问的类成员创建地址空间之外,该方法属于更高层次结构中的类型。当您有 CParent 类型的指针并且它用于从 CChild 类访问方法时,这是必需的。
正确的写法如下所示:
virtual returnDataType SomeMethod(inputDataTypes)=0;
您无法实例化抽象类,并且只需要一个没有主体的方法即可成为抽象类。
问题定义
创建具有完全虚拟方法的类 CFigure 作为类 CSquare 的基础。我们将创建 CFigure 类型的方块和指针来访问 CSquare 的方法。永远不会创建对象 CFigure,但您将使用 CFigure 的指针并使用一些参数对其进行实例化。
针对问题的 C++ 代码
以下 C++ 代码是上述问题定义的答案:
#include <iostream>
using namespace std;
class CFigure
{
protected:
double dSide;
public:
CFigure(double a){dSide=a;};
virtual ~CFigure(){};
virtual double Surface(void)const=0;
virtual double Circumference(void)const=0;
virtual double Side(void)const=0;
};
class CSquare: public CFigure
{
public:
CSquare(double a):CFigure(a){};
~CSquare(){};
double Surface(void)const{ return dSide*dSide;}
double Circumference(void)const{ return 4.0*dSide;}
double Side(void)const{return dSide;}
};
int
main(void)
{
CSquare Square_1(1);
cout<<"Surface="
<<Square_1.Surface()<<endl
<<"Circumference="
<<Square_1.Circumference()<<endl;
CFigure* ptrFigure = new CSquare(2);
cout<<"Surface="
<<ptrFigure->Surface()<<endl
<<"Circumference="
<<ptrFigure->Circumference()<<endl;
delete ptrFigure;
return EXIT_SUCCESS;
}
代码分析
第一类是:CFigure。
它是抽象类的一个例子。这意味着您将无法从该对象创建对象,但您将有机会从该类创建指针,并且因为它是类 CSquare 或练习 1 中的任何其他类的基类,所以有可能将该 CFigure 类型的指针连接到从 CFigure 继承的任何对象。
这个类几乎没有虚拟方法来克服由于我们使用指针间接访问类成员而产生的问题。
dSide 是受保护的数据,这意味着它在第一次被提及的那个类旁边的所有继承类中都是可见的。
还有一个虚拟析构函数,当我们使用指针进行挖掘并使用 new 运算符创建类时,必须有这个析构函数。
在这种情况下我没有使用数组,但我也可以展示如何使用它们,以防我们需要某些对象的数组或某些其他数据结构。但是向量是组织更多对象的更合适的选择,以防您遇到需要处理更多对象的情况。
类 CSquare 是从类 CFigure 公开继承的,它将实现那些在其基类中没有主体的方法。它们并不难理解,这就是为什么我不会详细介绍它们如何工作的原因。
在主程序中,我们有第一个参数为 1 的对象 Square_1。这意味着我们正在实例化一个参数为 1 的对象。
之后我们计算:给定正方形的表面和周长。
计算出的值在监视器上呈现给程序员。
如果您需要找出 side 的值,您可以调用具有足够名称的方法。在这个程序中我没有使用这种方法,但建议读者也使用这种方法。这对你来说是一个额外的练习。
接下来,我们所做的有点不同,我们创建了基类的指针并将其与参数等于 2 的 CSquare 连接起来。
因为子类比父类有更多的数据,所以可能会丢失一些不必要的数据,但其他方式可能有点困难。您可以使用转换,但在这种情况下这不是推荐的做法。
然后我们间接访问对象的方法并将它们呈现给用户。
只剩下一件事要做。现在您可以在指针 ptrFigure 上使用 operator delete 来清除您不再需要的内存。
- 点赞
- 收藏
- 关注作者
评论(0)