C++基础 虚函数

这篇具有很好参考价值的文章主要介绍了C++基础 虚函数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

参考

顺便记录下写的比较好的博客
C++ Primer Plus (第6版)
C++虚函数表
C++内存模型
关于vtordisp知多少?
【VC++】虚函数 内存结构 - 第四篇(多重继承,无虚函数覆盖)
C++ 虚函数表剖析

虚函数

静态联编: 在编译过程中函数实现与函数关联
动态联编: 在程序执行阶段函数实现和调用关联

基类中没有把函数声明为虚, 根据指针类型调用调用对应的函数实现, 编译器对非虚方法使用静态联编
基类中把函数声明位虚, 根据对象类型调用对应的函数实现, 编译器对虚方法使用动态联编

向上强制转换: 派生类的引用或指针转换为基类引用或指针
向下强制转换: 基类的引用或指针转换为派生类的引用或指针(不能隐式转换, 会导致不安全)

虚函数: 在基类方法的神明中使用关键字virtual可使改方法在基类以及所有派生类(包括从派生类派生出来的类)中是虚的

使用指向对象的引用或指针来调用虚方法, 程序会使用对象类型定义的方法, 而不是引用或指针类型定义的方法

#include <iostream>

using namespace std;
class A {
public:
	A(int a):m_a(a) {
		cout << "A地址:" << this << endl;
	}

	virtual void Show() {
		cout << "A::Show()" << endl;
	}
public:
	int m_a;
};

class B : public A{
public:
	B(int a, int b):A(a), m_b(b) {
		cout << "B 地址:" << this << endl;
	}
	
	virtual void Show() {
		cout << "B::Show()" << endl;
	}
public:
	int m_b;
};

void fr(A& rb) {
	rb.Show();
}

void fp(A* rb) {
	rb->Show();
}

void fv(A a) {
	a.Show();
}

int main() {
	A a(10);
	B b(10, 11);
	fr(a);		// 使用 A::Show()
	fr(b);		// 使用 B::Show()
	fp(&a);		// 使用 A::Show()
	fp(&b);		// 使用 B::Show()
	fv(a);		// 使用 A::Show()
	fv(b);		// 使用 A::Show()
}

虚函数的注意事项

虚函数内存和执行速度方面有一定成本:

  • 每个对象都会增大, 增大存储地址的空间
  • 每个类, 编译器都创建一个虚函数地址表
  • 对于每个函数调用, 都需要执行一项额外的操作, 表中查地址

构造函数
构造函数不能是虚函数, 创建派生类对象, 将先调用基类的构造函数, 在调用派生类的构造函数

析构函数
析构函数需要为虚函数, 默认静态联编, delete会调用指针类型的析构函数, 释放派生类对象中基类指向的内存, 不会释放派生类指向的内存
析构函数时虚的, 则会先调用对象析构函数先释放派生类指向的内存, 再调用基类析构函数释放基类指向的内存

友元函数
友元不能是虚函数, 友元不是类成员, 只有成员才能是虚函数, 可以用过友元函数使用虚成员函数来解决

派生类重新定义函数, 不会生成函数的两个重载版本, 会隐藏基类所有同名的类方法

  1. 重新定义继承的方法, 确保与原来原型完全相同, 如果返回类型是基类引用或指针, 则可以修改为指向派生类的引用或指针, 这种特性称为返回类型协变(convariance of return type)
  2. 基类声明被重载, 则应再派生类中重新定义所有的基类版本, 只定义一个, 则另外两个版本将被隐藏
    C++基础 虚函数

虚函数表

编译器处理虚函数是给对象添加隐藏成员, 隐藏成员中保存一个指向函数地址数组的指针, 这个数组称为虚函数表(virtual function table, vtbl)
虚函数表存储了为类进行声明的虚函数的地址
C++基础 虚函数
1. 同类对象的虚函数表

#include <iostream>

using namespace std;

class A {
public:
	virtual void func2() {
		cout << "A func2" << endl;
	}

	virtual void func1() {
		cout << "A func1" << endl;
	}

	virtual void func3() {
		cout << "A func3" << endl;
	}
};

int main(){
	A a;
	A a1;
	return 0;
}

C++基础 虚函数
会发现同一类的对象用的虚函数是相同的, 都指向一个指针

2. 继承(无虚函数覆盖)

#include <iostream>

using namespace std;

class A {
public:
	virtual void func2() {
		cout << "A func2" << endl;
	}
	
	virtual void func1() {
		cout << "A func1" << endl;
	}
	
	virtual void func3() {
		cout << "A func3" << endl;
	}
};

class B : public A {
public:
	virtual void func4() {
		cout << "B func4" << endl;
	}

	virtual void func5() {
		cout << "B func5" << endl;
	}

	virtual void func6() {
		cout << "B func6" << endl;
	}
};

int main(){
	A a;
	B b;
	return 0;
}

C++基础 虚函数

基类虚函数在子类虚函数前面, 虚函数按照声明顺序放于表中

3. 继承(虚函数覆盖)

#include <iostream>

using namespace std;

class A {
public:
	virtual void func2() {
		cout << "A func2" << endl;
	}

