系列文章目录
前言
一、虚函数和虚函数表
虚函数创建方式:在类函数前加上关键字virtual即可
例:
class A
{
public:
virtual void ppd(){}//虚函数
void ppee() {}//普通函数
protected:
};
虚函数对于类的影响:
当类中存在虚函数类的类存会变为4个字节;当类中只存在普通函数函数时类的大小为1个字节; 注意:不管有多少个虚函数都是站4个字节;因为存在类中是一个指向虚函数表的指针
因为虚函数的这种存储方式所以可以通过函数指针的方式来访问虚函数
例:
class A
{
public:
virtual void ppd1() { cout << "ppd1"<<endl; }
virtual void ppd2() { cout << "ppd2" << endl; }
virtual void ppd3() { cout << "ppd3" << endl; }
protected:
};
int main()
{
A a;
int** pp = (int**)&a;
typedef void (*po)();
po i = (po)pp[0][0];
i();//此处输出ppd1
i= (po)pp[0][1];
i();//此处输出ppd2
i=(po)pp[0][2];
i();//此处输出ppd3
return 0;
}
虽说是可以这样访问但一般不 支持这样写;可以拿来装13,但实用性不强;
二、虚函数和多态
多态定义: 同一种行为(调用)导致的不同的结果
多态的必要性原则
-
必须父类存在虚函数
-
子类必须采用public继承
-
必须存在指针的引用(使用)
这里用代码举几个例子:
class A
{
public:
virtual void ppd1() { cout << "Appd1"<<endl; }
virtual void ppd2() { cout << "Appd2" << endl; }
virtual void ppd3() { cout << "Appd3" << endl; }
protected:
};
class B :public A
{public:
void ppd1() { cout << "Bppd1" << endl; }
void ppd2() { cout << "Bppd2" << endl; }
void ppd3() { cout << "Bppd3" << endl; }
protected:};
int main()
{
//一般的调用无法看出区别
B b;
A a;
b.ppd2();//调用的为B中的ppd1函数
a.ppd2();//调用的为A中的ppd1函数
//一定要使用子类对象初始化父类对象时才会看出区别
A* c = new B;
c->ppd1();//注意这里调用的是B中的函数
return 0;
}
就是这虚函数的这种性质造就了多态;我可以通过这种性质打造模板、
例如:
class A
{
public:
virtual void ppd1() { cout << "Appd1"<<endl; }
virtual void ppd2() { cout << "Appd2" << endl; }
virtual void ppd3() { cout << "Appd3" << endl; }
protected:
};
class B :public A
{public:
void ppd1() { cout << "Bppd1" << endl; }
void ppd2() { cout << "Bppd2" << endl; }
void ppd3() { cout << "Bppd3" << endl; }
protected:};
class C :public A
{
public:
void ppd1() { cout << "新加入的" << endl;}
protected:
};
void xiaog(A* i)//接口函数
{
i->ppd1();
i->ppd2();
i->ppd3();
}
int main()
{
xiaog(new C);
xiaog(new B);
return 0;
}
这样可以方便我们在后续的更新,如果没有采用虚函数,在更行功能时避免不了要修改类,但采用了虚函数后就可以通过增加代码的方式来增加新功能例如增加一个新的类;
三、纯虚函数和ADT
纯虚函数也是虚函数但是纯虚函数没有函数体
virtual void prin()=0;
ADT也叫抽象类;
抽象类: 具有至少一个纯虚函数的类,叫做抽象类
-
抽象类不能构建对象
-
抽象类可以构建对象指针
注意:当抽象类被继承后没有被重写那么子类也会变成抽象类;
class A
{public:
virtual void aaa()=0;
protected:
};
class B:public A
{public:
protected:
};
class C:public B
{public:
protected:
};
以上代码中的B类与C类因为都没有重写aaa函数所以B、C都为抽象类;
在有了抽象类之后就可以更好的修建模板函数,因为抽象类无法创建对象所以更适合制作模板类;制作好模板类后可以交给其他人根据模板类来书写出需要的功能;
模板例子:
class mom
{public:
virtual void name() = 0;
virtual void lian() = 0;
virtual void snan() = 0;
protected:
};
四、虚析构函数
当使用子类初始化父类时,你如果需要调用子类中的析构函数,就是需要使用虚析构函数;
例:
class A
{
public:
virtual void ppd1() { cout << "Appd1"<<endl; }
virtual void ppd2() { cout << "Appd2" << endl; }
virtual void ppd3() { cout << "Appd3" << endl; }
virtual ~A()//如果在这里未使用虚析构函数,那么子类中的析构函数就不会被调用;
{
cout << "AA" << endl;
}
protected:
};
class B :public A
{public:
void ppd1() { cout << "Bppd1" << endl; }
void ppd2() { cout << "Bppd2" << endl; }
void ppd3() { cout << "Bppd3" << endl; }
~B()
{
cout << "BB" << endl;
}
protected:};
int main()
{
A *a=new B;
delete a;//注意在这里父类与子类的析构函数都会被调用
return 0;
}
五、C++类型转换
c++为我们提供了传闻中变安全了的类型转化方式;
-
const_cast //可以用来专门作const属性的类型转换;
使用方法
const int A;
int B=const_cast<int&>(A); //<>中必须为指针类型或引用类型;
-
static_cast //可以将父类的指针转换为子类的;//其他的功能就是隐式转换都能作到的它也能作;
int main()
{
//不使用static_cast
int *u=new int(10);
void *e = u;
//使用static_cast
int* ui = new int(20);
void* y = static_cast<void*>(ui);
//A、B为两个类A是B的父类;
A* a=new A;
B* b =static_cast<B*>(a);//我认为唯一还有点用的地方
return 0;
}
-
dynamic_cast //基本与static_cast相同,不同住处在于父类的指针转换为子类时调用子类中有但父类中没有的函数时会报错;static_cas不会他会继续调用;
-
reinterpret_cast //用来给数据转换类型
int i = 0x00636261;
char* ii = reinterpret_cast<char*>(&i);
总结
谢谢你可以看完,也希望你能给我一个大大的点赞谢谢