C++ day7 多态

 一、多态的概念

摘要:多态:一个接口,多种方法,运行时决定调用哪个。

多态是面向对象编程领域的核心概念(封装wrap(类) 继承inheritance(代码重用) 多态polymorphism(函数重用-适应对象))

虚函数实现多态

虚函数的限制:成员函数(非静态,非构造)

二、重写(覆盖)、重载、隐藏(重定义,理解为不同域的重载)

重写--覆盖(override):重写虚函数,名字、参数、返回值都必须相同(虚函数、不同作用域,其他相同,相当于覆盖继承来的虚成员函数的函数体内容)

重载(overload):成员函数重载(同域同名)

重定义-隐藏:(不同域,同名),相当于子类有两个该同名函数,默认访问子类的,可以加父类作用域访问符来访问继承的函数

        参数不同。此时,不论有无 virtual 关键字,基类的函数将被隐藏(因为不同域不能重载,所以称为隐藏)

        参数相同,但是基类函数没有 virtual关键字。此时,基类的函数被隐藏(注意与覆盖的区别)(基类有virtual就是覆盖了)

总结:
同域,同名重载
虚函数,不同域,同名同参同返回值重写
虚函数,不同域,同名隐藏
非虚函数,不同域,同名隐藏(理解为不同域的重载)
#include <iostream>
using namespace std;
        class Base{
            public:
                Base(int val) : val(val)
                {   
                    cout << __func__ << ":" << __LINE__ << endl;
                }
                ~Base()
                {   
                    cout << __func__ << ":" << __LINE__ << endl;
                }
            public:
                virtual void prnmsg();//---1    虚函数,且子类同名函数也自动为虚函数
                void prnmsg(int i);   //---2    1和2是重载(同作用域同名)
            private:
                int val;
        };

        void Base::prnmsg()            //函数定义不需要virtual关键字
        {
            cout << __func__ << ":" << __LINE__ << endl;
        }
        void Base::prnmsg(int i)
        {
            cout << __func__ << ":" << __LINE__ << ":" << i<< endl;
        }

class Inherit:public Base{            //继承 is-a关系
            public:
                Inherit(int val) : myval(val),Base(val)
                {
                    cout << __func__ << ":" << __LINE__ << endl;
                }
                ~Inherit()
                {
                    cout << __func__ << ":" << __LINE__ << endl;
                }

                void prnmsg()              //---3重写/覆盖(针对虚函数,不同作用域,同名同参同返回值)
                {
                    cout << __func__ << ":" << __LINE__ << endl;
                }
                void prnmsg(int i)         //---4隐藏/重定义(不同作用域,同名,理解为不同域的重载)
                {
                    cout << __func__ << ":" << __LINE__ << ":" << i<< endl;
                }
            private:
                int myval;
        };
//静态链接函数
void test(Base &obj){
    obj.prnmsg();   //如果obj是Base对象,则照常静态链接调用,否则就动态链接,调用相应函数
    obj.prnmsg(123);//非虚函数,不论对象,都调用Base???
}
int main()
{
    Base obja(123);
    Inherit objb(666);
    test(obja);                    //正常调用Base,有参和无参
    test(objb);//Inherit -> Base    //包含关系?调用1次重写的虚函数,一次Base(有参)

    objb.prnmsg(888);    //子类隐藏函数(有参)
    objb.Base::prnmsg(666);//调用被隐藏的继承函数
    return 0;
}

三、动态链接-虚函数表

相关概念:

联编将模块或函数合并在一起生成可执行代码
静态联编就是静态链接,编译阶段,也叫早绑定
动态联编

就是动态链接,执行阶段,也叫晚绑定。

涉及到多态和虚拟函数就必须要使用动态联编

虚函数表使程序达成动态联编。

代码1:区分静态链接与动态链接与隐式转换

因为Inherit是Base的子类,可以使用Base类型的变量接收Inherit类型的变量,系统会自动隐式转换

void test(Base &obj){
    obj.prnmsg();   
}
//如果prnmsg是虚函数,则除了Base &类的对象,其子类在调用该函数时,都使用动态链接(调用相应的prnmsg()) --- 隐式转换
//如果prnmsg不是虚函数,则父类及其子类都调用Base的prnmsg();   --隐式转换

代码2:虚函数表

在C++中,对象的虚函数表通常是一个指向虚函数的指针数组。每个类都有一个虚函数表,而每个对象都有一个指向其所属类的虚函数表的指针。这个指针通常被存储在对象的内存布局中,而不是对象本身。

通过将对象的地址转换为长整数指针并解引用它,我们可以获得存储在对象中的虚函数表指针的值,即虚函数表的首地址。这个地址通常存储在对象的内存布局中的某个固定偏移量上。

目前我是这么理解的:先把obj对象的地址&obj转化为长整型数// long vaddr = (long *)&obj

两边同时取地址可得,&vaddr = (ling *)&obj

PFUNC是指针类型,好比char *

函数指针的指针名+()  表示调用该函数

..........

函数指针:void (*p)(void) ,p是指针类型,可以取别名为PFUNC,指针类型比如int *  char *...

