c++ 多态

    多态是c++中很重要的一环。多态能够分为如下几个层面来剖析:c++

    1.对象的类型编程

    2.多态ide

    3.虚表函数


    先说第一点对象的类型,这个很是简单。好比说、this

int a;

    那么我就定义了一个int类型的变量a。再来看下面的代码3d

class Base
{

};

class Derive:public Base
{

};

    这里我写了一个Base类和一个Derive类,而且Derive类是派生于Base类指针

Base b;
Derive d;

Base* pb=&b;
pb=&d;

    上面的代码实例化了一个Base类类型的对象b,Derive类类型的对象d,Base*类型的指针pb。
对象

pb的静态类型就是Base*类型,咱们也可让pb指向d,Derive*就是pb的动态类型。blog


    下面来讲说第二点,多态。继承

    

int Add(int left,int right)
{
    return left+right;
}

double Add(double left,double right)
{
    return left+right;
}

    上面这两个函数构成了函数的重载,传进去int类型的参数就调用上面的,double类型的参数就调用下面的。这也是一种多态,称为静态的多态。还有一种泛型编程也是静态的多态。静态多态就是在编译器编译期间完成的,编译器根据函数的实参的类型(可能进行隐式的类型转换),可推断出到底要调用哪一个函数,若是有对应的函数就调用该函数,不然就会出现编译错误。

    

    那么动态多态(也叫动态绑定)就是指在程序执行期间判断所引用对象的实际类型,根据其实际类型调用相应的方法。

    使用virtual关键字来修饰函数,指明该函数为虚函数,派生类须要从新实现,编译器将实现动态绑定。

    所谓虚函数就是指在类中被声明为virtual的成员,基类但愿这种成员在派生类中重定义。除了构造函数外,任意非static成员均可觉得虚成员。保留字 virtual 只在类内部的成员函数声明中出现,不能用在类定义体外部出如今函数定义上。

    看一段代码:

class Base
{
public:
    virtual void FunTest()
    {
        cout<<"Base::FunTest()"<<endl;
    }
};

class Derive :public Base
{
public:
    void FunTest()
    {
        cout<<"Derive::FunTest()"<<endl;
    }
};

int main()
{
    Derive d;
    d.FunTest();
    
    Base b;
    b.FunTest();
    
    Base *pb=&b;
    pb->FunTest();
    pb=&d;
    pb->FunTest();
    
    return 0;
}

    在这一段代码里面,我定义了两个类一个是Base,另外一个是他的派生类Derive。Base类里面有一个虚函数FunTest(),Derive类里面也有一个FunTest()。而且在主函数里面实例化了两个类的对象,而且调用了FunTest函数,下面也定义了Base*类型的指针,先指向b,而后调用了FunTest函数,以后指向d,而后调用FunTest函数。这段代码运行结果会是什么样呢?

wKioL1cl1quARdN6AAANKMNh70U673.png

    正如咱们所看到的调用派生类里面的函数他有他就调用他本身的,他没有再去基类里面找。 

    那么动态绑定实现的条件是什么呢?第一,必需要是虚函数。第二,要经过基类类型的引用或者指针调用。

class CBase
{
public:
	virtual void FunTest1(int _iTest)
	{
		cout << "CBase::FunTest1()" << endl;
	}
	void FunTest2(int _iTest)
	{
		cout << "CBase::FunTest2()" << endl;
	}
	virtual void FunTest3(int _iTest1)
	{
		cout << "CBase::FunTest3()" << endl;
	}
	virtual void FunTest4(int _iTest)
	{
		cout << "CBase::FunTest4()" << endl;
	}

};


class CDerive:public CBase
{
public:
	virtual void FunTest1(int _iTest)
	{
		cout << "CDerive::FunTest1()" << endl;
	}
	virtual void FunTest2(int _iTest)
	{
		cout << "CDerive::FunTest2()" << endl;
	}
	void FunTest3(int _iTest1)
	{
		cout << "CDerive::FunTest3()" << endl;
	}
	virtual void FunTest4(int _iTest1,int _iTest2)
	{
		cout << "CDerive::FunTest4()" << endl;
	}
};

int main()
{
	CBase* pBase = new CDerive;
	pBase->FunTest1(0);
	pBase->FunTest2(0);
	pBase->FunTest3(0);
	pBase->FunTest4(0);
	return 0;
}


    上面是一个例子,CBase类是CDerive类的基类。以后FunTest1()是一个虚函数,FunTest2()不是一个虚函数,FunTest3()也是虚函数,FunTest4()虽然是虚函数可是在子类里面从新实现给了两个参数。因此运行结果是这样的:

wKiom1cmuYKwahkaAAALt2t1hLw612.png

    假如咱们想调用CDerive里面的FunTest4(),咱们就要用CDerive类的对象了。就像下面这样:

        CDerive d;
	d.FunTest4(0, 0);

    咱们这里有一个图片,能看明白继承体系中同名成员函数的关系:
wKioL1cmvB2z9w6hAACZa0goxnk476.png

    这里还须要注意:构造函数是不能够定义为虚函数的,由于构造函数是用来构建咱们的对象 的,构造函数没有执行完咱们的对象就是不完整的。假如咱们要调用构造函数,是须要经过咱们的基类对象来调用的,可是咱们的对象都没有构造完,因此是不能这样的。

    静态函数和友元函数也一样不能够用virtual来修饰。由于这两种函数都没有this指针。


    这里还有一个东西:

class test
{
	virtual void Test() = 0;
};

    这段代码定义的类叫抽象类。它不可以实例化产生对象,它只是提供一些接口。它里面的那个函数后面跟了一个=0,表示它是纯虚函数,它表示它的派生类要对它这个函数进行重写。


    最后来讲虚表和虚指针。

    当咱们求sizeof(test)时,咱们得出的结果是4。为何呢?对类求大小的时候不该该是它的成员的大小吗?这里就是有一个虚指针。那这个虚指针指向那里呢?是指向的虚表。虚表里面存的就是虚函数的地址。

    举一个例子:

class test
{
public:
	virtual void FunTest1()
	{}
	virtual void FunTest2()
	{}
	virtual void FunTest3()
	{}
	virtual void FunTest4()
	{}
};


    这个类里面只有四个虚函数,那么sizeof(test)等于多少呢?

wKiom1crAcuxar5bAAAJ0p4dP7s313.png

    再来看看t中到底有什么:

wKiom1crAgey11AWAAAhAUsZcj4440.png


    因此当咱们要调用虚函数的时候,编译器是先找到咱们的虚表地址,以后找到对应的虚函数。

相关文章
相关标签/搜索