手撕虚函数,虚函数调用虚函数

本文深入探讨了C++中虚函数的工作原理,包括虚函数表的使用,如何通过虚函数表指针访问虚函数,以及构造函数、析构函数和虚函数调用虚函数的具体行为。通过实例代码展示了虚函数在继承中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 虚函数

一个类的内存分布,虚函数表指针,普通成员变量,sizeof(A) = 为一个虚表指针(32位是4字节)+ 普通成员变量(静态什么的都不是)。虚函数本身也是函数,相当于一个指针,虚函数表里面含有虚函数,相当于一个二级指针,虚函数表指针指向虚函数表,相当于一个三级指针,所有可以通过一个三级指针直接访问某个虚函数。见代码:

#include <iostream>
using namespace std;

class A {
private:
	int m_data;
public:
	A(int _data = 5) : m_data(_data){
		//...
	}
	virtual void f() {
		cout << "f()" << endl;
	}
	virtual void g() {
		cout << "g()" << endl;
	}
	virtual void h() {
		cout << "h()" << endl;
	}
	~A() {	
		//...
	}
};

void test1() {
	A a;	//a.m_data默认位5
	//类的小大
	cout << "sizeof(A) = "<<sizeof(A) << endl;
	//虚表指针和一个int大小

	//强行访问数据成员
	int* pdata = (int*)(&a)+1;	
	int* pdata2 = (int*)((char*)(&a)+4);//这也行,char+4等同于int+1,都是4字节
	//a的地址也是虚函数表的地址,需要向后移动4字节,强转成int*型,然后+1,
	//则向后进行4个字节(所有指针都是4字节,指针类型决定其寻址大小)
	cout << "a.m_data的位置 = " << (int)pdata << endl;//将地址值强转为int,方便查看
	cout << "*pdata = " << *pdata << endl;
	cout << "*pdata2 = " << *pdata2 << endl;
	//此时打印值为5,即a.m_data的值,尽管m_data为private
	
	//虚函数相当于一级指针(等同于普通函数)
	//虚函数表相当于二级指针(通过虚函数表可以寻址虚函数,相当于虚函数的指针)
	//虚函数表指针就是三级指针(虚表的指针,再加一个*)
	void*** pvtable = (void***)&a;
	cout << "pvtable = " << (int)&a << endl;//将地址值强转为int,方便查看
	//虚表地址和a_mdata的地址相差4字节

	//强行访问各个虚函数
	//虚函数类型为void(*pf)()型
	void(*pf1)() = (void(*)())(**((void***)&a));
	//((void***)&a)		虚表指针
	//*((void***)&a)	虚函数表
	//**((void***)&a)	虚函数
	//(void(*)())		函数类型,类型强转
	//void(*pf)()		函数指针去接住
	pf1();		//调用的是f()函数
	//强行访问第二个虚函数
	void(*pf2)() = (void(*)())(*(*((void***)&a) + 1));
	//(*((void***)&a) + 1)		虚函数表的第一个元素,类比数组
	pf2();		//调用的是g()函数
	void(*pf3)() = (void(*)())(*(*((void***)&a) + 2));
	pf3();		//调用的是h()函数
}

int main() {
	test1();
	//system("pause");
	return 0;
}

2 构造函数调用虚函数

直接上代码:

class Father {
private:
	int m_data;
public:
	Father(int _data = 1) : m_data(_data) {
		func1();
	}
	virtual void func1() {
		cout << "Father::func1() m_data = "<< m_data << endl;
	}
};

class Son : public Father {
private:
	int m_data;
public:
	Son(int _data = 2) : m_data(_data) {
		func1();
	}
	void func1() override {
		cout << "Son::func1()" << endl;
	}
};

void test2() {
	Son s;
	s.func1();
}

打印内容为:

Father::func1() m_data = 1
Son::func1() m_data = 2
******
Father::func1() m_data = 1
******
Son::func1() m_data = 2

