c++多态详解

多态

多态基本概念

**多态的概念**:
通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态 。

多态定义以及实现

**	1..重写/覆盖 的要求**

重写/覆盖: 子类中有一个跟父类完全相同的虚函数,子类的虚函数重写了基类的虚函数
即:子类父类都有这个虚函数 + 子类的虚函数与父类虚函数的 函数名/参数/返回值 都相同 -> 重写/覆盖(注意:参数只看类型是否相同,不看缺省值)

	**2.多态两个要求:**

1、被调用的函数必须是虚函数,子类对父类的虚函数进行重写 (重写:三同(函数名/参数/返回值)+虚函数)
2、父类指针或者引用去调用虚函数。

#include <iostream>

using namespace std;

class Person
{
	public:
		//constructor不能用虚函数关键字修饰
		virtual void buyTicket()
		{
			cout << "100$" << endl;
		}
};

class Student : public Person
{
	public:
		void buyTicket()
		{
			cout << "80$" << endl;
		}
};

int main(int argc, const char *argv[])
{
	Person p;
	p.buyTicket();//
	cout << "-------------" << endl;
	Student s;
	s.buyTicket();
	cout << "-------------" << endl;
	Person *p1 = &s;
	p1->buyTicket();
	Person &p2 = s;
	p2.buyTicket();
	cout << "-------------" << endl;
	Person p3 = s;
	p3.buyTicket();
	return 0;
}

静态绑定和动态绑定

**静态绑定**:绑定的是对象的静态类型,某特性(比如函数)依赖于对象的静态类型,发生在编译期
**动态绑定:**绑定的是对象的动态类型,某特性(比如函数)依赖于对象的动态类型,发生在运行期
**代码实例:**
    #include "iostream"


    using namespace std;

    class B{
        public:
            void DoSomething(){
                cout << "B DoSomething()......\n";
            }
            virtual void vfun(){
                cout << "B vfun()......\n";
            }
    };


    class C: public B{
        public:
            void DoSomething(){  // 首先说明一下,这个子类重新定义了父类的no-virtual函数,这是一个不好的设计,会导致名称隐藏;这里只是为了说明动态绑定和静态绑定才这样使用。
                cout << "C DoSomething().....\n";
            }
            virtual void vfun(){
                cout << "C vfun()......\n";
            }
    };

    class D:public B{
        public:
            void DoSomething(){
                cout << "D DoSomething().....\n";
            }
            virtual void vfun(){
                cout << "D vfun()......\n";
            }
    };

    int main(){
        // 静态绑定
        D *pD = new D();
        B *pB = pD;
        pD->DoSomething();
        pB->DoSomething();
        // 动态绑定
        pD->vfun();
        pB->vfun();
        return 0;
    }


注意
当缺省参数和虚函数一起出现的时候情况有点复杂,极易出错。我们知道虚函数是动态绑定的,但是为了执行效率,缺省参数是静态绑定的

    #include "iostream"


    using namespace std;


    // 对象的动态与静态类型
    class B{
        public:
            void DoSomething(){
                cout << "B DoSomething()......\n";
            }
            virtual void vfun(){
                cout << "B vfun()......\n";
            }
            // 缺省参数和虚函数一起出现,是静态绑定!
            virtual void func(int i = 20){
                cout << "B func()......\n";
            }
    };


    class C: public B{
        public:
            void DoSomething(){  // 首先说明一下,这个子类重新定义了父类的no-virtual函数,这是一个不好的设计,会导致名称隐藏;这里只是为了说明动态绑定和静态绑定才这样使用。
                cout << "C DoSomething().....\n";
            }
            virtual void vfun(){
                cout << "C vfun()......\n";
            }
            virtual void func(){
                cout << "C func()......\n";
            }
    };

    class D:public B{
        public:
            void DoSomething(){
                cout << "D DoSomething().....\n";
            }
            virtual void vfun(){
                cout << "D vfun()......\n";
            }
            virtual void func(){
                cout << "D func()......\n";
            }
    };



    int main(){
        // 缺省参数和虚函数一起出现,是静态绑定!
        D *pD = new D();
        B* pB = pD;
        pD->func();
        pB->func();
        return 0;
    }

虚函数与动态多态(上面介绍了一些)

重写的特点

1.与基类的虚函数有相同的参数个数
2.与基类的虚函数有相同的参数类型
3.与基类的虚函数有相同的返回值类型
4.不同作用域
5.基类必须有virtual关键字不能有static
6.重写函数访问限定符可以不用相同

**例外: 协变和析构函数可以不尊崇上面的规则**
**协变(返回值为对象引用或者指针)**

