C++中静态绑定与动态绑定

C++中静态绑定与动态绑定

多态:多态是指同一实体同时具有多种形式,即一个名字可以具有多种语义,函数重载、类模板、函数模板等都属于多态性。通俗来说就是借口的多种不同实现方式。

1.对象的静态类型:对象在声明时采用的类型,是在编译期确定的。

2.对象的动态类型:目前所指对象的类型,是在运行期决定的。对象的动态类型可以更改,但是静态类型无法更改。

对象静态类型和动态类型:

class A{
};
class B:public A{
};
class C:public A{
};
C* pc=new C();  //pc的静态类型是它声明类型C*,动态类型也是C*
A* pa=pc;  //pa的静态类型是它声明的类型A*,动态类型是pa所指向的对象pc的类型C*
B* pb=new B();
pa=pb; //pb的动态类型是可以更改的,现在它的动态类型是B*

函数绑定:就是函数的入口地址同函数调用相联系的过程。分为静态绑定和动态绑定。

区别:静态绑定在程序执行前完成,由编译系统或操作系统装入程序计算函数的入口地址;而动态绑定则是执行过程中完成,由程序自身计算函数的入口地址。

C++中,非虚函数都是静态绑定,而虚函数却是动态绑定。

重点:构造函数和析构函数中调用虚函数,虚函数是静态绑定的,本类只会调用类本身的虚函数。

静态绑定和动态绑定例:

静态绑定是编译期间就可以确定的类型

Base *B=new Base();    //B的类型只能是Base

动态绑定则只能运行时确定

Derived是Base的派生类

Base *B=new Derived();  //B的静态类型是Base  动态类型却是Derived

#include<iostream>
using namespace std;
class Base{
	public:
		void fun(){
			cout<<"Base"<<endl;
		} 
};
class Derived:public Base{
	public:
		
};
int main(){
	Derived x;
	Base *pb=&x;
	pb->fun();
	Derived *pd=&x;
	pd->fun();
	return 0;	
}

如果Derived中没有定义fun()函数,则两次调用的行为肯定会是一样的,为:

Base

Base

但是调用过程却不相同,

pb->fun();  //pb为指向B类型的指针,直接调用B中的fun()函数

pd->fun();  //pd为指向D类型的指针,首先在Derived中查找fun()函数声明,没有找到,然后到Base中去找,找到fun(),则停止查找。

但如果Derived中有自己定义的fun()函数,将会执行Derived的fun()函数。

pb->fun();  //调用Base::fun();

pd->fun();//调用Derived::fun();

原因为:非虚函数都是静态绑定,也就是说,由于pb被声明为指向B类型的指针,那么通过pb调用的非虚函数用于是Base所定义的版本,即使pb指向一个类型为"Base的派生类Derived"的对象。

但是,虚函数却是动态绑定,不管是通过什么类型的指针调用的这个虚函数,都会根据指针实际指向的对象类型来决定虚函数的调用,而与指针类型无关,如果fun()函数为虚函数,那么不管是通过pd还是pb调用fun()函数,都会调用Derived::fun(),因为pd与pb真正指向的都是同一个类型Derived的对象:

#include <iostream>
using namespace std;
class Base{
    public:
        virtual void fun(){
            cout<<"Base"<<endl;
        }
};
class Derived:public Base{
    public:
        void fun(){
            cout<<"Derived"<<endl;
    }
};
int main(){
    Derived x;
    Base *pb=&x;
    pb->fun();
    Derived *pd=&x;
    pd->fun();
    return 0;
}

将fun函数声明为虚函数,不管是通过pb还是pd调用fun()函数,都会调用Derived::fun(),因为pb和pd真正指向的都是同一类型Derived的对象,程序输出为:

Derived

Derived

因此C++中,绝对不要重新定义继承而来非虚函数,如果这样,函数的调用决定因素不在对象本事,而与调用函数的指针类型有关。

如果一个对象Derived可能表现出Base或Derived的行为,决定因素不在x对象本身,而是在于指向x的指针类型,同时,不要重新定义一个继承而来的virtual函数的缺省参数值,因为缺省参数值都是静态绑定,而virtual函数却是动态绑定。

虚函数:

虚函数:也是类的一种成员函数,并且不能是静态成员函数。

虚函数的作用是实现动态绑定,也就是在程序的运行阶段动态的选择合适的成员函数。从而实现多态性

设置虚函数的一写注意事项:

1、只有类的成员函数才能声明为虚函数。因为虚函数只适用于有继承关系的类对象,所以普通函数不能声明为虚函数。

2、静态成员不能是虚函数,因为静态成员不受限于某个对象。

3、内联函数不能是虚函数,因为内联函数不能在运行中欧诺个动态的确定其位置。即使虚函数在类的内部定义,编译时仍将其看做是非内联的。

4、构造函数不能是虚函数,因为构造时对象还是一片未定型的空间。只有在构造完成后,对象才能成为一个类的名副其实的实例。

5、析构函数可以是虚函数,而且通常声明为虚函数。目的在于:使用delete删除一个对象时,确保析构函数能够被正确运行。因为设置虚析构函数后,可以利用动态绑定方式选择析构函数。

class A
{
public:
	A()
	{
		cout<<"A()"<<endl;
	}
	virtual ~A()
	{
		cout<<"~A()"<<endl;
	}
};
class B:public A
{
private :
	char *_buf;
public:
	B(int i)
	{
		_buf = new char[i];
		cout<<"B()"<<endl;
	}
	virtual ~B()
	{
		delete[] _buf;
		cout<<"~B()"<<endl;
	}
};
void fun(A* pa)
{
	delete pa;
}
int main()
{
	A *pa = new B(10);
	fun(pa);
	system("pause");
	return 0;
}

