虚函数:虚函数相当于函数指针,占用四个字节(对于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(); }