28 虚函数and纯虚函数

本文详细介绍了C++中的虚函数和纯虚函数概念。虚函数允许子类重写基类方法,实现多态。纯虚函数则在基类中声明但不实现,强制子类必须实现该函数,常用于接口定义。通过示例代码展示了虚函数和纯虚函数的用法及其在多态中的作用。
摘要由CSDN通过智能技术生成

这部分细看,忘差不多了

28 虚函数

定义

以下内容为虚函数引入,虚函数实现了子类可以干自己的事情,而不仅仅只是完成基类

代码说明

#include <iostream>
#include <string>
//定义基类,有一个公用方法getName;
class Entity  
{
public:
    std::string getNmae()
    {
        return "Entity";
    }
};
//定义子类
class Player:public Entity  
{
private:
    std::string m_Name;
public:
    Player(const std::string& name):m_Name(name)//形参传给实参
    {
        //todo
    }
    std::string getName()
    {
        return m_Name;
    }
};

int main()
{
    Entity* e = new Entity(); //常见一个Entity实例,
    std::cout << e->getNmae() << std::endl;
    Player* p = new Player("wangbo");
    std::cout << p->getName() << std::endl;
    //预期输出  Entity  wagnbo
    //实际输出  Entity  wagnbo


    //如果使用多态的概念,所写内容内容将会失效 example
    Entity* entity = p;
    std::cout << entity->getNmae() << std::endl;
    //预期输出  wagnbo
    //实际输出  Entity
    //从而引出虚函数。虚函数表创建一个基类的映射,并且能够调取子类的函数

    std::cout << "Hello World!\n";
}

一个更好的例子是以PrintName函数为例引出虚函数

static void printNmae(Entity* entity) //名为printNmae的函数,输入一个Entity实体,看清楚输入是Entity哦
{
    std::cout << entity->getNmae() << std::endl;
}
int main()
{
    printNmae(e);
    printNmae(p);  //传递p不出问题是因为p是一个Player,而Player继承了Entity; 也就是说Player也是一个Entity;
    printNmae(entity);
    //预期输出  Entity  wagnbo   wagnbo
    //实际输出  Entity  Entity   Entity
    //出现实际输出的原因恰恰是我们正确的在类中申明了我们的函数,方法。在调用这个方法时候,无论如何都去调取了属于这个基类Entity的方法
    //我们如何让C++知道我们想调取基类的子类所属的方法呢,————虚函数出来了 虚函数表创建一个基类的映射,并且能够调取子类的函数
    std::cout << "Hello World!\n";
}

虚函数的写法

在基类函数前面加上 virtual,函数就变成了虚函数。虚函数引入了动态分布的概念,通常用虚函数表来实现编译。虚函数表包括基类中所有虚函数的映射,在运行时候能够映射它们向正确的覆写(重写)函数。
virtual添加后生成虚函数表,在子类中一般还可以将覆写的函数用关键字override标记出来,便于观察,使用等。或者在子类函数前面再加上virtual也可以。(祖类不是虚函数,父类是虚函数,子类和父类的关系依旧成立,这里好像设计到了纯虚函数=接口)

class Entity  
{
public:
   virtual std::string getName()    //virtual function
    {
        return "Entity";
    }
};

class Player :public Entity 
{
private:
    std::string m_Name;
public:
    
    //Player(const std::string& name) :m_Name(name) {} //构造函数初始化列表用法
    Player(const std::string& name) 
    {
        m_Name = name;
    }
    virtual std::string  getName() override   //virtual function ovrride 覆写标识可以帮助检查虚函数拼写错误,很有用的
    {
        return m_Name;
    }
    
};

29 纯虚函数(接口)

定义

纯虚函数允许我们定义一个在基类中没有实现的函数,在子类中实际实现。 如果在基类的函数里面有实际函数体(body),意味着在子类中重写函数是可以选择的(可写可不写) ,如果不在子类中覆写,则调取的就是基类的函数体。

如下说明

代码说明

