c++学习总结(2021/5/16)

本文深入探讨C++中的多态性,包括如何利用虚函数实现动态绑定,详细解析虚函数表的结构与作用。同时,介绍了final和override关键字在限制函数重写和明确重写标识上的应用,以及析构函数为虚函数的必要性,确保正确释放子类资源。此外,还讨论了纯虚函数在抽象类中的使用,以实现接口定义。

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

1.多态

使用原因

有两个类继承于同一个类,其中一个对象不再使用,需要切换到另一个对象时,可以采用多态

【例】代码:

#include<iostream>

using namespace std;

class Father
{
public:
	void play() {
		cout << "KTV唱歌" << endl;
	}
};

class Son : public Father
{
public:
	void play() {
		cout << "玩吃鸡" << endl;
	}
};

void party(Father **f, int n) {
	for (int i = 0; i < n; i++) {
		f[i]->play();
	}
}

int main() {
	Father f;
	Son s1, s2;
	Father *fy[] = { &f, &s1, &s2 };

	party(fy,sizeof(fy)/sizeof(fy[0]));

	return 0;
}

代码中,父类对象指向子类地址,调用的是父类的方法,没有调用子类中重写的方法

解决方法:virtual void play();
在父类中方法声明前加上virtual 关键词,表示为虚函数,在父类对象指向子类地址时,父类对象调用的方法就为子类中重写的方法

2.虚函数表

1)单个类的虚函数表

类中如果有虚函数,类中会存放一个指针来指向虚函数表中的第一个虚函数,对象的内存分布就是存放一个指向虚函数表的指针和成员数据

#include <iostream>

using namespace std;

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

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

	void func4() {
		cout << "Father::func4" << endl;
	}

public:
	int x = 10;
	int y = 12;
	static int n;
};

typedef void (*fun_def)(void);

int main(void) {
//	父亲类
	Father father;

	cout << "sizeof father: " << sizeof(father) << endl;

//	cout << "对象地址: " << (int*)&father << endl;
	cout << "对象地址: " << &father << endl;

	int *vcpt = (int*) *((int *)&father);		//以16进制取出地址,解引地址,然后强制类型转换为int*

	cout << "调用第1个虚函数: ";
	((fun_def) *(vcpt + 0))();

	cout << "调用第2个虚函数: ";
	((fun_def) *(vcpt + 1))();

	cout << "调用第3个虚函数: ";
	((fun_def) *(vcpt + 2))();

	cout << "第1个数据成员的地址: " << endl;
	cout << &father.x << endl;
	cout << std::hex << (int)&father + 4 << endl;
	cout << "第1个数据成员的值: " << endl;
	cout << std::dec << father.x << endl;
	cout << *(int*)((int)&father + 4) << endl;		//将地址转换为16进制,加4后,转为为int* 指针类型,再解引

	cout << "第2个数据成员的地址: " << endl;
	cout << &father.y << endl;
	cout << std::hex << (int)&father + 8 << endl;		//以16进制输出内容
	cout << "第2个数据成员的值: " << endl;
	cout << std::dec << father.y << endl;		//以十进制打印内容
	cout << *(int*)((int)&father + 8) << endl;
	
	return 0;
}

2)继承的虚函数表

当一个类中有虚函数,且有子类继承这个类时,子类就会创建属于他自己的一个虚函数表,这个虚函数表通过复制父类的虚函数表,若子类有重写或增加虚函数,则在复制后的虚函数表中进行修改或增加

#include <iostream>

using namespace std;

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

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

	void func4() {
		cout << "Father::func4" << endl;
	}

public:
	int x = 10;
	int y = 12;
	static int n;
};

class Son : public Father{
public:
	void func1() {
		cout << "Son::func1" << endl;
	}
	
	virtual void func5() {
		cout << "Son::func5" << endl;
	}
};

typedef void (*fun_def)(void);

int main(void) {
	//儿子类,继承于父亲类的内容
	Son son;
	int *vct = (int*) *(int*)&son;

	for (int i = 0; i < 4; i++) {
		cout << "调用第" << i + 1 << "虚拟函数: ";
		((fun_def)*(vct+i))();
	}
	cout << endl;
	
	for (int i = 0; i < 2; i++) {
		cout << "第" << i + 1 << "数据成员的值: " << *((int*)((int)&son + 4*(i+1))) << endl;
	}
	
	return 0;
}

3)多重继承的虚函数表

当有两个类中都有虚函数,子类继承这两个类时,子类就会创建属于他自己的两个个虚函数表,通过复制两个父类的虚函数表,若子类分别重写了两个父类中的虚函数,并增加了属于自己的虚函数,那会先在第一个复制的虚函数表中修改重写了的第一个父类中的虚函数,再在表中创建自己新增的虚函数,再在第二个复制的虚函数表中修改重写了的第二个父类中的虚函数

