C++虚函数---我的理解

先了解一些基础知识

1.构造函数和析构函数一般是公有成员,否则该类不能实例化

2.类成员默认访问权限是private类型(strcut默认访问权限是public

 

3.析构函数一般是虚函数,否则可能会造成内存泄漏

4.拥有虚函数的的类都会有一个虚表,该虚表的指针位于类的首地址

虚表展示:

 

 

5.没有虚函数的类,首地址是成员变量(先后顺序和在类声明里面的一直,与公私有无关),

  如果有虚函数,则类的成员变量从第5个位置开始放,前四个位置储放虚表地址

6.函数编译产生的地址与inlinevirtual无关(产生地址是连续的)

 

7.打印成员变量和函数地址的方法

printf("成员变量m_char的地址%p\n",&Base::m_char);

printf("成员函数funBase1的地址%p\n",&Base::funBase1);

8.类空指针访问成员函数,但是如果drivedFun1是虚函数则会访问失败

 

这样的成员函数(非虚拟函数)与类实体不是存放在一起的。所以你调用时与实体是没关的。 但是如果是虚函数,就要用到vptr,这个是与类实体有关的,当然就不行了。static函数更是与实体没关的,也是可以调用的。 如果要深入理解,建议去看《深度探索C++对象模型》

进一步的测试

1.如果类指针没有访问类的虚函数和类指针的普通函数没有访问成员变量,则即使类指针即使为空该指针一样可以正常调用该函数(无所谓父类对象转为子类指针或者子类对象转为父类指针,甚至无所谓父子类)。

具体例子:

Drived d;

//Drived *pDrived = (Drived *)&d;  (2

Drived *pDrived = NULL;         (3

pDrived->drivedFun1();           (4

代码行2和代码行3的赋值结果不会影响到代码行4的执行结果

 

2.每个拥有虚函数的类都有自己的虚表(下图展示了父子类虚表地址值的不同)

 

 

3.虚函数好处:父类指针指向子类对象可以调用子类函数,从而实现多态

原理:带有虚函数的类会产生一个虚表,虚表里有虚函数地址,程序执行时会动态加载

总结:虚函数的好处在于利用父类指针调用子类函数,当然也可以用子类指针指向父类对象,调用父类的函数,根据对象类型而定义函数,而不是指针类型。

问题:为什么非得继承?不具有继承关系的可以吗?如果不可以那么为什么不可以?

 

 

 

 

 

 

 

 

 

 

==================继承前(下面的父子类其实并无继承关系)==================

基类指针和子类指针结构:

 

把指向子类对象的指针强转为父类指针之后(pBase = (Base *)pDrived;)

 

观察pBase的变化

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

=================继承后(下面的父子类有了真实的继承关系)=================

基类指针和子类指针结构:

 

把指向子类对象的指针强转为父类指针之后(pBase = (Base *)pDrived;)

 

 

 

 

 

 

 

 

 

 

 

 

// VirtualFunction.cpp : 定义控制台应用程序的入口点。

//

 

#include "stdafx.h"

#include <stdio.h>

 

/Base/

class Base

{

//测试默认访问权限

int test;

 

public:

char m_char[20];

int m_int;

 

public:

Base();

virtual ~Base();

 

public:

void funBase1();

void funBase2();

void funBase3();

 

virtual void commonFun();

};

 

Base::Base()

{

m_int = 0x11223344;

strcpy(m_char"123456781234567");

}

Base::~Base()

{

}

 

void Base::funBase1()

{

printf("funBase1\n");

}

void Base::funBase2()

{

printf("funBase2\n");

}

void Base::funBase3()

{

printf("funBase3\n");

}

void Base::commonFun()

{

printf("commonFun in Base\n");

}

class Base2

{

public:

Base2(){};

virtual ~Base2(){};

 

public:

//virtual void commonFun();

};

// void Base2::commonFun()

// {

// //printf("commonFun in Base2");

// }

///Drived/

class Drived : public Basepublic Base2

{

public:

int m_int;

public:

Drived(){};

virtual ~Drived(){};

 

virtual void drivedFun1();

virtual void commonFun();

};

 

void Drived::drivedFun1()

{

printf("drivedFun1\n");

}

void Drived::commonFun()

{

printf("commonFun in Drived\n");

}

 

int _tmain(int argc_TCHARargv[])

{

 

// printf("成员变量m_char的地址%p\n",&Base::m_char);

// printf("成员变量m_int的地址%p\n",&Base::m_int);

// 

// printf("成员函数funBase1的地址%p\n",&Base::funBase1);

// printf("成员函数funBase2的地址%p\n",&Base::funBase2);

// printf("成员函数funBase3的地址%p\n",&Base::funBase3);

 

Base b;

Base *pBase = (Base *)&b;

 

Drived d;

Drived *pDrived = (Drived *)&d;

 

// pBase = (Base *)pDrived;

// pBase->commonFun();

 

pBase = (Base *)pDrived;

pBase->drivedFun1();

 

return 0;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值