虚函数与多态(一)

1.多态

   (1)多态性是面向对象程序设计的重要特征之一。

   (2)多态性是指发出相同的消息被不同类型的对象接收时,有可能导致完全不同的行为。

   (3)多态的实现:函数重载,运算符重载,模板,虚函数

2.静态绑定与动态绑定

   (1)静态绑定:绑定过程出现在编译阶段,在编译期就已确定要调用的函数。

   (2)动态绑定:绑定过程工作在程序运行时执行,在程序运行时才确定将要调用的函数。

3.虚函数

   (1)虚函数的概念:在基类中冠以关键字virtual的成员函数

   (2)虚函数的定义:

             [1]virtual 函数类型 函数名称(参数列表)

             [2]如果一个函数在基类中被声明为虚函数,则它在所有派生类中都是虚函数

   (3)只有通过基类指针或引用调用虚函数才能引发动态绑定

   (4)虚函数不能声明为静态

4.虚析构函数

   (1)何时需要虚析构函数?

   (2)当你可能通过基类指针删除派生类对象是

   (3)如果你算允许其他人通过基类指针调用对象的析构函数(通过delete这样做是正常的),并且被析构的对象是有重要的析构函数的派生类的对象,就需要让基类的析构函数作为虚函数

5.虚表指针

   (1)虚函数的动态绑定是通过虚表来实现的

   (2)包含虚函数的类头4个字节存放指向虚表的指针

 32 位系统虚拟内存空间的分布情况,最上方的内核空间不用多讲,下方的用户空间内存

通过这张图你可以看到,用户空间内存,从低到高分别是五种不同的内存段。

  1. 只读段,包括代码和常量等。

  2. 数据段,包括全局变量等。

  3. 堆,包括动态分配的内存,从低地址开始向上增长。

  4. 文件映射段,包括动态库、共享内存等,从高地址开始向下增长。

  5. 栈,包括局部变量和函数调用的上下文等。栈的大小是固定的,一般是 8 MB。

#include <iostream>

using namespace std;


class Base {
public:
	virtual void Fun1() {
		cout << "Base::Fun1" << endl;
	}
	virtual void Fun2() {
		cout << "Base::Fun2" << endl;
	}
	int data1_;
};



class Derived : public Base {
public:
	virtual void Fun2() override final {
		cout << "Derived::Fun2" << endl;
	}
	virtual void Fun3() {
		cout << "Derived::Fun3" << endl;
	}
	int data2_;
};

typedef void (*FUNC)();

int main() {
	cout << sizeof(Base) << endl;
	cout << sizeof(Derived) << endl;
	
	Base b;	
	long**p = (long**)&b;
	FUNC fun = (FUNC)p[0][0];
	(*fun)();
	fun = (FUNC)p[0][1];
	(*fun)();
	cout << endl;
	
	Derived d;
	p = (long**)&d;
	fun = (FUNC)p[0][0];
	(*fun)();
	fun = (FUNC)p[0][1];
	(*fun)();
	fun = (FUNC)p[0][2];
	(*fun)();
	cout << endl;
	
	Base* pp = &d;
	pp->Fun2();  // 动态绑定
	d.Fun2();  //  静态绑定 
	return 0;
}

6.object slicing与虚函数

模板方法模式

#include <iostream>

using namespace std;


class CObject {
public:
	virtual void Serialize() {
		cout << "CObject::Serialize ..." << endl;
	}
};


class CDocument : public CObject {
public:
	int data1_;
	CDocument() {
		cout << "CDocument()" << endl;
	}
	CDocument(const CDocument& other) {
		cout << "CDocument(const CDocument& other)" << endl;
	}
	void func() {
		cout << "CDocument::func ..." << endl;
		Serialize();
	}	
	virtual void Serialize() override {
		cout << "CDocument::Serialize ..." << endl;
	}
};


class CMyDoc : public CDocument {
public:
	int data2_;
	virtual void Serialize() override {
		cout << "CMyDoc::Serialize ..." << endl;
	}
};