结果为:

A();

B();

~B();

~A();

如果~A没有定义为虚函数,则结果为:

A();

B();

~A();

纯虚函数:无函数体的一种特殊的虚函数。

其声明的格式为:virtual 类型 函数名(参数表)= 0;

抽象类:至少有一个纯虚函数的类。

作用:将有关类组织在一个继承层次结构中,由它提供一个公共的根,相关的子类都是从这个根派生出来的。

抽象类的使用规定:

1、抽象类只能用作其他类的基类,不能建立抽象类对象。

2、抽象类不能用作参数类型、函数返回类型或显示转换类型

3、可以定义抽象类的指针和引用,此指针可以指向它的派生类,从而实现多态。

#include<iostream>
using namespace std;
class A //抽象类
{
public:
	A()
	{
		cout<<"A()"<<endl;
	}
	virtual ~A()
	{
		cout<<"~A()"<<endl;
	}
	virtual void fun()=0;
};
class B: public A 
{
	public:
	B()
	{
		cout<<"B()"<<endl;
	}
	virtual ~B()
	{
		cout<<"~B()"<<endl;
	}
	void fun()
	{
		cout<<"B::fun()"<<endl;
	}
};
class C: public A 
{
	public:
	C()
	{
		cout<<"C()"<<endl;
	}
	virtual ~C()
	{
		cout<<"~C()"<<endl;
	}
	void fun()
	{
		cout<<"C::fun()"<<endl;
	}
};
int main()
{
	A *arr[2];
	B b;
	C c;
	arr[0] = &b;
	arr[0]->fun();
	arr[1] = &c;
	arr[1]->fun();
	system("pause");
	return 0;
}

特别注意:

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

class A{
    virtual void fun(int i=1){};
};
class B:public A{
    virtual void fun(int i=2){};
};
B* pb=new B();
A* pa=pb;
pb->fun();
pa->fun();

pb->fun()和pa->fun()调用都是函数B::fun(),但是缺省参数为多少?

缺省参数是静态绑定的,pb->fun(),pb的静态类型是B*,所以它的缺省参数应该是2;pa->fun()的缺省参数应该是1.

所以要记住:绝对不要重新定义一个继承而来的virtual函数的缺省参数值,因为缺省参数值都是静态绑定(为了执行效率),而virtual函数却是动态绑定。

二、RTTI

RTTI机制的来源:在C++中存在虚函数,也就存在多态性,对于多态的对象,在程序编译时可能会出现无法确认对象的类型的情况,当类中有虚函数时,基类的指针可以指向任何派生类的对象,这时可能不知道基类指针到底指向那个派生类的对象,而类型的确定要子啊运行时类型标志做出,为了获得一个对象的类型可以使用typeid函数;

1.两个操作符

(1)typeid操作符,返回指针和引用所指的实际类型

(2)dynamic_cast操作符,用来执行运行是类型的识别和装换,dynamic_cast用于将一个父类对象的指针转换为子类对象的指针或者引用(动态转换);

2.typeid函数

该函数的作用主要是让用户知道当前的变量时什么类型

#include<iostream>
#include<typeinfo.h> 
using namespace std;
class A{
    public:
        void fun(){ cout<<"A::fun()"<<endl;}
};
class B:public A{
    public:
        void fun(){cout<<"B::fun()"<<endl;}
};
int main(){
    A *pa=new A();
    A a;
    cout<<typeid(pa).name()<<endl;
    cout<<typeid(a).name()<<endl;
    B *pb=new B();
    cout<<typeid(pb).name()<<endl;
}

在调用完typeid()函数之后,接着调用name()成员函数,因为typeid()函数是一个返回类型为const typeid_info&类型的函数,接下来简单了解type_info类;

3.type_info类

class type_info
{
public:
    virtual ~type_info();
    //在type_info类中重载了==运算符,该运算符用于比较两个对象的类型是否相等
    bool operator ==(const type_info&)const;
    //在type_info类中重载了!=运算符,该运算符用于比较两个对象的类型是否不相等
    bool operator !=(const type_info&)const;
    //使用成员函数name,该函数返回对象的类型的名字
    const char*name()const;

    bool before(const type_info&);
private:
    type_info(const type_info&);
    type_info&operator =(const type_info&);
    //type_info类的构造函数和赋值运算符都是私有的
};

( 1 )因为typr_info类的构造和赋值运算符重载都是私有,所以不允许用户自己创建,所以要使用type_info类的方法使用typeid函数。
(2)使用type_info类中重载的==与!=用于比较两个类型是否相等,如果两个对象的类型相等,则返回1,不相等返回0
这种方法也可以用于比较两个带有虚函数的类的对象是否相等如例。

4.dynamic_cast<>运算符

dynamic_cast<>运算符用来执行运行时类型识别和转换
dynamic_cast<>可以实现两个方向的转换,upcast和downcast
upcast:把派生类的指针,引用转换成基类的指针和引用
downcast::父类对象指针->子类指针/引用(用dynamic_cast转型是安全的)

#include<iostream>
using namespace std;
class Base
{
public:
    virtual void fun()
    {
        cout << "Base::fun()" << endl;
    }
};
class Driver :public Base
{
public:
    virtual void fun()
    {
        cout << "Driver::fun()" << endl;
    }
};
int main()
{

    Base  b;
    Driver d;

    Driver *pd = dynamic_cast<Driver*>(&b);//不需要转换返回0
    cout << pd << endl;

    Base *pb = dynamic_cast<Driver*>(&b);//不需要转换 返回0
    cout << pb<< endl;

    pb = &d;
    pd = dynamic_cast<Driver*>(pb);//转换成功非0
    cout << pd << endl;
}

 

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值