【C++】多态(详解)

07-06 818阅读

前言:今天学习的内容可能是近段时间最难的一个部分的内容了,C++的多态,这部分内容博主认为难度比较大,各位一起慢慢啃下来。

💖 博主CSDN主页:卫卫卫的个人主页 💞

👉 专栏分类:高质量C++学习 👈

💯代码仓库:卫卫周大胖的学习日记💫

💪关注博主和博主一起学习!一起努力!

【C++】多态(详解)


目录标题

  • 什么是多态
  • 什么是虚函数
  • 多态的定义及实现
    • 1.多态构成的条件
    • 2. 虚函数的重写
    • 3.协变
    • 4.析构函数的重写
    • 5. override 和 final
    • 6. 重载、覆盖(重写)、隐藏(重定义)的对比
    • 抽象类
    • 多态的原理
      • 虚函数表
      • 虚表指针的内容
      • 引用和指针如何实现多态
      • 动态绑定与静态绑定
      • 虚函数表存放位置
      • 单继承和多继承中的虚拟表

        什么是多态

        多态的概念:多态(polymorphism)是C++中面向对象编程的一个重要概念,它指的是同一种消息(方法调用)在不同的对象上产生不同的行为。这种特性使得程序设计更加灵活,提高了代码的可扩展性和可维护性。(通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态)。


        什么是虚函数

        在C++中,虚函数是一种特殊的成员函数,用于实现多态性。通过将基类的成员函数声明为虚函数,可以在派生类中对该函数进行重写。当通过基类指针或引用调用虚函数时,实际调用的是相应派生类中的函数。

        虚函数的声明和定义如下:

        class Base//基类
        {
        public:
            virtual void foo() {
                // 函数实现
            }
        };
        

        在基类的函数声明前加上virtual关键字,就将该函数声明为虚函数。派生类可以选择重写基类的虚函数,使用相同的函数签名来定义派生类中的函数:

        class Derived : public Base//派生类 
        {
        public:
            void foo() override {
                // 函数实现
            }
        };
        

        注意,在派生类中重写虚函数时,可以使用override关键字显式标注,以增强代码可读性。

        使用虚函数时,需要通过基类指针或引用来调用虚函数。根据指针或引用所指向的具体对象类型,调用的将是相应对象的虚函数实现。

        例如:

        Base* base = new Derived();
        base->foo(); // 调用的是Derived的foo()函数
        

        在上述示例中,通过基类指针base指向Derived对象,并调用虚函数foo(),实际调用的是Derived类中的foo()函数。

        总结来说,虚函数实现了在基类中声明一个函数,使其可以在派生类中被重写,并能通过基类指针或引用调用派生类对象的对应实现,从而实现多态性。


        多态的定义及实现

        1.多态构成的条件

        C++中的多态性是指在相同的函数签名下,通过基类指针或引用调用不同的派生类对象时,能够实现不同的行为。在C++中,实现多态性需要满足以下三个条件:

        1. 存在继承关系:多态性需要至少有一个基类和一个或多个派生类。
        2. 基类函数为虚函数:基类中的函数必须声明为虚函数,以便在派生类中进行重写。子类父类都有这个虚函数 + 子类的虚函数与父类虚函数的函数名/参数/返回值 都相同 。
        3. 使用基类指针或引用:通过基类指针或引用来调用派生类对象的函数,实现函数的动态绑定。

        下面是一个示例代码,演示了多态性的实现:

        class Animal //基类
        {
        public:
            virtual void sound() 
            {
                cout 
        public:
            virtual void sound() {
                cout 
        public:
            virtual void sound() {
                cout 
            s.sound();
        }
        void test1()
        {
            Animal s1;
            Cat s2;
            Dog s3;
            func(s1);
            func(s2);
            func(s3);
        }
        void test2()
        {
            Animal* animal1 = new Animal();//当基类的指针指向派生类的时候,只能操作派生类中从基类中继承过来的数因据和基类自身的数据
            Animal* animal2 = new Cat();
            Animal* animal3 = new Dog();
            animal1-sound(); // Animal is making a sound.
            animal2-sound(); // Cat is meowing.
            animal3-sound(); // Dog is barking.
            delete animal1;
            delete animal2;
            delete animal3;
        }
        int main() 
        {
            test1();
            test2();
            return 0;
        }
        
        public:
            virtual Animal* clone() 
            {
                cout 
        public:
            virtual Dog* clone() override 
            {
                cout 
            Dog s1;
            Animal* s2 = s1.clone();//基类接受派生类的虚函数的返回值构造对象
            return 0;
        }
        
        public:
            virtual ~Animal() {
                cout 
        public:
            virtual ~Dog() override {
                cout 
            Animal* animal = new Dog();//先调用子类的析构,在调用父类的析构
            delete animal;
            return 0;
        }
        
        public:
            virtual void foo() const;
        };
        class Derived : public Base {
        public:
            void foo() const override;  // override关键字表示覆盖基类的虚函数
        };
        
        public:
            virtual void foo() const final;  // final关键字表示禁止派生类进一步覆盖该虚函数
        };
        class Derived : public Base {
        public:
            // 下面的代码会导致编译错误,因为foo()函数被标记为final,无法再被派生类覆盖
            // void foo() const;
        };
        
        public:
            virtual void foo();
        };
        class Derived : public Base {
        public:
            void foo() override;
        };
        
        public:
            void foo(int x);
        };
        class Derived : public Base {
        public:
            void foo(float x);
        };
        
        public:
            virtual void pureVirtualFunction() = 0; // 纯虚函数
            virtual void virtualFunction() { // 非纯虚函数
                // 具体实现
            }
        };
        
        public:
            void pureVirtualFunction() override {
                // 实现纯虚函数
            }
        };
        
        public:
        	virtual void func1() { cout  cout }
         
        int main()
        {
        	Base b1;
        	Base b2;
        	static int a = 0;
        	int b = 0;
        	int* p1 = new int;
        	const char* p2 = "hello world";
        	printf("静态区:%p\n", &a);
        	printf("栈:%p\n", &b);
        	printf("堆:%p\n", p1);
        	printf("代码段:%p\n", p2);
        	printf("虚表:%p\n", *((int*)&b1));//虚表的地址是存放在类对象的头4个字节上因此我们对其进行强转,取地址就会得到虚表的位置了,
        	printf("虚函数地址:%p\n", & Base::func1);
        	printf("普通函数:%p\n", func);
        }
        
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]