#include <iostream>

using namespace std;

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

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

	void func4() {
		cout << "Father::func4" << endl;
	}

public:
	int x = 10;
	int y = 12;
	static int n;
};

class Mother
{
public:
	virtual void handel1() {
		cout << "Mother::handel1" << endl;
	}

	virtual void handel2() {
		cout << "Mother::handel2" << endl;
	}

	virtual void handel3() {
		cout << "Mother::handel3" << endl;
	}

public:
	int m = 100;
	int n = 120;
};

class Son : public Father, public Mother{
public:
	void func1() {
		cout << "Son::func1" << endl;
	}

	virtual void handel1() {
		cout << "Son::handel1" << endl;
	}

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

typedef void (*fun_def)(void);

int main(void) {
	//儿子类,继承于父亲类的内容
	Son son;
	int *vct = (int*) *(int*)&son;

	for (int i = 0; i < 4; i++) {
		cout << "调用第" << i + 1 << "虚拟函数: ";
		((fun_def)*(vct+i))();
	}
	cout << endl;
	
	for (int i = 0; i < 2; i++) {
		cout << "第" << i + 1 << "数据成员的值: " << *((int*)((int)&son + 4*(i+1))) << endl;
	}

	//儿子类,继承于母亲类的内容
//	int *vct2 = (int*) *((int*)&son + 3);				//转换成指针类型往前走3步
	int *vct2 = (int*)*((int*)((int)&son + 12));		//转换成整数类型加12个字节

	for (int i = 0; i < 3; i++) {
		cout << "调用第" << i + 1 << "虚拟函数: ";
		((fun_def)*(vct2 + i))();
	}
	cout << endl;

	for (int i = 0; i < 2; i++) {
		cout << "第" << i + 1 << "数据成员的值: " << *((int*)((int)&son + 16 + (i*4))) << endl;
	}

	return 0;
}

3.final关键词

1)修饰类

这个类将不能被继承

//father不能被继承
class Father final{
	//...
	//...	
};

2)修饰方法

final只能用来修饰virtual 方法(虚函数),用final修饰的虚函数将不能被子类重写

class Father{
public:
	virtual void play() final;
	//...
	//...	
};

4.override关键词

只能修饰虚函数,修饰子类中重写父类的方法,在函数声明时使用
作用:
1)提示程序员,这个是重写父类的方法
2)在写函数名时,若函数名输入不是父类中拥有的,编译器将会报错

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

class Son : public Father{
public:
	void func1() override;

};

5.析构函数的虚函数

当父类中存在内存的申请与释放,子类中也存在内存的申请与释放时,使用父类对象指向子类地址,然后释放父类对象,会导致子类的析构函数没有调用,没有释放子类中申请的内存。
解决方法:在父类中的析构函数前加上virtual

#include <iostream>
#include <string>

using namespace std;

class Faher 
{
public:
	Faher(const char *addr = "中国") {
		cout << "调用父亲的构造函数" << endl;

		int len = strlen(addr) + 1;
		this->addr = new char[len];
		strcpy_s(this->addr, len, addr);
	};
	virtual ~Faher() {
		cout << "调用父类的析构函数" << endl;
		if (addr) {
			delete addr;
			addr = NULL;
		}
	};

private:
	char *addr;

};

class Son : public Faher
{
public:
	Son(const char *game = "斗地主", const char *addr = "中国"):Faher(addr) {
		cout << "调用儿子的构造函数" << endl;
		int len = strlen(game) + 1;
		this->game = new char[len];
		strcpy_s(this->game, len, game);
	};
	~Son() {
		cout << "调用儿子的析构函数" << endl;
		if (game) {
			delete game;
			game = NULL;
		}
	};

private:
	char *game;
};


int main() {
	cout << "-------1--------" << endl;
	Faher *f = new Faher();
	delete f;

	cout << "-------2--------" << endl;
	Son *s = new Son();
	delete s;

	cout << "-------3--------" << endl;
	f = new Son();
	delete f;

	return 0;
}

6.纯虚函数

当一个类只需要提供方法,不需要对方法做具体实现,在他的子类中做具体实现。这时可以使用纯虚函数,使用了纯虚函数得到类就是抽象类,抽象类不能创建对象

#include <iostream>
#include <string>

using namespace std;

class Animal {
public:
	int getWeight();
	virtual void getColor() = 0;		//定义为纯虚函数,当前类变为抽象类

private:
	int weight;
	string color;
};

class Dog : public Animal {
public:
	/*virtual(可加或不加)*/ void getColor() {
		cout << "dog's color is black" << endl;
	}

};

int main() {
//	Animal a;		//无法创建抽象类对象

	Dog dog;
	dog.getColor();

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值