精华内容
下载资源
问答
  • C++ 多态 动态绑定

    2010-10-20 20:56:00
    C++ 动态绑定 多态

    动态绑定的多态的三个条件:

    1、类之间满足类型兼容规则;

    2、申明虚函数

    3、由成员函数来调用 或者 通过指针、引用来访问虚函数

     

     

    展开全文
  • C++ 多态/动态绑定

    2020-05-25 15:03:39
    C++ 父类 子类 继承相关 参照文章: https://blog.csdn.net/sparkjin/article/details/8768743. 上次看到一个继承题目,代码如下: class A { public: A(){func(0);}; virtual void func(int data){printf(...

    C++ 多态/动态绑定

    参照文章: https://blog.csdn.net/sparkjin/article/details/8768743.

    上次看到一个继承题目,代码如下:

    class A
    { 
    	public: 
    	A(){func(0);}; 
    	virtual void func(int data){printf("A1 :%d\n",data);} 
    	virtual void func(int data) const{printf("A2 :%d\n",data);} 
    	void func(char *str){printf("A3 :(%s)\n",str);} 
    }; 
    class B:public A
    { 
    	public: 
    	void func(){printf("B1 :%s\n","");} 
    	void func(int data){printf("B2 :%d\n",data);} 
    	void func(char *str){printf("B3 :(%s)\n",str);} 
    }; 
    int main() 
    { 
    	A *pA; //1
    	B b; //2
    	const A *pcA; //3
    	pA=&b; //4
    	pA->func(1); //5
    	pA->func("test"); //6
    	A().func(1); //7
    	pcA=&b; //8
    	pcA->func(2); //9
    	return 0; //
    }  
    

    以下是代码输出分析:
    1、声明了一个A类指针pA;
    2、实例了一个B类对象b;(执行默认构造函数,先父类A的构造,调fun(0),是虚函数,但是在基类构造时,所有虚函数都不虚;后子类的构造函数,无)
    3、const A类指针pcA;
    4、基类指针pA指向派生类对象;
    5、基类的指针指向派生类对象:那么该指针只能够调用基类所定义的函数,如果基类中没有该函数,则报错,如果有且非虚则调基类该函数(不管派生类中该函数情况),但是如果该函数为虚函数,引发了动态绑定,到派生类中去找是否重写了该函数,重写则调用该派生类自己的成员函数,没重写则仍调用基类的该函数。
    基类有,虚,派生类中也有,调派生类的。
    6、基类的func(char *str)不是虚函数,所以调A的。
    7、A()实例一个临时A类对象,调A的默认构造,.func(1)调A的,因为这个对象就是个A类对象,不因为是虚函数就必须调派生类重写函数。
    8、const A类指针pcA指向派生类B对象b;
    9、虽然基类对应函数是虚,但是派生类B类并没有对其进行重写,所以还是输出A类的。

    答案:
    A1 :0 //第2句输出
    B2 :1 //第5句输出
    A3 :(test) //第6句输出
    A1 :0 //第7句输出
    A1 :1
    A2 :2 //第9句输出

    这就比较好解释,为什么基类的虚函数为什么要是虚函数:当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生。当基类的指针指向子类对象析构时,会动态绑定到派生类的析构函数上,调用派生类的析构函数。随后派生类析构后,会自动调用父类的析构函数(C++的设定,与多态无关,即使析构函数不是虚函数,子类析构后也会调用父类的析构函数)。

    展开全文
  • C++ 多态

    2021-08-02 20:50:38
    静态多态在编译阶段绑定地址,地址早绑定,静态联编。 动态多次在运行阶段绑定地址,地址晚绑定,动态联编。 静态多态 函数重载(函数名相同,函数列表不同),运算符重载 动态多态 先有继承关系 父类中有虚函数,...

    C++ 多态

    静态联编动态联编

    1. 静态多态在编译阶段绑定地址,地址早绑定,静态联编。
    2. 动态多次在运行阶段绑定地址,地址晚绑定,动态联编。

    静态多态

    函数重载(函数名相同,函数列表不同),运算符重载

    动态多态

    1. 先有继承关系
    2. 父类中有虚函数,子类重写父类中的虚函数
    3. 父类的指针或引用 指向子类的对象
    class Animal
    {
    public:
    	//虚函数
    	virtual void speak()
    	{
    		cout << "动物在说话" << endl;
    	}
    
    	virtual void eat(int a )
    	{
    		cout << "动物在吃饭" << endl;
    	}
    };
    
    class Cat :public Animal
    {
    public:
    	void speak()
    	{
    		cout << "小猫在说话" << endl;
    	}
    
    	void eat(int a)
    	{
    		cout << "小猫在吃饭" << endl;
    	}
    };
    
    class Dog :public Animal
    {
    public:
    	void speak()
    	{
    		cout << "小狗在说话" << endl;
    	}
    };
    
    
    //动态多态产生条件:
    //先有继承关系
    //父类中有虚函数,子类重写父类中的虚函数
    //父类的指针或引用  指向子类的对象
    
    //对于有父子关系的两个类  指针或者引用 是可以直接转换的。
    //父类的指针或引用  指向子类的对象
    void doSpeak(Animal & animal) //Animal & animal = cat;
    {
    	//如果地址早就绑定好了,地址早绑定,属于静态联编
    	//如果想调用小猫说话,这个时候函数的地址就不能早就绑定好,而是在运行阶段再去绑定函数地址,属于地址晚绑定,叫动态联编
    	animal.speak(); 
    }
    
    void test01()
    {
    	//调用doSpeak()方法,若父类中的doSpeak()方法未指定为虚方法(virtual),
    	//则调用时使用的是父类中的方法,会输出:动物在说话。
    	
    	//若父类中的doSpeak()方法指定为虚方法(virtual),则调用时doSpeak()方法后,会使用子类的doSpeak()方法
    	Cat cat;
    	doSpeak(cat);
    
    	Dog dog;
    	doSpeak(dog);
    }
    
    

    多态原理

    class Animal
    {
    public:
    	//虚函数
    	void speak()
    	{
    		cout << "动物在说话" << endl;
    	}
    };
    
    //此时不占字节,sizeof  Animal = 1
    //加上virtual关键字后  sizeof  Animal = 4
    cout << "sizeof  Animal = " << sizeof (Animal) << endl;
    
    1. 当父类写了虚函数后,类内部结构发生改变,多了一个vfptr
    2. vfptr 虚函数表指针 ---- > vftable 虚函数表
    3. 虚函数表内部记录着 虚函数的入口地址
    4. 当父类指针或引用指向子类对象,发生多态,调用是时候从虚函数中找函数入口地址

    虚函数 关键字 virtual
    利用指针的偏移调用 函数

    在这里插入图片描述

    Cat未发生重写时
    在这里插入图片描述
    Cat重写后
    在这里插入图片描述

    使用指针偏移方式调用虚函数

    void test02()
    {
    	Animal * animal = new Cat;
    	//默认调用
    	animal->speak();
    	// *(int *)animal 解引用到虚函数表中
    	// *(int *)(*(int *)animal) 解引用到函数speak地址
    
    	//调用猫说话,函数指针(void(*)()
    	((void(*)()) (*(int *)(*(int *)animal))) ();
    
    
    	//C/C++默认调用惯例  __cdecl
    	//用下列调用时候 真实调用惯例  是 __stdcall
    	//调用猫吃饭,若虚函数中存在形参,要统一调用惯例,否则程序调用会出问题
    	typedef void( __stdcall *FUNPOINT)(int);
    	(FUNPOINT (*((int*)*(int*)animal + 1)))(10);
    }
    

    调用惯例

    1. 主调函数和被调函数必须要有一致约定,才能正确的调用函数,这个约定我们称为调用惯例
    2. 调用惯例 包含内容: 出栈方、参数传递顺序、函数名称修饰
    3. C/C++下默认调用惯例: cdecl 从右到左 ,主调函数管理出栈
    ((void(*)()) (*(int *)*(int *)animal)) ();
    //指定函数的 调用惯例
    typedef void( __stdcall *FUNPOINT)(int);
    (FUNPOINT (*((int*)*(int*)animal + 1)))(10);
    

    纯虚函数和抽象类

    1. 语法:virtual int getResult() = 0;
    2. 如果一个类中包含了纯虚函数,那么这个类就无法实例化对象了,这个类通常我们称为 抽象类
    3. 抽象类的子类 必须要重写 父类中的纯虚函数,否则也属于抽象类
    //利用多态实现计算器
    class AbstractCalculator
    {
    public:
    
    	//纯虚函数
    	//如果一个类中包含了纯虚函数,那么这个类就无法实例化对象了,这个类通常我们称为 抽象类
    	//抽象类的子类 必须要重写 父类中的纯虚函数,否则也属于抽象类
    	virtual int getResult() = 0;
        
        //纯函数
    	virtual int getVirtualResult()
    	{
    		return 0;
    	}
    
    	int m_A;
    	int m_B;
    };
    

    虚析构和纯虚析构

    虚析构语法:

    1. virtual ~Animal(){}
    2. 如果子类中有指向堆区的属性,那么要利用虚析构技术 ,在delete的时候 调用子类的析构函数,添加虚析构后才会调用子类的析构函数

    纯虚析构语法:

    1. virtual ~Animal() = 0;
    2. Animal::~Animal(){ .. }
    3. 纯虚析构,需要有声明,也需要有实现(类内声明,类外实现)
    4. 如果一个类中 有了 纯虚析构函数,那么这个类也属于抽象类,无法实例化对象了
    class Animal
    {
    public:
    	Animal()
    	{
    		cout << "Animal的构造函数调用" << endl;
    	}
    	virtual void speak()
    	{
    		cout << "动物在说话" << endl;
    	}
    	
    	//如果子类中有指向堆区的属性,那么要利用虚析构技术 在delete的时候 调用子类的析构函数
    	//virtual ~Animal()
    	//{
    	//	cout << "Animal的析构函数调用" << endl;
    	//}
    
    	//纯虚析构 需要有声明 也需要有实现
    	//如果一个类中 有了 纯虚析构函数,那么这个类也属于抽象类,无法实例化对象了
    	virtual ~Animal() = 0;
    };
    
    Animal::~Animal()
    {
    	cout << "Animal的纯虚析构函数调用" << endl;
    }
    
    
    class Cat :public Animal
    {
    public:
    	Cat(const char* name)
    	{
    		cout << "Cat的构造函数调用" << endl;
    		this->m_Name = new char[strlen(name) + 1];
    		strcpy(this->m_Name, name);
    	}
    
    	virtual void speak()
    	{
    		cout << this->m_Name << " 小猫在说话" << endl;
    	}
    
    	~Cat()
    	{
    		if (this->m_Name)
    		{
    			cout << "Cat的析构函数调用" << endl;
    			delete[] this->m_Name;
    			this->m_Name = NULL;
    		}
    	}
    	char* m_Name;
    };
    

    在这里插入图片描述

    向上类型转换和向下类型转换

    1. 父转子 :向下类型转换 ,不安全,会出现地址越界
    Animal * animal = new Animal;
    Cat * cat  = (Cat *) animal;
    
    1. 子转父 向上类型转换 ,安全,仅仅是取址范围缩小
    Cat * cat = new Cat;
    Animal * animal = (Animal *) cat;
    
    1. 如果发生多态,那么转换永远都是安全的,父类子针或引用指向子类对象
    Animal * animal = new Cat;
    Cat * cat = (Cat * ) animal;
    

    Animal * animal = new Cat;执行时已经开辟出了Cat所需要的内存,只是当用animal指针指向时取址范围为Animal大小,当强转回Cat时,使用原始地址范围。
    在这里插入图片描述

    重载、重写、重定义

    重载

    同一作用域的同名函数

    1. 同一个作用域下
    2. 参数个数,参数顺序,参数类型不同
    3. 和函数返回值,没有关系
    4. const也可以作为重载条件
    do(const Teacher& t){}
    
    do(Teacher& t){}
    

    重写(覆盖)

    子类重写父类中的虚函数,函数返回值、函数名、形参列表完全一致

    1. 有继承
    2. 子类重写父类的virtual函数
    3. 函数返回值,函数名字,函数参数必须和基类中的虚函数一致

    重定义(隐藏)

    子类重新定义父类中的同名成员函数,隐藏掉父类中同名成员函数,如果想调用加作用域

    1. 有继承
    2. 子类重新定义父类的同名成员(非virtual函数)

    多态案例2 - 电脑组装案例

    
    //纯虚函数
    //CPU基类
    class CPU
    {
    public:
    	virtual void calculate() = 0;
    };
    //显卡基类
    class VideoCard
    {
    public:
    	virtual void display() = 0;
    };
    //内存基类
    class Memory
    {
    public:
    	virtual void storage() = 0;
    };
    
    
    //电脑类
    class computer
    {
    public:
    
    	computer(CPU * cpu, VideoCard * vc, Memory * mem)
    	{
    		cout << "电脑构造调用" << endl;
    		this->m_Cpu = cpu;
    		this->m_Vc = vc;
    		this->m_Mem = mem;
    	}
    
    	void doWork()
    	{
    		this->m_Cpu->calculate();
    		this->m_Vc->display();
    		this->m_Mem->storage();
    	}
    
    	~computer()
    	{
    		cout << "电脑析构调用" << endl;
    		if (this->m_Cpu)
    		{
    			delete this->m_Cpu;
    			this->m_Cpu = NULL;
    		}
    		if (this->m_Vc)
    		{
    			delete this->m_Vc;
    			this->m_Vc = NULL;
    		}
    		if (this->m_Mem)
    		{
    			delete this->m_Mem;
    			this->m_Mem = NULL;
    		}
    	}
    
    	CPU * m_Cpu;
    	VideoCard * m_Vc;
    	Memory * m_Mem;
    };
    
    
    //inter厂商
    class intelCPU :public CPU
    {
    public:
    	void calculate()
    	{
    		cout << "intelCPU开始计算了" << endl;
    	}
    };
    
    class intelVideoCard :public VideoCard
    {
    public:
    	void display()
    	{
    		cout << "intel 显卡开始显示了" << endl;
    	}
    
    };
    class intelMemory :public Memory
    {
    public:
    	void storage()
    	{
    		cout << "intel 内存条开始存储了" << endl;
    	}
    };
    
    
    //Lenovo 厂商
    class LenovoCPU :public CPU
    {
    public:
    	void calculate()
    	{
    		cout << "Lenovo CPU开始计算了" << endl;
    	}
    };
    
    class LenovoVideoCard :public VideoCard
    {
    public:
    	void display()
    	{
    		cout << "Lenovo 显卡开始显示了" << endl;
    	}
    
    };
    class LenovoMemory :public Memory
    {
    public:
    	void storage()
    	{
    		cout << "Lenovo 内存条开始存储了" << endl;
    	}
    };
    
    
    void test01()
    {
    	cout << "第一台电脑组成:" << endl;
    
    	CPU * intelCpu = new intelCPU;
    	VideoCard * lenovoVC = new LenovoVideoCard;
    	Memory * lenovoMem = new LenovoMemory;
    
    	computer c1(intelCpu, lenovoVC, lenovoMem);
    
    	c1.doWork();
    
    	cout << "第二台电脑组成:" << endl;
    
    	CPU * intelCpu2 = new LenovoCPU;
    	VideoCard * lenovoVC2 = new intelVideoCard;
    	Memory * lenovoMem2 = new intelMemory;
    
    	computer c2(intelCpu2, lenovoVC2, lenovoMem2);
    
    	c2.doWork();
    
    
    }
    
    展开全文
  • C++多态之动态绑定

    2017-03-19 23:49:52
    是指与给定的过程调用相关联的代码,只有在运行期才可知的一种绑定,它是多态实现的具体形式。 原理: C++中,通过基类的引用或指针调用虚函数时,发生动态绑定。引用(或指针)既可以指向基类对象也可以指向派生类...

    动态绑定:
    定义:
    动态绑定是将一个过程调用与相应代码链接起来的行为。是指与给定的过程调用相关联的代码,只有在运行期才可知的一种绑定,它是多态实现的具体形式。
    原理:
    C++中,通过基类的引用或指针调用虚函数时,发生动态绑定。引用(或指针)既可以指向基类对象也可以指向派生类对象,这一事实是动态绑定的关键。用引用(或指针)调用的虚函数在运行时确定,被调用的函数是引用(或指针)所指对的实际类型所定义的。
    C++中动态绑定是通过虚函数实现的。而虚函数是通过一张虚函数表实现的。这个表中记录了虚函数的地址,解决继承、覆盖的问题,保证动态绑定时能够根据对象的实际类型调用正确的函数。
    在C++的标准规格说明书中说到,编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。
    缺点:
    1.动态绑定在函数调用时需要在虚函数表中查找,所以性能比静态函数调用稍低。
    2.通过基类类型的指针访问派生类自己的虚函数将发生错误。

    虚函数、动态绑定、运行时多态之间的关系:
    虚函数是动态绑定的基础;动态绑定是实现运行时多态的基础。

    动态绑定两个条件:
    (1) 只有虚函数才能进行动态绑定,非虚函数不进行动态绑定。
    (2) 必须通过基类类型的引用或指针进行函数调用。

    对应的有静态绑定
    静态绑定是指不需要考虑表达式的执行期语义,仅分析程序文本而决定的表达式类型。静态绑定仅依赖于包含表达式的程序文本的形式,而在程序运行时不会改变。简单的讲,就是上下文无关,在编译时就可以确定其类型。

    动态绑定与静态绑定
    静态绑定:编译时绑定,通过对象调用
    动态绑定:运行时绑定,通过地址实现

    示例:

    #include <iostream>
    using namespace std;
    class Base
    {
    public:
        virtual void f1()
        {
            cout<<"Base"<<endl;
        }
    };
    class Drived1:public Base
    {
    public:
        void f1()
        {
            cout<<"Drived1"<<endl;
        }
    };
    class Drived2:public Base
    {
    public:
        void f1()
        {
            cout<<"Drived2"<<endl;
        }
    };
    void Test(Base* pB)
    {
        pB->f1 ();
    }
    int main()
    {
        Base b;
        Drived1 d1;
        Drived2 d2;
        Test(&b);
        Test(&d1);
        Test(&d2);
        return 0;
    }

    输出结果:
    Base
    Drived1
    Drived12

    展开全文
  • 什么是多态? 简单来说,就是不同继承关系的派生类对象,去调用同一个函数,产生不同的行为。 多态在游戏中的应用场合? 比如有一个游戏,里面有不同的怪物,每个怪物都可以攻击,走路,idel。在编写怪物逻辑的时候...
  • 文章目录 待完成
  • C++多态

    2019-04-12 20:03:04
    C++多态 多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。 C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。 静态链接(静态多态) ...
  • c++多态

    2020-07-30 12:25:03
    C++多态总结(动态)多态概念(动态)多态底层(动态)多态优点纯虚函数和抽象类虚析构和纯虚析构 多态是C++面向对象三大特性封装、继承、多态之一 多态分为两类 静态多态: 函数重载 和 运算符重载属于静态多态,...
  • C++ 多态(迟绑定)原理

    千次阅读 2014-05-28 10:58:34
    今天看到一篇帖子关于虚函数的问题,原以为自己已经对c++虚函数,多态了解的不错了,但是,还是发现有一些模糊点,所以,又回头重新总结了一番。 有了不小的收获,下面,就和大家一起来分享一下,也希望对一些...
  • C++系列】C++ 多态

    2021-04-27 11:22:07
    C++系列】C++ 多态
  • 【C++】C++多态实现

    2020-06-02 06:48:24
    参考链接: ...C++ 多态 多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过...有时候这也被称为早绑定,因为 area() 函数在程序编译期间就已经设置好了。 但现在,让我们对程序稍作修改,在 Shape

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 30,834
精华内容 12,333
关键字:

c++多态早绑定

c++ 订阅