int main() {
	
	CMyDoc mydoc;
	CMyDoc* pmydoc = new CMyDoc;

	cout << "#1 testing" << endl;
	mydoc.func();
	
	cout << "#2 testing" << endl;
	((CDocument*)(&mydoc))->func();

	cout << "#3 testing" << endl;
	pmydoc->func();

	cout << "#4 testing" << endl;		
	((CDocument)mydoc).func();  // mydoc对象强制转换为CDocument对象,向上转型
				   //完完全全将对象转化为基类对象
				   //调用默认的拷贝构造函数
	
	delete pmydoc;
	pmydoc = NULL;
	return 0;
}

7.overload,override,overwrite 

 (1)成员函数被重载的特征:overload

             [1]相同的范围(在同一个类中);

             [2]函数名字相同;

             [3]参数不同;

             [4]virtual关键字可有可无

  (2)覆盖是指派生类函数覆盖基类函数,特征是:override

            [1]不同的外围(分别位于派生类与基类);

            [2]函数名字相同;

            [3]参数相同;

            [4]基类函数必须有virtual关键字

   (3)重定义(派生类与基类):overwrite

            [1]不同的范围(分别位于派生类与基类);

            [2]函数名与参数都相同,无virtual关键字;

            [3]函数名相同,参数不同,virtual可有可无

1. 子类继承父类的虚函数表的方式是复制一份。存在虚函数的类,都有自己的虚函数表,不与其他类共用。

2. 只要祖先类的某个函数被声明位virtual, 则在后代中无论是否显式地添加virtual,该函数一直都是虚的。

3. 如果子类重写了某个虚函数,则该类的虚函数表对应位置上的虚函数地址会被覆盖。如果完全不重写,则子类的虚函数表里面的内容和父类虚函数表内容保持一致。

4. 虚函数表类似于类的静态成员,被该类的所有实例共享。只不过该表是只读的,不存在线程安全问题。

5. 实例对象在起始4字节中,会存放虚函数表的地址(跟系统寻址范围有关,有的系统8字节才能表示一个地址),可以通过手动寻址的方式完成虚函数调用。

6. 多重继承(有多个父类并列)时,该类有多张虚函数表,0~3字节,4~7字节分别表示第一张虚函数表地址,第二张虚函数表地址,以此类推。

7. 即便父类的虚函数是private的,子类的虚函数表中也依然存在它的地址,我们可以通过子类实例的首地址,寻找到虚函数表进而找到该虚函数,完成调用。这样会违背c++语言的设计初衷,破坏数据的安全性,一般不要轻易使用。

#include <iostream>
#include <string>
using namespace std;
 
typedef void(__stdcall *Fun)();
typedef void(__stdcall *Fun1)(double);
 
//声明virtual,让该成员函数地址在虚函数表中留下记录
class Brass{
  private:
    std::string fullname;
    long acctNum;
    double balance;
  public:
    Brass(const std::string & s ="NullBody",long an=-1,double bal=0.0);
    void Deposit(double amt);
    virtual void ViewAcct() const;
    virtual void withdraw(double amt);
    double Balance() const;
    virtual void test(){cout<<"Brass::test"<<endl;}
    virtual ~Brass(){}
};
 
//即便子类不再将父类的虚函数函数声明为virtual,只要祖先类中该函数是virtual,则它依然是虚函数
class BrassPlus:public Brass{
private:
    double maxLoan;
    double rate;
    double owesBank;
public:
    BrassPlus(const std::string & s ="NullBody",long an=-1,
              double bal=0.0,double ml=500,double r=0.11125);
    BrassPlus(const Brass &ba,double ml=500,double r=0.11125);
    void ViewAcct()const;
    void withdraw(double amt);
    void ResetMax(double m){maxLoan=m;}
    void ResetRate(double r){rate = r;}
    void ResetOwes(){owesBank=0;}
};
 