eg:

void (*p)(void) = myFunction;        //表示一个指针,是一个整体

p(); // 调用 myFunction                  //特殊的调用函数方式

	#include <iostream>
    using namespace std;
	class Base{
        public:
            virtual void funa(void)
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }

            virtual void funb(void)
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }

    };

    class Inherit : public Base{
        public:
            void funa(void)
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }

            void funb(void)
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }

    };
//---------------------------------------------------------------//
    typedef void (*PFUNC) (void);//函数指针

    int main()
    {
        Base obj;

        long vaddr = *(long *)&obj;//得到虚函数表的首地址
        PFUNC fun =  (PFUNC)*(long *)vaddr;//得到虚函数表中登记的第一个虚函数的首地址
        fun();//调用funa()

        fun =  (PFUNC)*((long *)vaddr + 1);//
        fun();

        return 0;
    }

四、抽象类

父类有纯虚函数的类叫抽象类,在父类public中声明=0,在子类public中实现

派生类应该实现抽象类的所有方法

应用:比如一个点派生出圆形,矩形,三角形.   那么求面积函数就可以定义为纯虚函数wu

#include <iostream>
using namespace std;
		class Graphic{
        public:
           virtual double area(void) = 0;//纯虚函数 在派生类中实现
    };

    class Circle : public Graphic{
    	public:
    		Circle(double r) : r(r)
    		{
    		
    		}
        public:
            double area(void) //重写基类的纯虚接口
            {
            	return r*r*3.14;
            }
		private:
			double r;
    };
	int main()
	{
		Graphic obja;//error 抽象类不能有对象
		Circle objb(5);
		cout << objb.area() << endl;
	
		return 0;
	}

五、虚继承(多重继承 主函数使用时加  类名::)

解决多重继承产生的二义性

	class Base{
        public:
            Base()
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }
            ~Base()
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }
        public:
            void prnmsg()
            {
                cout << __func__ << ":" << __LINE__ << endl;

            }
    }; 

    class Man : public virtual Base{//虚继承:解决多重继承产生的二义性
        public:
            Man()
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }
            ~Man()
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }
    };

    class Wolf :public virtual Base{
        public:
            Wolf()
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }
            ~Wolf()
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }
    };

    //多重继承
    class WolfMan : public Wolf,public Man{
        public:
            WolfMan()
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }
            ~WolfMan()
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }


    };
    int main()
    {
        WolfMan obj;

        obj.prnmsg();//多重继承产生二义性

        return 0;
    }

六、虚析构函数(最好都定义为虚析构,防止空间回收不完整)

父类虚析构就行了,子类自动虚析构

父类指针指向子类空间的时候,防止回收不完整

	class Base{
        public:
            Base(int val=0) : val(val)
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }
            virtual ~Base()
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }
        private:
            int val;
    };

    class Inherit : public Base{
        public:
            Inherit(int val = 0) : myval(val)
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }
            ~Inherit()//基类析构为虚时,子类析构自动为虚
            {
                cout << __func__ << ":" << __LINE__ << endl;
            }
        private:
            int myval;
    };

    int main()
    {   
        Base *p = new Inherit;//类型转换 Inherit * - > Base *
       //用空间来理解:子类中包含了父类的val,所以如果不加虚析构就写Base *p就只会释放val的那一部分

        delete p;//子类对象析构时,基类也会被析构

        return 0;
    }

 Base *p = new Inherit;//类型转换

怎么理解这行代码:子类空间中会包含父类的成员(可用sizeof验证),用空间来理解,如果隐式转换为Base类型,使用普通析构函数时,只会释放Base,如果使用虚析构函数,则Base和Inherit都会析构一次(因为本质还是Inherit创建的对象,而子类创建对象又必须途径父类)

七、限制构造函数(加友元)

构造函数的权限不是public,这样的构造函数叫限制构造函数。

需要友元函数

		class Base{
		protected:
			Base() //限制构造函数
			{
				cout << __func__ << ":" << __LINE__ <<endl;
			}
		public:
			~Base()
			{
				cout << __func__ << ":" << __LINE__ <<endl;
			}
		};
		
		class Inherit : public Base{
		public:
			Inherit()
			{
				cout << __func__ << ":" << __LINE__ <<endl;
			}
			~Inherit()
			{
				cout << __func__ << ":" << __LINE__ <<endl;
			}
		};
		
		int main()
		{
			Base obj;//error
			Inherit obj;//right
		
			return 0;
		}
	eg2:
		class Demo{
        private:
            Demo() //限制构造函数
            {
                cout << __func__ << ":" << __LINE__ <<endl;
            }
        public:
            ~Demo()
            {
                cout << __func__ << ":" << __LINE__ <<endl;
            }
        friend Demo *getobj(void);
        friend void freeobj(Demo *p);
    };

    Demo *getobj(void)
    {
        Demo *p = new Demo;

        return p;
    }

    void freeobj(Demo *p)
    {
        delete p;
    }

    int main()
    {
        Demo *p = getobj();
        freeobj(p);

        return 0;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值