美文网首页
C++ 继承 & 虚拟继承

C++ 继承 & 虚拟继承

作者: 我阿郑 | 来源:发表于2021-12-23 18:19 被阅读0次

一、类的继承

class Tutorial
{
};
class VideoTutorial : public Tutorial
{
};

语法:class B : public A {}
表示类B继承于类A,把A称为父类(基类),把B称为子类(派生类)

当B继承于A时,则自动地将父类中的所有public成员继承

class Tutorial
{
public:
    char name[32];
    char author[16];
public:
    void ShowInfo();
};

则在VideoTutorial类中也具有了这些成员,而不必显式写出
子类只需要把自己的独有的那部分特性写出来

// 例如
class VideoTutorial : public Tutorial
{
public:
    void Play(); // 播放
public:
    char url[128]; // 在线观看的URL地址 
    int visits; // 播放量
};


// 使用
VideoTutorial  cpp_guide;
strcpy(cpp_guide.name, "C++学习指南");
strcpy(cpp_guide.author, "牛逼Class");
strcpy(cpp_guide.url, "http://xxx");
cpp_guide.ShowInfo();

二、访问修饰符 protected

在描述继承关系时,新增一种访问修饰符 protected(受保护的)

当一个类成员被修饰为protected的时候,有以下规则成立:

  • 该成员不能被外部访问,同private
  • 该成员可以被子类继承,同public

所以,public和protected的成员都能够被子类继承

class Tutorial
{
protected:
    char name[32];
    char author[16];
public:
    void ShowInfo();
};

三、虚拟继承

子类可以重写从父类继承而来的函数 (overwriting)

class Parent
{
public:
    void Test();
};
class Child : public Parent
{
public:
    void Test(); // 重写父类方法
};

如果重写的时候,还是要嵌入调用一下父类的函数,怎么办?

void Child::Test()
{
    Parent::Test(); // 显式地调用父类的函数
}

父类指针指向子类对象

可以将父类指针指向一个子类的对象

class Parent
{
public:
    int a;
public:
    void Test();
};

class Child : public Parent
{
public:
    int b;
public:
    void Test(); // 重写父类方法
};

int main()
{
    Child ch;
    ch.a = 0x11111111;
    ch.b = 0x22222222;

    Parent* p = &ch; // p指向的对象是Child*
    printf("Parent::a = %d \n", p->a);
    
   // 此时调用的Test()是父类的、还是子类的 ??
   p->Test();
    return 0;
}
  • 指针p的类型是Parent*
  • 指针p指向的对象是Child*

virtual 关键字

当一个成员函数需要子类重写,那么在父类应该将其声明为virtual

class Parent
{
public:
// virtual本身表明该函数即将被子类重写
    virtual void Test(); 
};

Parent* obj = new Child(); 
obj->Test();

(1)只需要在父类中将函数声明为virtual,子类自动地就是virtual了。
(2)即将被重写的函数添加virtual,是一条应该遵守的编码习惯。
(3)构造函数不能加 virtual

四、继承关系中的构造与析构

有Child类继承于 Parent类

class Child : public Parent {}
  • 子类对象构造时,先调用父类的构造函数,再调用子类的构造函数。
  • 子类对象析构时,先调用子类的析构函数,再调用父类的构造函数

当一个类被继承时,应该将父类的析构函数声明为virtual,否则会有潜在的问题

class Parent
{
    virtual ~Parent(){}  // 声明为virtual
};

Parent* p = new Child();
delete p;  // 此时,调用的是谁的析构函数?

如果析构函数没有标识为virtual,则有潜在的隐患,并有可能直接导致程序崩溃。(资源没有被释放,并引申一系列问题)

类的大小,与 virtual关键字的影响

(1) 类的大小由成员变量决定。(这struct的原理相同)

类的大小成员函数的个数无关,即使一个类有10000个成员函数,对它所占的内存空间是没有影响的。

(2) 但是,如果有一个成员函数被声明为virtual,那类的大小会有些微的变化。(这个变化由编译器决定,一般是增加了4个字节)

五、纯虚函数和纯虚类

什么是纯虚函数

纯虚函数的语法:

  • 将成员函数声明为virtual
  • 后面加上 = 0
  • 该函数没有函数体
class CmdHandler
{
public:
   virtual void OnCommand(char* cmdline) = 0;
};

含有纯虚函数的类,称为抽象类(Abstract Class)(或称纯虚类)
CmdHandler中有一个纯虚函数OnCommand(),因此,它是纯虚类
抽象类不能够被实例化,即无法创建该对象
问题:不能被实例化,还定义这个类做什么用?

CmdHandler ch; // 编译错误!!
CmdHandler* p = new CmdHandler(); // 编译错误!

抽象类的实际作用

抽象类/纯虚函数的实际用途:充当的“接口规范” ,相当于Java中的interface语法
接口规范:凡是遵循此规范的类,都必须实现指定的函数接口。通常是一系列接口

凡是遵循CmdHandler规范的类,都必须实现指定的函数接口:OnCommand()

相关文章

网友评论

      本文标题:C++ 继承 & 虚拟继承

      本文链接:https://www.haomeiwen.com/subject/oinhqrtx.html