class BrassPlusPlus:public BrassPlus{
public:
    void ViewAcct() const override{cout<<"BrassPlusPlus viewacct"<<endl;}
};
 
 
Brass::Brass(const std::string & s,long an,double bal):fullname(s),acctNum(an),balance(bal){}
void Brass::Deposit(double amt){return;}
void Brass::withdraw(double amt){cout<<"Brass withdraw: "<<amt<<endl;return;}
double Brass::Balance() const{return balance;}
void Brass::ViewAcct()const{cout<<"Brass viewacct"<<endl;}
 
BrassPlus::BrassPlus(const std::string & s,long an,double bal,double ml,double r):Brass(s,an,bal),maxLoan(ml),rate(r){}
BrassPlus::BrassPlus(const Brass &ba,double ml,double r):Brass(ba),maxLoan(ml),rate(r){}
void BrassPlus::ViewAcct()const{cout<<"BrassPlus viewacct"<<endl;}
void BrassPlus::withdraw(double amt){cout<<"BrassPlus withdraw: "<<amt<<endl;return;}
 
void test(const Brass& br){
    br.ViewAcct();
    //br.withdraw(6.0);//编译不通,const 实例只能调用const成员函数;
    Brass& bbp=const_cast<Brass&>(br);
    bbp.withdraw(6.0);
}
 
int main()
{
#ifdef _WIN64
    typedef __int64 ADTY;//64位操作系统,8个字节表示一个地址
#else
    typedef __int32 ADTY;
#endif
    cout << "Hello World!" << endl;
    Brass bra("Jim",2,0.06);
    BrassPlus brap("Jhon",5,0.58,500,0.014);
 
    const Brass bra_2("Jack",3,0.02);
    Brass& b1 = bra;
    Brass& b2 = brap;
 
    Brass *bas[2];
    bas[0] = &bra;
    bas[1] = &brap;
 
    cout << "对象bas[0]的虚函数表位于:"<<hex<<*(ADTY*)bas[0]<<",对象bas[0]的第一个虚函数位于:"<<hex<<*( (ADTY *)*(ADTY*)bas[0] + 0 )<<endl;
    cout << "对象bas[1]的虚函数表位于:" <<hex<<*(ADTY*)bas[1]<<",对象bas[1]的第一个虚函数位于:"<<hex<<*( (ADTY *)*(ADTY*)bas[1] + 0 )<<endl;
    cout << "子类虚函数表会从父类继承(复制一份,不是共用一份),如果子类重写了某个虚函数,则子类的虚函数表中对应的函数地址会被覆盖"<<endl;
 
    Fun cc = (Fun)( *( (ADTY *)*(ADTY*)bas[1] + 0 ) );
    Fun1 dd = (Fun1)( *( (ADTY *)*(ADTY*)bas[1] + 1 ) );
    cout<<"对象bas[1]虚函数调用结果:"<<endl;
    cc();
    dd(8.9);
 
    ADTY *ptr = (ADTY*)(&bra);
 
    //可以看出虚函数表类似于类的静态成员,对象的首地址位置保存了虚函数表的地址
    cout << "对象bra的虚函数表位于:"<<hex<<*ptr<<",对象bra的第一个虚函数位于:"<<hex<<*( (ADTY*)*ptr + 0 )<<endl;
    cout << "对象bra_2的虚函数表位于:" <<hex<<*(ADTY*)(&bra_2)<<",对象bra_2的第一个虚函数位于:"<<hex<<*( (ADTY*)*(ADTY*)(&bra_2) + 0 )<<endl;
    cout << "说明bra的虚函数表 与 bra_2的虚函数表 是同一张表"<<endl;
 
    ADTY* ptr1 =(ADTY*)*ptr;
    Fun pFun = (Fun)*(ptr1+0); //等效于 (Fun)(*((ADTY*)*(ADTY*)(&bra)+0));
    pFun();
 
    Fun1 pFun1 = (Fun1)*(ptr1+1); //等效于 (Fun)(*((ADTY*)*(ADTY*)(&bra)+1));
    pFun1(9.0);
    cout << "Hello World end" << endl;
    return 0;
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值