	virtual void func1() {
		cout << "A func1" << endl;
	}

	virtual void func3() {
		cout << "A func3" << endl;
	}
};

class B : public A {
public:
	virtual void func6() {
		cout << "B func6" << endl;
	}

	virtual void func1() {
		cout << "B func1" << endl;
	}

	virtual void func2() {
		cout << "B func3" << endl;
	}

	virtual void func7() {
		cout << "B func6" << endl;
	}
};

C++基础 虚函数
派生类的函数被放到原来基类虚函数的位置(覆盖)
虚函数表添加顺序还是先基类, 后按声明顺序添加派生类的虚函数

4. 多重继承(无虚基类, 无虚函数覆盖)

#include <iostream>

using namespace std;

class A {
public:
	virtual void func2() {
		cout << "A func2" << endl;
	}

	virtual void func1() {
		cout << "A func1" << endl;
	}

	virtual void func3() {
		cout << "A func3" << endl;
	}
};

class AA {
public:
	virtual void func2_AA() {
		cout << "AA func2" << endl;
	}

	virtual void func1_AA() {
		cout << "AA func1" << endl;
	}

	virtual void func3_AA() {
		cout << "AA func3" << endl;
	}
};

class B : public A, public AA {
public:

	virtual void func6() {
		cout << "B func6" << endl;
	}

	virtual void func4() {
		cout << "B func4" << endl;
	}

	virtual void func5() {
		cout << "B func3" << endl;
	}

	virtual void func7() {
		cout << "B func6" << endl;
	}

};

int main(){
	AA aa;
	A a;
	B b;
	return 0;
}

C++基础 虚函数
派生类有两个虚函数表, 根据继承先后排列, 派生类的虚函数添加到第一个继承基类的虚函数表中

5. 多重继承(无虚基类, 有虚函数覆盖)

#include <iostream>

using namespace std;

class A {
public:
	virtual void func2() {
		cout << "A func2" << endl;
	}

	virtual void func1() {
		cout << "A func1" << endl;
	}

	virtual void func3() {
		cout << "A func3" << endl;
	}
};

class AA {
public:
	virtual void func2_AA() {
		cout << "AA func2" << endl;
	}

	virtual void func1_AA() {
		cout << "AA func1" << endl;
	}

	virtual void func3_AA() {
		cout << "AA func3" << endl;
	}
};

class B : public A, public AA {
public:

	virtual void func1() {
		cout << "B func1" << endl;
	}

	virtual void func4() {
		cout << "B func4" << endl;
	}

	virtual void func5() {
		cout << "B func3" << endl;
	}

	virtual void func2_AA() {
		cout << "B func2" << endl;
	}

};

int main(){
	AA aa;
	A a;
	B b;
	return 0;
}

C++基础 虚函数
6. 多重继承(有虚基类, 无虚函数覆盖)

#include <iostream>

using namespace std;

class Base {
public:
	Base(int base):m_base(base){ cout << "Base地址:" << this << endl; }

	virtual void func10() {
		cout << "Base func10" << endl;
	}
public:
	int m_base;
};

class A: virtual public Base {
public:
	A(const Base& base, int a): Base(base), m_a(a) { cout << "A的地址:" << this << endl;}

	virtual void func2() {
		cout << "A func2" << endl;
	}

	virtual void func1() {
		cout << "A func1" << endl;
	}

	virtual void func3() {
		cout << "A func3" << endl;
	}
public:
	int m_a;
};

class AA: virtual public Base {
public:
	AA(const Base& base, int aa) : Base(base), m_aa(aa) { cout << "AA的地址:" << this << endl; }

	virtual void func2_AA() {
		cout << "AA func2" << endl;
	}

	virtual void func1_AA() {
		cout << "AA func1" << endl;
	}

	virtual void func3_AA() {
		cout << "AA func3" << endl;
	}
public:
	int m_aa;
};

class B : public A, public AA{
public:
	B(const Base& base, int a, int aa) : Base(base), A(base, a), AA(base, aa) { cout << "B的地址:" << this << endl; }
	
	virtual void func11() {
		cout << "B func11" << endl;
	}
public:
	int m_b;
};

int main(){
	B b(Base(2), 10, 11);
	return 0;
}

C++基础 虚函数
C++基础 虚函数
多了vbptr和vbtable
A对应vbptr指针位置 + vbtable[0][0] = A对应vfptr的指针位置(4 + -4 = 0)
A对应vbptr指针位置 + vbtable[0][1] = Base对应vfptr的指针位置(4 + 24 = 28)文章来源地址https://www.toymoban.com/news/detail-428985.html

	int** t = (int**)(int*)(((int*)&b + 1) + 0); // 指向A的vbtable
	cout << t[0][0] << endl;					 // -4
	
	int** baseFun = (int**)((int*)&b + 7);  // 指向base的vbptr
	cout << baseFun[0][0] << endl;          // 7607512
	pFun = (Fun)(baseFun[0][0]);			// 指向Base::func10()函数的地址
	pFun();									// Base func10

到了这里,关于C++基础 虚函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包