子类构造之前,先调用父类构造,就m_data这个值,先找自己函数的作用域,再找其他作用域,有虚成员函数,但是没有虚成员变量之说,若有虚成员变量,猜测m_data都为2。

3 析构函数调用虚函数

 

class Father {
private:
	int m_data;
public:
	Father(int _data = 1) : m_data(_data) {
		//func1(); //构造调用其他函数注释掉
	}
	~Father() {
		func1();
	}
	virtual void func1() {
		cout << "Father::func1() m_data = "<< m_data << endl;
	}

};

class Son : public Father {
private:
	int m_data;
public:
	Son(int _data = 2) : m_data(_data) {
		//func1();	//构造调用其他函数注释掉
	}
	~Son() {
		func1();
	}
	void func1() override {
		cout << "Son::func1() m_data = "<<m_data << endl;
	}
};

void test3() {
	Son s;
}

先析构子类,再析构父类。

4 虚函数调用虚函数

class Father {
private:
	int m_data;
public:
	Father(int _data = 1) : m_data(_data) {
		//func1(); //构造调用其他函数注释掉
	}
	~Father() {
		//func1();
	}
	virtual void func1() {
		cout << "Father::func1() m_data = "<< m_data << endl;
	}
	virtual void func2() {
		cout << "Father::func2() m_data = " << m_data << endl;
		func1();
	}

};

class Son : public Father {
private:
	int m_data;
public:
	Son(int _data = 2) : m_data(_data) {
		//func1();	//构造调用其他函数注释掉
	}
	~Son() {
		//func1();
	}
	void func1() override {
		cout << "Son::func1() m_data = "<<m_data << endl;
	}
	void func2() override {
		cout << "Son::func2() m_data = " << m_data << endl;
		func1();
	}
};

void test4() {
	Son s;
	s.Father::func2();
}

输出结果:

Father::func2() m_data = 1
Son::func1() m_data = 2

s.Father::func2();调用的是父类func2,而父类func2调用func1确实Son::func1(),成员函数传参的时候,默认有一个指向调用该函数的对象指针,所以Father::func2()尽管调用了,但是fun2()调用func1()时,传入参数是子类对象s,所以调用的是子类的S::func1();

转载请注明出处,谢谢

### 动实现 DeepSeek 函数的概念解析 DeepSeek 是一种高级的人工智能工具,旨在辅助程序员自动生成代码片段。然而,要完全从零构建一个像 DeepSeek 这样的复杂系统是非常具有挑战性的任务,因为其背后涉及大量的机器学习算法、自然语言处理技术以及深度神经网络结构。 尽管如此,为了理解这一过程的核心原理,下面将展示简化版的 `deepseek` 函数概念性实现方式。此版本仅用于教育目的,并不包含完整的功能特性: #### Python 中的动实现示例 ```python import random from typing import List, Dict class SimpleCodeGenerator: def __init__(self): self.templates: Dict[str, str] = { "for_loop": "for i in range({n}):", "if_statement": "if {condition}:" } def generate_code(self, description: str) -> str: """基于简单的模板匹配来生成代码""" if '循环' in description or '遍历' in description: n = random.randint(1, 10) return self.templates["for_loop"].format(n=n) elif '条件判断' in description: condition = ["x > y", "a == b"][random.choice([True, False])] return self.templates["if_statement"].format(condition=condition) else: raise ValueError("无法识别的需求描述") def deepseek(description: str) -> str: generator = SimpleCodeGenerator() try: code_snippet = generator.generate_code(description) print(f"Generated Code Snippet:\n{code_snippet}") return code_snippet except Exception as e: print(e) return "" # 测试用例 description_examples = [ "创建一个带有终止条件的简单循环", "执行一次基本的条件判断操作" ] for desc in description_examples: result = deepseek(desc)[^1] ``` 上述代码展示了如何定义一个非常基础的代码生成功能——它可以根据给定的文字提示(如“创建一个带有终止条件的简单循环”),尝试返回一段对应的Python代码片段。请注意,实际中的 DeepSeek 工具远比这里所呈现的例子更为强大和智能化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值