C++学习总结(二十)——虚函数,虚函数的重载特性,纯虚函数抽象类,虚函数表,异质链表

虚函数:虚函数相当于函数指针,占用四个字节(对于32位),在类中虚函数占用四个字节,其成员函数不占类的内存。

基类定义虚函数,优先调用子类中的同名函数,覆盖虚函数。基类指针访问不同派生对象,调用不同方法。

注意:1.虚函数必须是类的成员函数

         2.不能将友元函数说明为虚函数,但是虚函数可以是另一个类的友元。

         3.析构函数可以是虚函数,但是构造函数不能是虚函数。

#include<iostream>
using namespace std;
class base1
{
public:
	virtual int run() = 0;
	virtual double go() = 0;
};

class base2
{
public:
	virtual void run()
	{

	}
	virtual void go()
	{

	}
};

void main()
{
	cout << sizeof(base1)<<endl;
	cout << sizeof(base2) << endl;


	cin.get();

}

当父类对象指针指向派生类对象时,析构时会自动调用子类析构函数,

#include<iostream>
class base
{
public:
	 virtual void name()	//虚函数占据四个字节的地址
	{
		std::cout << "base" << std::endl;
		std::cout << "x=" << x << std::endl;
	}
	int x;
	base(int a) :x(a)
	{

	}
};
class zi :
	public base
{
	public:
		void name()
		{
			std::cout << "zi" << std::endl;
			std::cout << "x=" << x << "y=" << y<< std::endl;
		}

		int y;
		zi(int a,int b) :base(a),y(b)
		{

		}
};
class sun :
	public zi
{
public:
	void name()
	{
		std::cout << "sun" << std::endl;
		std::cout << "x=" << x << "y=" << y << "z=" << z << std::endl;
	}
	int z;
	sun(int a,int b,int c) :zi(a,b),z(c)
	{

	}
};
//父类指针的迁移
void main()
{
	base p1(1);
	zi p2(2,3);
	sun p3(4,5,6);
	base *p;

	p = &p1;  //访问父类成员数据
	p->name(); //基类是虚函数的首先访问派生类的函数

	p = &p2; //访问子类成员继承的父类成员的数据
	p->name();

	p = &p3;//访问孙类成员继承的父类成员的数据
	p->name();

	std::cout << sizeof(base) << std::endl;

	std::cin.get();



}
void main1()
{
	base *pb = new base(1);
	pb->name();

	zi *pz = new zi(2,3);
	pz->name();

	sun *ps = new sun(4,5,6);
	ps->name();
 
	zi *p =static_cast<zi *>(pb) ;
	p->name();


	std::cin.get();
}

析构函数是虚函数时,会自动调用派生类和基类的析构函数。

#include<iostream>

class my
{
public:
	//virtual  构造函数不能是虚函数 
	my()
	{
		std::cout << "my creat" << std::endl;
	}
	virtual
		~my()  //析构函数是虚函数时,派生类在析构时会自动调用派生类的析构函数,析构派生类和基类
	{
		std::cout << "my delete" << std::endl;
	}
};

class myzi :public my
{
public:
	myzi()
	{
		std::cout << "myzi creat" << std::endl;
	}
	~myzi()
	{
		std::cout << "myzi delete" << std::endl;
	}

};
void run()
{
	/*my *p = new my;
	delete p;*/

	my *p1 = new myzi;
	delete p1;
}
void main3()
{
	run();
	std::cout << "hello world!" << std::endl;

	std::cin.get();
}

虚函数重载特性:

1.派生类中的任何函数属性都应该与基类中的相同。

2.返回类型应该相同,否则则认为错误重载。

3.原型不同,仅函数名相同,丢失虚函数特性。

#include<iostream>

class A
{
public:
	virtual int run()
	{
		return 1;
	}
	virtual int go()
	{
		return 0;
	}

};

class B :public A
{
public:
     virtual int run()
	{
		return 2;
	}
	 //void go()
	 //{

	// }

};

void main4()
{
	B c;
	std::cout<<c.A::run()<<std::endl;

	std::cin.get();
}

纯虚函数函数抽象类。

1.纯虚函数,在基类中说明虚函数,但是在基类中没有定义,要求任何派生类都定义自己的版本。

2.纯函数作为派生类的一个公共界面。

3.具有纯虚函数的基类抽象类。抽象类没有办法进行实例化,通过派生进行实例化。

4.抽象类不可以用于函数的参数以及返回值类型,但是抽象类指针可以。

5.虚函数表位于编译器的代码区中,虚函数指针指向虚函数表,一个或者多个虚函数都占用四个字节。

#include<iostream>

//抽象类
class A
{
public:
	int a;
	virtual void show() = 0;
	virtual void go() = 0;//纯虚函数

};

class B :public A
{
public:
	int num;
	void show()
	{
		std::cout << "B show()" << std::endl;
	}
	void go()
	{
		std::cout << "B go()" << std::endl;
	}
};

A *test()
{
	A *p(nullptr);
	return p;
}
void main5()
{
	B c;
	//A aa;//抽象类不能实例化对象,但是可以实例化指针;
	A *p;
	c.go();
	c.show();

	std::cin.get();
}

异质链表:利用基类可以存储派生类指针的特点,用基类指针接收派生类的地址。用基类指针生成一个连接不同派生类对象的动态链表,每个结点指针,指向类层次中不同的派生类对象。结点类型不同的链表。

#include<iostream>
#include<Windows.h>
#include<stdlib.h>
using namespace std;
class base
{
public:
	virtual void show() = 0;
};

class linknode
{
public:
	base *p; //数据域
	linknode *pnext; //指针域
};

class A :public base
{  
public:
	void show()
	{
		cout << "class A\n";
	}

};

class B :public base
{
public:
	void show()
	{
		cout << "class B\n";
	}
};

class C :public base
{
public:
	void show()
	{
		cout << "class C\n";
	}
};
class D:public base
{
public:
	void show()
	{
		cout << "class D\n";
	}
};
void showall(linknode *phead)
{
	while (phead != NULL)
	{
		phead->p->show();
		phead = phead->pnext;
	}
}
void add(linknode *phead, base *p)
{
	linknode *ptemp = phead;
	if (ptemp == NULL)
	{
		ptemp->p = p;
		ptemp->pnext = NULL;
	}
	else
	{
		while (ptemp->pnext != NULL)
		{
			ptemp = ptemp->pnext;
		}
		linknode *padd = new linknode; //新建一个节点,然后插入到链表的尾部
		padd->p = p;
		padd->pnext = NULL;

		ptemp->pnext = padd; //让上一节点指向新节点
	}
	
}
void main()
{
	linknode *phead;
	linknode node1, node2, node3;
	A a1,a2,a3;
	B b1,b2,b3;
	C c1,c2,c3;
	D d1;
	phead = &node1;	     //指针指向
	node1.pnext = &node2;
	node2.pnext = &node3;
	node3.pnext = NULL;

	node1.p = &a1;	    //结点存储的数据
	node2.p = &b1;
	node3.p = &c1;



	showall(phead);
	add(phead,&d1);
	showall(phead);

	cin.get();
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值