class Entity
{
public:
    virtual std::string getName()
    {
        return "Entity";
    }
};
class Player :public Entity
{
private:
    std::string m_Name;
public:
    //Player(const std::string& name) :m_Name(name) {} //构造函数初始化列表用法
    Player(const std::string& name)
    {
        m_Name = name;
    }
    virtual std::string  getName() override  //不写这个函数,依旧可以调取getName,只不过调取结果是基类Entity的
    {
        return m_Name;
    }

接口

在面向对象程序设计中,创建一个只包含为实现方法并交由子类去实际实现功能的类是非常普的。通常就被称之为接口interface。在接口中,类仅仅包含未实现的方法并充当以勉强的模板,并且由于接口类实际上不包含实现方法,所以我们无法实例化该类。
实例化一个类,这个类必须提供所属纯虚函数的实现。或者祖类是纯虚函数,父类覆写实现后就不是纯虚函数,子类可以继承、(复杂的继承 祖类,父类,子类 理论上应该一样的原理 )。

代码说明

#include <iostream>
class Printable   //这就是一个接口,一个类,里面只有纯虚函数。接口只是C++的一种规定
{
public:
    virtual std::string GetClassName() = 0;
};

class PureEntity: public Printable
{
public:
    virtual std::string getName()
    {
        return "Entity";
    }
    std::string GetClassName() override { return "类名为PureEntity"; }
};
class PurePlayer :public PureEntity
{
private:
    std::string m_Name;
public:
    //Player(const std::string& name) :m_Name(name) {} //构造函数初始化列表用法
    PurePlayer(const std::string& name)
    {
        m_Name = name;
    }
    virtual std::string  getName() override
    {
        return m_Name;
    }
    std::string GetClassName() override { return "类名为PurePlayer"; }
};
static void  pureprintNmae(PureEntity* entity) //名为printNmae的函数,输入一个PureEntity实体,看清楚输入是PureEntity哦
{
    std::cout << entity->getName() << std::endl;
}

class A :public Printable
{
public:
    //virtual std::string GetClassName() override { return "类名为A"; }//如果没有实现这个函数,就不能实例化这个类
    std::string GetClassName() override { return "A"; }
};
//打印所有我们所写的类名的函数实现
//static void print(??? obj) // print(??? obj)  ???代表接受某种类型的参数  一种写程序技巧
static void print(Printable* obj) // print(printable obj)  ???代表接受某种类型的参数,将这种类型命名为printable
{
    std::cout << obj->GetClassName() << std::endl;
}


static void PureVirFct()
{
    PureEntity *pe1 = new PureEntity();
    //pureprintNmae(pe1);
    PurePlayer* pp1 = new PurePlayer("有参数的构造函数 王博");
    //pureprintNmae(pp1);
    print(pe1); //传入指针调用 PureEntity
    print(pp1);
    print(new(A));//这么写可能内存泄露
    /*所有打印都来自于print函数,接受Printable对象。它不关心实际是什么类(基于虚函数表实现的多态性)
    比如随便建立一个类A ,继承Printable这个接口,只需要覆写其中的纯虚函数即可完成实例化。而且print函数也生效
    */
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您提供一份简单的示例程序,其中包含了类的多重继承、类的多级继承、虚继承、公共继承、保护继承、私有继承、子对象、类的组合、虚函数纯虚函数等特性。请您参考以下代码: ```cpp #include <iostream> using namespace std; // 基类 A class A { public: A() { cout << "A's constructor called" << endl; } virtual ~A() { cout << "A's destructor called" << endl; } void print() { cout << "This is A" << endl; } }; // 基类 B class B { public: B() { cout << "B's constructor called" << endl; } virtual ~B() { cout << "B's destructor called" << endl; } void print() { cout << "This is B" << endl; } }; // 基类 C class C { public: C() { cout << "C's constructor called" << endl; } virtual ~C() { cout << "C's destructor called" << endl; } virtual void print() { cout << "This is C" << endl; } }; // 基类 D class D { public: D() { cout << "D's constructor called" << endl; } virtual ~D() { cout << "D's destructor called" << endl; } virtual void print() = 0; // 纯虚函数 }; // 派生类 E 继承自 A、B class E : public A, public B { public: E() { cout << "E's constructor called" << endl; } ~E() { cout << "E's destructor called" << endl; } }; // 派生类 F 继承自 E、C class F : public E, public C { public: F() { cout << "F's constructor called" << endl; } ~F() { cout << "F's destructor called" << endl; } void print() override { cout << "This is F" << endl; } // 覆盖基类 C 的虚函数 }; // 派生类 G 继承自 B,以虚继承方式继承自 C class G : public virtual B, public virtual C { public: G() { cout << "G's constructor called" << endl; } ~G() { cout << "G's destructor called" << endl; } void print() override { cout << "This is G" << endl; } // 覆盖基类 C 的虚函数 }; // 派生类 H 继承自 A,以保护继承方式继承自 D class H : protected A, protected D { public: H() { cout << "H's constructor called" << endl; } ~H() { cout << "H's destructor called" << endl; } void printA() { A::print(); } // 访问 A 的成员函数 void printD() { D::print(); } // 访问 D 的成员函数 }; // 派生类 I 继承自 F,以私有继承方式继承自 G class I : private F, private G { public: I() { cout << "I's constructor called" << endl; } ~I() { cout << "I's destructor called" << endl; } void printF() { F::print(); } // 访问 F 的成员函数 void printG() { G::print(); } // 访问 G 的成员函数 }; // 类 J 包含一个 C 的子对象 class J { public: J() { cout << "J's constructor called" << endl; } ~J() { cout << "J's destructor called" << endl; } private: C c; }; // 类 K 组合了 A、B、C、D 四个类的对象 class K { public: K() { cout << "K's constructor called" << endl; } ~K() { cout << "K's destructor called" << endl; } private: A a; B b; C c; D* d; }; int main() { // 演示类的多重继承 cout << "----- Test for multiple inheritance -----" << endl; E e; e.A::print(); // 访问基类 A 的成员函数 e.B::print(); // 访问基类 B 的成员函数 // 演示类的多级继承 cout << "----- Test for multilevel inheritance -----" << endl; F f; f.A::print(); // 访问基类 A 的成员函数 f.B::print(); // 访问基类 B 的成员函数 f.C::print(); // 访问基类 C 的成员函数 f.print(); // 访问覆盖后的虚函数 // 演示虚继承 cout << "----- Test for virtual inheritance -----" << endl; G g; g.B::print(); // 访问基类 B 的成员函数 g.C::print(); // 访问基类 C 的成员函数 g.print(); // 访问覆盖后的虚函数 // 演示保护继承 cout << "----- Test for protected inheritance -----" << endl; H h; h.printA(); // 访问基类 A 的成员函数 h.printD(); // 访问基类 D 的成员函数 // 演示私有继承和成员访问控制 cout << "----- Test for private inheritance and member access control -----" << endl; I i; // i.A::print(); // 错误:A 对 I 不可访问 // i.B::print(); // 错误:B 对 I 不可访问 // i.E::print(); // 错误:E 对 I 不可访问 i.printF(); // 访问 F 的成员函数 i.printG(); // 访问 G 的成员函数 // i.C::print(); // 错误:C 对 I 不可访问 // i.D::print(); // 错误:D 对 I 不可访问 // 演示子对象和类的组合 cout << "----- Test for sub-object and class composition -----" << endl; J j; K k; return 0; } ``` 上述程序中,基类 A、B、C、D 分别表示不同的类,其中 C 类中包含一个虚函数 print(),D 类中包含一个纯虚函数 print() = 0。派生类 E 继承自 A、B,派生类 F 继承自 E、C,派生类 G 继承自 B 以虚继承方式继承自 C,派生类 H 继承自 A 以保护继承方式继承自 D,派生类 I 继承自 F 以私有继承方式继承自 G。类 J 包含一个 C 的子对象,类 K 组合了 A、B、C、D 四个类的对象。 在程序中,我们演示了类的多重继承、类的多级继承、虚继承、公共继承、保护继承、私有继承、子对象、类的组合、虚函数纯虚函数等特性,并在每个类的构造函数、析构函数中输出相应的信息,以方便观察对象的创建和销毁过程,同时还演示了不同的成员访问控制方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值