指针
class A {};
class B :public A {};
class Person
{
public:
	virtual A* func()
	{
		cout << "virtual A* func()" << endl;
		return new A;
	}
};
class Student : public Person
{
public:
	virtual B* func()
	{
		cout << "virtual B* func()" << endl;
		return new B;
	}
};

引用

class Human
{
public:
	virtual Human& print()
	{
		cout << "i am a human" << endl;
		
		return *this;
	}
};

class Student : public Human
{
public:
	virtual Student& print()
	{
		cout << "i am a student" << endl;

		return *this;
	}
};

析构函数的重写

class Human
{
public:
	~Human()
	{
		cout << "~Human()" << endl;
	}
};

class Student : public Human
{
public:
	~Student()
	{
		cout << "~Student()" << endl;
	}
};

int main()
{
	Human* h = new Student;
	delete h;

	return 0;
}

final和override关键字

final
使用final的虚函数不能被重写(c++11)

class Human
{
public:
	virtual void print() final
	{
		cout << "i am a human" << endl;
	}

};

class Student : public Human
{
public:
	virtual void print()
	{
		cout << "i am a student" << endl;
	}
};

override
override关键字是用来检测派生类虚函数是否构成重写的关键字

class A
{
public:
	virtual void func() {}
};

class B : public A
{
public:
	//未重写则报错
	virtual void func() override {};
};

覆盖,重载,隐藏

在这里插入图片描述
当用virtual修饰的时候,类的实例化就会产生一个虚表,专门放虚函数,当子类继承父类后,重写虚函数的时候,子类的对象实例化就会覆盖原来的虚函数,所以也就是上面的子类对象在调用的时候,总是调用子类实现的成员函数,当父类的指针或引用指向子类对象时候如上所示!

动态原理和虚函数表

在这里插入图片描述

#include <iostream>

using namespace std;

class A
{
	public:
		virtual void show()
		{
			cout << "A show" << endl;
		}

		virtual void show2()
		{
			cout << "A show2" << endl;
		}
};

class B : public A
{
	public:
		virtual void show2()
		{
			cout << "B show2" << endl;
		}
};

typedef void (*Func)();

int main(int argc, const char *argv[])
{
	A *a = new B;
	a->show();
	a->show2();
	cout << "-------------" << endl;
	cout << "virtual_Pointer Address is " << (long *)a << endl;
	cout << "virtual_table Address is " << (long *)*(long *)a << endl;
	cout << "virtual_table func1 Address is " << (long *)*(long *)a << endl;
	cout << "virtual_table func2 Address is " << (long *)*(long *)a + 1 << endl;
	cout << "virtual_table real func1 Address is " << (long*)((long *)*(long *)a) << endl;
	cout << "virtual_table real func2 Address is " << (long *)((long *)*(long *)a + 1) << endl;
	Func fa = (Func)*((long *)*(long *)a);
	fa();

	cout << "---------"  << endl;
	B b;
	A &a1 = b;

	cout << "virtual_Pointer Address is " << (long *)&a1 << endl;
	cout << "virtual_table Address is " << (long *)*(long *)&a1 << endl;
	cout << "virtual_table func1 Address is " << (long *)*(long *)&a1 << endl;
	cout << "virtual_table func2 Address is " << (long *)*(long *)&a1 + 1 << endl;
	cout << "virtual_table real func1 Address is " << (long*)((long *)*(long *)&a1) << endl;
	cout << "virtual_table real func2 Address is " << (long *)((long *)*(long *)&a1 + 1) << endl;

	Func fa1 = (Func)*((long *)*(long *)&a1);
	fa1();

	return 0;
}

抽象类与纯虚函数

在这里插入图片描述

#include <iostream>

using namespace std;
//如果子类没有实现抽象类的所有虚函数,那么子类也会变成抽象类
class Base
{
	public:
		virtual void get_area() = 0;
};

class Circle : public Base
{
	public:
		Circle(int r)
		{
			this->r = r;
		}

		void get_area()
		{
			cout << "Circle area is: " << 3.14 * r * r << endl;
		}

	private:
		int r;

};

class Rect : public Base
{
	public:
		Rect(int length, int height)
		{
			this->length = length;
			this->height = height;
		}

	void get_area()
	{
		cout << "Rect area is: " << length * height << endl;
	}

	private:
	int length;
	int height;
};

int main(int argc, const char *argv[])
{
	Circle c(4);
	c.get_area();
	Rect r(10,10);
	r.get_area();
	Base *b = &c;//抽象类只能用指针和引用,不能直接实例化
	b->get_area();
	Base &rb = r;
	rb.get_area();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值