虚函数:虚函数相当于函数指针,占用四个字节(对于32位),在类中虚函数占用四个字节,其成员函数不占类的内存。
基类定义虚函数,优先调用子类中的同名函数,覆盖虚函数。基类指针访问不同派生对象,调用不同方法。
注意:1.虚函数必须是类的成员函数
2.不能将友元函数说明为虚函数,但是虚函数可以是另一个类的友元。
3.析构函数可以是虚函数,但是构造函数不能是虚函数。
#include
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)<
当父类对象指针指向派生类对象时,析构时会自动调用子类析构函数,
#include
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(pb) ;
p->name();
std::cin.get();
}
析构函数是虚函数时,会自动调用派生类和基类的析构函数。
#include
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
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<
纯虚函数函数抽象类。
1.纯虚函数,在基类中说明虚函数,但是在基类中没有定义,要求任何派生类都定义自己的版本。
2.纯函数作为派生类的一个公共界面。
3.具有纯虚函数的基类抽象类。抽象类没有办法进行实例化,通过派生进行实例化。
4.抽象类不可以用于函数的参数以及返回值类型,但是抽象类指针可以。
5.虚函数表位于编译器的代码区中,虚函数指针指向虚函数表,一个或者多个虚函数都占用四个字节。
#include
//抽象类
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
#include
#include
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();
}