当前位置:   article > 正文

C++基础从0到1入门编程(六)- 类继承、类多态_c十十类如何系列化

c十十类如何系列化

系统学习C++,本章将记录类继承类多态的相关概念
方便自己日后复习,错误的地方希望积极指正
往期文章:
C++基础从0到1入门编程(一)
C++基础从0到1入门编程(二)
C++基础从0到1入门编程(三)
C++基础从0到1入门编程(四)
C++基础从0到1入门编程(五)
参考视频:
1.黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难
2.系统化学习C++

1 类继承

1.1 基本概念

继承:一个类从另一个类获取成员变量和成员函数的过程
语法:

class 派生类名: [继承方式] 基类名
{
	派生类新增加的成员
}
  • 1
  • 2
  • 3
  • 4

被继承的类称为基类或父类;继承的类称为派生类或子类
使用继承的场景
(1)新创建的类和现有的类相似,只是多出若干成员变量或成员函数,可以使用继承

#include <iostream>
using namespace std;
class CallComers
{
public:
    string name_;
    string tel_;
    CallComers() { name_ = "Big"; tel_ = "123"; }
    void sing() { cout << "I am a bird\n"; }
    void setname(const string& name) { name_ = name; }
    void settel(const string& tel) { tel_ = tel; }
};
class CGirl : public CallComers
{
public:
    int bh_;
    CGirl() { bh_ = 8; }
    void show() { cout << bh_ << ' ' << name_ << ' ' << tel_ << endl; }
};
int main()
{
    CGirl g;
    g.name_ = "Small";
    g.show(); // 8 Small 123
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

(2)当需要创建多个类时,如果拥有很多相似的成员变量或成员函数,可以将这些类提取出来,定义为基类,然后从基类继承

class Sort 
{
	int  data[30];
	void print()
};
class BubbleSort :public Sort
{
	void sort();
};
class ShellSort :public Sort
{
	void sort();
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
1.2 继承方式

类成员访问权限:public - > protected - > private
public成员在类外可以访问,private成员只能在类的成员函数中访问

不考虑继承关系,protected成员和private成员一样,类外不能访问。当存在继承关系,基类的protected成员可以在派生类中访问,而基类的private成员不能在派生类中访问

继承方式:public、protected、private。如果不写,默认为private
在这里插入图片描述
(1)基类成员在派生类中的访问权限不得高于继承方式中指定的权限。继承方式中的public、protected、private是用来指明基类成员在派生类中的最高访问权限的
(2)不管继承方式如何,基类中的private成员在派生类中始终不能使用,不能在派生类的成员函数中访问或调用
(3)如果希望基类的成员能够被派生类继承并且毫无障碍地使用,那么这些成员只能声明为public 或protected;只有那些不希望在派生类中使用的成员才声明为private
(4)如果希望基类的成员既不向外暴露(不能通过对象访问),还能在派生类中使用,那么只能声明为 protected

由于private和protected继承方式会改变基类成员在派生类中的访问权限,导致继承关系复杂,在实际开发中,一般使用public

(5)在派生类中,可以通过基类的公有成员函数间接访问基类的私有成员
(6)使用 using 关键字可以改变基类成员在派生类中的访问权限

using只能改变基类中public和protected成员的访问权限,不能改变private成员的访问权限,因为基类中的private成员在派生类中是不可见的,根本不能使用

#include <iostream>
using namespace std;
class A
{
public:
    int a_ = 10;
protected:
    int b_ = 20;
private:
    int c_ = 30;
};
class B :public A
{
public:
    using A::b_;
    // using A::c_; // 报错
private:
    using A::a_;
};
int main()
{
    B b;
    // b.a_ = 10;
    b.b_ = 21;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
1.3 继承的对象模型
  1. 创建派生类对象,先调用基类的构造函数,再调用派生类的构造函数。销毁派生类对象,先调用派生类的析构函数,再调用基类的析构函数。如果手工调用派生类的析构函数,也会调用基类的析构函数
    基类(构造函数)-> 派生类(构造函数) -> 派生类(析构函数) -> 基类(析构函数)
  2. 创建派生类对象只会申请一次内存,派生类对象包含了基类对象的内存空间,this指针是相同的
  3. 创建派生类对象,先初始化基类对象,再初始化派生类对象
  4. 对派生类对象用sizeof得到的是基类所有成员(包括私有成员)+ 派生类对象所有成员的大小
  5. 在C++中,不同继承方式的访问权限只是语法上的处理
  6. 对派生类对象用memset()会清空基类私有成员
memset(p, 0, sizeof(B));
  • 1
  1. 用指针可以访问到基类中的私有成员(内存对齐)
*((int*)p + 2) = 123;
  • 1
#include <iostream>
#include <cstring>
using namespace std;
void* operator new(size_t size)
{
    void* ptr = malloc(size);
    cout << ptr << ' ' << size << endl;
    return ptr;
}
void operator delete(void* ptr)
{
    if (ptr == 0) return;
    free(ptr);
    cout << "Free\n";
}
class A
{
public:
    int a_ = 10;
protected:
    int b_ = 20;
private:
    int c_ = 30;
public:
    A()
    {
        cout << this << endl;
        cout << &a_ << endl;
        cout << &b_ << endl;
        cout << &c_ << endl;
    }
    void func()
    {
        cout << a_ <<' ' << b_ << ' ' << c_ << endl;
    }
};
class B:public A
{
public:
    int d_ = 40;
    B()
    {
        cout << this << endl;
        cout << &a_ << endl;
        cout << &b_ << endl;
        cout << &d_ << endl;
    }
    void func1() { cout << d_ << endl; }
};
int main()
{
    cout << sizeof(A) << endl; // 12
    cout << sizeof(B) << endl; // 16
    B* p = new B;
    p->func(); p->func1();
//    memset(p, 0, sizeof(B));
//    p->func(); p->func1();
    *((int*)p + 2) = 123;
    p->func(); p->func1();
    delete p;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
1.4 构造基类

派生类构造函数的要点:
(1)创建派生类对象,程序先调用基类构造函数,然后再调用派生类构造函数
(2)没有指定基类构造函数,将使用基类的默认构造函数
(3)可以用初始化列表指明要使用的基类构造函数
(4)基类构造函数负责初始化被继承的数据成员;派生类构造函数用于初始化新增的数据成员
(5)派生类的构造函数总是调用一个基类构造函数,包括拷贝构造函数

#include <iostream>
using namespace std;
class A
{
public:
    int a_;
private:
    int b_;
public:
    A() : a_(0), b_(0)
    {
        cout << "Base class MorenGouzao\n";
    }
    A(int a, int b) : a_(a), b_(b)
    {
        cout << "Base class Gouzao\n";
    }
    A(const A &a) : a_(a.a_), b_(a.b_)
    {
        cout << "Base class KaoBeiGouzao\n";
    }
    void showA()
    {
        cout << a_ << ' ' << b_ << endl;
    }
};
class B:public A
{
public:
    int c_;
    B() : c_(0), A()
    {
        cout << "Moren GouzaoB()\n";
    }
    B(int a,int b, int c) : A(a,b), c_(c)
    {
        cout << "B(int a,int b, int c)\n";
    }
    B(const A& a, int c) : A(a), c_(c)
    {
        cout << "B(const A& a, int c)\n";
    }
    void showB()
    {
        cout << c_ << endl;
    }
};
int main()
{
    B b1;
    b1.showA(); // 0 0
    b1.showB(); // 0

    B b2(1, 2, 3);
    b2.showA(); // 1 2
    b2.showB(); // 3

    A a(10, 20);
    B b3(a, 20);
    b3.showA(); // 10 20
    b3.showB(); // 20
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
1.5 名字遮蔽与类作用域

如果派生类中的成员(包括成员变量和成员函数)和基类中的成员重名,通过派生类对象或者在派生类的成员函数中使用该成员时,将使用派生类新增的成员,而不是基类的

#include <iostream>
using namespace std;
class A
{
public:
    int a_ = 30;
    void func() { cout << "A func()\n"; }
};
class B:public A
{
public:
    int a_ = 80;
    void func() { cout << "B func()\n"; }
};
int main()
{
    B b;
    cout << b.a_ << endl; // 80
    b.func();             // B func()
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

Tip:基类的成员函数和派生类的成员函数不会构成重载,如果派生类有同名函数,那么就会遮蔽基类中的所有同名函数(因为作用域)

在成员名前面加类名和域解析符可以访问对象的成员
如果不存在继承关系,类名和域解析符可以省略不写

#include <iostream>
using namespace std;
class A
{
public:
    int a_ = 30;
    void func() { cout << "A func()\n"; }
    void func(int a) { cout << "A func(int a)\n"; }
};
class B:public A
{
public:
    int a_ = 80;
    //void func() { cout << "B func()\n"; }
};
int main()
{
    B b;
    cout << b.a_ << endl; // 80
    b.func();             // B func()
    b.func(1);            // 把派生类的func()注释掉可以运行
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

当存在继承关系时,基类的作用域嵌套在派生类的作用域中。如果成员在派生类的作用域中已经找到,就不会在基类作用域中继续查找;如果没有找到,则继续在基类作用域中查找
在成员的前面加上类名和域解析符,就可以直接使用该作用域的成员
在这里插入图片描述

#include <iostream>
using namespace std;
class A
{
public:
    int a_ = 10;
    void func() { cout << "A func()\n"; }
};

class B : public A
{
public:
    int a_ = 20;
    void func() { cout << "B func()\n"; }
};
class C : public B
{
public:
    int a_ = 30;
    void func() { cout << "C func()\n"; }
    void show()
    {
        cout << C::a_ << ' ' << B::a_ << ' ' << B::A::a_ << endl;
    }
};
int main()
{
    C c;
    cout << c.C::a_ << endl; // 30
    cout << c.B::a_ << endl; // 20
    cout << c.B::A::a_ << endl;// 10
    c.C::func(); // C func()
    c.B::func(); // B func()
    c.B::A::func();// A func()
    c.show(); // 30 20 10
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
1.6 继承的特殊关系

派生类和基类之间的特殊关系
(1)如果继承方式是公有的,派生类对象可以使用基类成员
(2)派生类对象赋值给基类对象(包括私有成员),会舍弃非基类的成员
(3)基类指针可以在不显示转换的情况下指向派生类对象
(4)基类引用可以在不显示转换的情况下指向派生类对象
Tip:

(1)基类指针或引用只能调用基类的方法,不能调用派生类的方法
(2)用派生类构造基类
(3)如果函数形参是基类,实参可以用派生类
(4)C++要求指针和引用类型与赋给的类型匹配,这一规则对继承来说是例外。但是,这种例外只是单向的,不可以将基类对象和地址赋给派生类引用和指针(没有价值)

#include <iostream>
using namespace std;
class A
{
public:
    int a_ = 0;
private:
    int b_ = 0;
public:
    void show()
    {
        cout << a_ << ' ' << b_ << endl;
    }
    void setb(int b) { b_ = b; }
};
class B:public A
{
public:
    int c_ = 0;
    void show() { cout << a_ << ' '  << c_ << endl; }
};
int main()
{
    B b;
    A* a = &b;

    b.a_ = 10;
    b.setb(20);
    b.c_ = 30;
    b.show(); // 10 30

    a->a_ = 11;
    a->setb(12);
    a->show(); // 11 12
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
1.7 类继承 - 多继承与虚继承

多继承语法:

class B : public A, public C
{
	int a_ = 10;
}
  • 1
  • 2
  • 3
  • 4

菱形继承:

在这里插入图片描述
虚继承可以解决菱形继承的二义性数据冗余问题
有了多继承,就存在菱形继承,有了菱形继承就有虚继承,变得更复杂
不提倡使用多继承,只有在比较简单和不出现二义性的情况下才使用多继承,能用单一继承解决的问题就不要用多继承
多继承:

#include <iostream>
using namespace std;
class A1 {
public:
    int a_ = 10;
};
class A2 {
public:
    int a_ = 20;
};
class B :public A1, public A2 {
public:
    int a_ = 30;
};
int main()
{
    B b;
    cout << b.a_ << endl;    // 30
    cout << b.A1::a_ << endl;// 10
    cout << b.A2::a_ << endl;// 20
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

菱形继承:

#include <iostream>
using namespace std;
class A
{
public:
    int a_ = 10;
};
class B : virtual public A {};
class C : virtual public A {};
class DD : public B, public C {};
int main()
{
    DD d;
    d.a_ = 20;
    cout << d.a_ << endl; // 20
    cout << &d.B::a_ << ' ' << &d.C::a_ << endl; // 0x47d1ffc90 0x47d1ffc90
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2 类多态

2.1 多态的基本概念

基类指针只能调用基类的成员函数,不能调用派生类的成员函数。

如果在基类的成员函数前加virtual关键字,把它声名为虚函数,基类指针就可以调用派生类中同名的成员函数,通过派生类中的成员函数,就可以访问派生对象的成员变量

有了虚函数,基类指针指向基类对象时就使用基类的成员函数和数据,指向派生类对象时就使用派生类的成员函数和数据,基类指针表现出了多种形式,这种现象称为多态
基类引用也可以使用多态
Tip:
(1)只需要在函数声名的时候加上virtual关键字,函数定义时不能加
(2)派生类中重定义虚函数,函数特征要相同
(3)基类中定义了虚函数时,如果派生类没有重定义该函数,那么将使用基类的虚函数
(4)在派生类中重定义了虚函数的情况下,如果想使用基类的虚函数,可以加类名和域解析符
(5)如果要在派生类中重新定义基类的函数,则将它设置为虚函数;否则,不要设置为虚函数
有两方面好处:(1)普通函数效率更高(2)指出不要重新定义该函数

#include <iostream>
using namespace std;
class CAllComers{
public:
    int bh_ = 0;
    virtual void show() { cout << "CAllComers::show() " << bh_ << endl; }
    virtual void show(int a) { cout << "CAllComers::show(int a) " << bh_ << endl; }
};

class CGirl : public CAllComers {
public:
    int age_ = 0;
    void show() { cout << "CGirl::show() " << bh_ << ' ' << age_ << endl; }
    void show(int a) { cout << "CGirl::show(int a) " << bh_ << ' ' << age_ << endl; }
};
int main()
{
    CAllComers a;
    a.bh_ = 3; // 创建基类对象并对成员赋值
    CGirl g;
    g.bh_ = 8; g.age_ = 23;// 创建派生类对象并对成员赋值
    CAllComers* p;// 声明基类指针
//    p = &a;
//    p->show(); //CAllComers::show() 3 // 让基类指针指向基类对象,并调用虚函数
//    p->show(5);
    p = &g;
    p->show(); // CGirl::show() 8 23 // 让基类指针指向派生类对象,并调用虚函数
    p->show(5);
    p->CAllComers::show(5);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
2.2 多态的应用场景

在这里插入图片描述

(1)基类的虚函数实现基本功能
(2)派生类重新定义虚函数,扩展功能、提升功能
(3)实现个性化功能

#include <iostream>
using namespace std;

class Hero
{
public:
    int viability;
    int attack;
    virtual void skill1() { cout << "One skill1" << endl; }
    virtual void skill2() { cout << "Two skill2" << endl; }
    virtual void uskill() { cout << "uskill" << endl; }
};

class XS : public Hero
{
public:
    void skill1() { cout << "XS skill1" << endl; }
    void skill2() { cout << "XS skill2" << endl; }
    void uskill() { cout << "XS uskill" << endl; }
};
class HX : public Hero
{
public:
    void skill1() { cout << "HX skill1" << endl; }
    void skill2() { cout << "HX skill2" << endl; }
    void uskill() { cout << "HX uskill" << endl; }
};
int main()
{
    int id = 0;
    cout << "Please input hero: " << endl;
    cin >> id;
    // 创建基类指针,让它指向派生类对象,用基类指针调用派生类的成员函数
    Hero* ptr = nullptr;
    if (id == 1)
    {
        ptr = new XS;
    }
    else if (id == 2)
    {
        ptr = new HX;
    }
    if (ptr != nullptr)
    {
        ptr->skill1();
        ptr->skill2();
        ptr->uskill();
        delete ptr;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
2.3 多态的对象类型

类的普通成员函数地址是静态的,在编译阶段指定

(1)如果基类中有虚函数,对象的内存模型中有一个虚函数表,表中存放了基类的函数名和地址
(2)如果派生类中重定义了基类的虚函数,创建派生对象时,将用派生类的函数取代虚函数表中基类的函数

C++中的多态分为两种:静态多态与动态多态
静态多态:编译时的多态,在编译时期就已经确定要执行了的函数地址了;主要有函数重载和函数模板
动态多态:动态绑定,在运行时才去确定对象类型和正确选择需要调用的函数,一般用于解决基类指针或引用派生类对象调用类中重写的方法(函数)时出现的问题

调用普通成员函数的效率比调用虚函数的效率更高,所以如果不考虑多态,不要把普通成员函数设置为虚函数

2.4 如何析构派生类

(1)构造函数不能继承,创建派生类对象,先执行基类构造函数,再执行派生类构造函数
(2)析构函数不能继承,销毁派生类对象时,先执行派生类析构函数,再执行基类析构函数
(3)派生类析构函数执行完,会自动调用基类的析构函数
(4)如果手工的调用派生类的析构函数,也会自动调用基类函数
析构派生类
1.析构派生类对象时,会自动调用基类的析构函数。与构造函数不同的是,在派生类的析构函数中不用显式地调用基类的析构函数,因为每个类只有一个析构函数,编译器知道如何选择,无需程序员干涉

2.析构函数可以手工调用,如果对象中有堆内存

delete ptr;
ptr = nullptr;
  • 1
  • 2

3.用基类指针指向派生类对象时,delete基类指针调用的时基类的析构函数,不是派生类的,如果希望调用派生类的析构函数,就要把基类的析构函数设置为虚函数

4.C++编译器对虚析构函数做了特别的处理

5.对于基类,即使它不需要析构函数,也应该提供一个空虚析构函数,不然析构派生类对象不执行

6.赋值运算符函数不能继承,派生类继承的函数的特征标与基类完全相同,但赋值运算符函数的特征标随类而异,它包含了一个类型为其所属类的形参

7.友元函数不是类成员,不能继承

#include <iostream>
using namespace std;

class AA
{
public:
    AA() { cout << "Base Gouzao\n"; }
    virtual void func() { cout << "Base func()\n"; }
    virtual ~AA() { cout << "Base Xigou\n"; }
};
class BB : public AA
{
public:
    BB() { cout << "Pai Gouzao\n"; }
    void func() { cout << "Pai func()\n"; }
    ~BB() { cout << "Pai Xigou\n"; }
};
int main()
{
    AA *a = new BB;
    a->func();
    delete a;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
2.5 纯虚函数和抽象类

纯虚函数是一种特殊的虚函数,在某些情况,基类中不能对虚函数给出有意义的实现,把它声名为纯虚函数
语法:virtual 返回值类型 函数名(参数列表) = 0
纯虚函数在基类中为派生类保留一个函数的名字,以便派生类进行重定义,如果在基类中没有保留函数名字,则无法支持多态性

含有纯虚函数的类被称为抽象类,不能实例化对象可以创建指针和引用

派生类必须重定义抽象类中的纯虚函数,否则也属于抽象类(不能实例化对象)

基类的纯虚析构函数也需要实现,为啥声名析构函数为纯虚析构函数?
有时候,想使一个类成为抽象类,刚好没有任何纯虚析构函数,在想要成为抽象类中声名一个纯虚析构函数

#include <iostream>
using namespace std;

class AA
{
public:
    AA() { cout << "Base Gouzao\n"; }
    virtual void func() = 0;// { cout << "Base func()\n"; }
    virtual ~AA() { cout << "Base Xigou\n"; }
};
class BB : public AA
{
public:
    BB() { cout << "Pai Gouzao\n"; }
    void func() { cout << "Pai func()\n"; }
    ~BB() { cout << "Pai Xigou\n"; }
};
int main()
{
    AA *a = new BB;
    a->func();
    delete a;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/917641
推荐阅读
相关标签
  

闽ICP备14008679号