1.虚函数
? 用virtual修饰的成员函数叫做虚函数
? 虚函数对类的影响是增加了一个指针的内存,也就是占了4个字节
? 不管有几个虚函数,都只占一个指针的内存大小
? 在类里 如无任何数据的情况下 类大小为1个字节(用于标识为类)
? 类中出现一个虚函数的时候(为4个字节)
#include<iostream>
using namespace std;
class Fox
{
public:
protected:
};
class Fox2
{
public:
virtual void print1()
{
cout << "虚函数 " << endl;
}
protected:
};
int main()
{
cout << sizeof(Fox) << endl;//占1个字节
cout << sizeof(Fox2) << endl;//4个字节
while (1);
return 0;
}
虚函数表: 就是一个指针存储所有虚函数的首地址
简单来说就是我们一个类中无论存在多少个虚函数,它的大小并未改变,也就是说
我们可以理解为虚函数为指针调用,类似于C语言的函数指针,指向了这个虚函数
调用了这个虚函数。
#include<iostream>
using namespace std;
class Fox
{
public:
protected:
};
class Fox2
{
public:
virtual void print1()
{
cout << "虚函数 " << endl;
}
virtual void print2()
{
cout << "虚函数2" << endl;
}
protected:
};
int main()
{
cout << sizeof(Fox) << endl;//占1个字节
cout << sizeof(Fox2) << endl;//4个字节
Fox2 fox;
int** pfox = (int**) &fox;
typedef void(*PF)();
PF pfunc = (PF) pfox[0][0];
pfunc();//调用虚函数1
pfunc= (PF)pfox[0][1];
pfunc();//调用虚函数2
while (1);
return 0;
}
2.虚函数和多态
?多态 同一种行为(调用)产生的不同结果,我们称之为多态
?多态必要原则:父类中必须存在虚函数
?子类必须采用公有继承的方式,必须存在指针类的使用
#include<iostream>
#include<string>
using namespace std;
class Fox
{
public:Fox(string name) :name(name) {};
virtual void coutname()//我们称成员函数为行为
{
cout << name << endl;
}
protected:
string name;
};
class FoxSon:public Fox
{
public:FoxSon(string Sname = "",string Fname="") :name(Sname), Fox(Fname) {};
void coutname() //不用再加入关键字 virtual
{
cout << name << endl;
}
private:
string name;
};
class Fox3 :public Fox
{
public:Fox3(string Sname = "", string Fname = "") :name(Sname), Fox(Fname) {};
void coutname() //不用再加入关键字 virtual
{
cout << name << endl;
}
private:
string name;
};
class PrintFoxName //打印狐狸 类
{
public:
void printName(Fox* parent)
{
parent->coutname();
}
};
int main()
{
//就近原则访问
Fox fox("大狐狸");
FoxSon foxson("小狐狸");
fox.coutname();//访问自己的
foxson.coutname();//访问自己的
//指针类型访问:依旧是就近原则
Fox* Pfxo = &fox;
Pfxo->coutname();
FoxSon* Pfoxsom = &foxson;
Pfoxsom->coutname();
//非正常赋值 子类对象初始化父类指针,换句话说 父类指针指向为子类对象
Pfxo = &fox;
Pfxo->coutname();
Pfxo= &foxson;
Pfxo->coutname();
//我们会发现相同行为,结果却是不一样的,这就是我们说的多态,同样的调用行为,结果却是不一样的
//多态的目的是用于统一接口,也就是说在程序需求改变的情况下,尽可能添加代码,而不是去修改原来已经写好的功能
//例子,比如我要打印不同狐狸的名字:我只需要传入一个狐狸的对象进去,就可以实现多态调用
PrintFoxName test;
Fox3 fox3("狐狸三号");
test.printName(&fox3);
test.printName(&foxson);
test.printName(&fox);
//通过传入的对象不同 实现多态行为
}
3.纯虚函数和?ADT? ??
纯虚函数也是虚函数的一种,就是没有函数体的虚函数我们成为纯虚函数
抽象类:无法构建对象,却能构建对象指针
纯虚函数没有被重写,无论被继承多少次 都是纯虚函数,虚函数无论被继承多少次都是虚函数
子类想要创建对象,必须重写父类的纯虚函数 ADT: 具有强迫性,所有子类重写函数必须和父类的一模一样
可以理解为你需要统一使用别人实现类留下的一个接口,你就必须自己重写该纯虚函数
#include<iostream>
#include<string>
using namespace std;
class Fox //没法构造对象 我们称之为抽象类
{
public:
virtual void Coutname() = 0;//纯虚函数
protected:
};
class Foxson:public Fox
{
public:
void Coutname() //重写了纯虚函数
{
cout <<"狐狸1" << endl;
}
protected:
};
class Foxsontow :public Fox
{
public:
void Coutname()
{
cout << "狐狸2" << endl;
}
};
int main()
{
Foxson son;
Foxsontow sontow;//子类必须重写了父类纯虚函数,子类才能构建对象
Fox* pfox = nullptr;
pfox = &son;
pfox->Coutname();
pfox = &sontow;
pfox->Coutname();
}
4.虚析构函数?
? 只存在虚析构函数,不存在虚构造函数
?应用于当继承了子类对象初始化父类指针的情况下
?也就是说用当我们析构父类时,是不会调用子类的析构函数,导致内存泄漏的问题
所以我们需要用虚析构函数避免此类问题出现
没有虚析构函数代码示例
#include<iostream>
#include<string>
using namespace std;
class Fox
{
public:
void Mallocm()
{
num = new int(100);
}
~Fox()
{
cout << "父类析构" << endl;
delete num;
}
protected:
int* num;
};
class Foxson:public Fox
{
public:
void Smallocm()
{
sonnum = new int(100);
}
~Foxson()
{
cout << "子类析构" << endl;
delete num;
}
protected:
int* sonnum;
};
int main()
{
Fox* pfox = new(Foxson);
delete pfox;//此处只会调用父类析构,会有内存泄露问题
while (1);
}
?有虚析构函数代码示例
#include<iostream>
#include<string>
using namespace std;
class Fox
{
public:
void Mallocm()
{
num = new int(100);
}
virtual ~Fox()
{
cout << "父类析构" << endl;
}
protected:
int* num;
};
class Foxson:public Fox
{
public:
void Smallocm()
{
sonnum = new int(100);
}
~Foxson()
{
cout << "子类析构" << endl;
delete num;
}
protected:
int* sonnum;
};
int main()
{
Fox* pfox = new(Foxson);
delete pfox; // virtual ~Fox() 增加父类的虚析构函数,当父类指针消亡时,自动调用子类析构函数
//避免出现内存泄露问题
while (1);
}
C++类型转换
在C++里面有一种概念 就是专人做专事,传闻C++使用类型转换更加的安全
const_cast? ? ?用于去除常属性 或者增加常属性
#include<iostream>
using namespace std;
class Str
{
public://Str(const char *str):str(str) {}
Str(const char* str) :str(const_cast<char*>(str)) {}
void print()
{
cout << str << endl;
}
protected:
char* str;
};
class Num
{
public:Num(int num) :num(num) {}
void print() //常成员函数
{
cout << num << endl;
}
protected:
int num;
};
int main()
{
//去掉常属性
const int a = 50;
//a = 100;//不允许被修改的常属性
int* pa = const_cast<int*>(&a);//去掉了const属性 才能赋值给这个int*指针
const int* pa2 = &a;
*pa = 100;//其实并没有改变a的值
cout << a << endl;
//增加常属性一般用于字符串的操作
Str str = "狐狸";//在没去掉常属性之前,无法使用
str.print();
//也可以给指针去掉常属性
const Num* pnum = new Num(100);
//pnum->print();//错误,常对象只能调用常成员函数
Num* newpnum =const_cast<Num*> (pnum);//去掉常属性
newpnum->print();//调用方法
}
static_cast? 用于数据类型的转换,它无法去除常属性
该转换下行转换不安全 在使用父类中没有的成员或者数据操作会导致中断问题
#include<iostream>
using namespace std;
class Fox
{
public:
virtual void print()
{
cout << "父类" << endl;
}
void print2()
{
cout << "测试1" << endl;
}
private:
};
class Foxson :public Fox
{
public:
void print()
{
cout << "子类" << endl;
}
void print3()
{
cout << "测试3" << endl;
}
private:
};
int main()
{
//基本数据转换
int num = 5;
double x = static_cast<double>(num);
//空指针类型
double* PD = new double;
void* VD =static_cast<void*>(PD);
//const类型转换 //不能去除const属性
int x1 = 10;
const int xx = static_cast<const int>(x);
//上行转换 (子类转父类)
Fox* pfox = new Fox;
Foxson* Pfoxson = new Foxson;
pfox = static_cast<Fox*>(Pfoxson);
//下行转换(父到子)
Pfoxson = static_cast<Foxson*>(pfox);
Pfoxson->print();
Pfoxson->print3();
Pfoxson->print2();
}
dynamic_cast? 主要用于上下行转换,和交叉转换
? ? ? ? ? ? ? ? ? ? ? ? 该转换可以检测出父类是否拥有虚函数,如果没有虚函数,父类下行转换子类报错
? ??
#include<iostream>
#include<string>
using namespace std;
class Fox
{
public:
Fox(string name="父类") :name(name) {}
virtual void print() { cout << name << endl; }
void printfdata() { cout << name << endl; }
protected:
string name;
};
class FoxSon :public Fox
{
public:FoxSon(string name="子类") :name(name) {}
void print() { cout << name << endl; }
void printfdata() { cout << name << endl; }
protected:
string name;
};
class A
{
public:
virtual void print()
{
cout << "A" << endl;
}
};
class B
{
public:
void print()
{
cout << "B" << endl;
}
};
class C :public A, public B
{
void print()
{
cout << "C" << endl;
}
};
int main()
{ //下行转换
Fox* Pfox = new Fox;
Pfox->print();
Pfox->printfdata();
FoxSon* PFoxSon = dynamic_cast<FoxSon*>(Pfox);
if(PFoxSon!=nullptr)
{
PFoxSon->print();//下行转换调用父类全部错误
PFoxSon->printfdata();
}//dynamic_cast类型转换成功才会赋值,不然放回空 该PFoxSon并没有转换成功
//交叉转换 用于多继承的才有交叉转换这个概念
// 我们可以通过子类对象为桥梁 实现父类指针的互转
A* PA = new C;
PA->print();
B* PB = dynamic_cast<B*>(PA);
PB->print();//实现父类之间的转换我们称为交叉互转
//while (1);
return 0;
}
? ? ?reinterpret_cast
? ? ?将一个指针转换成数字,也可以重新转换成一个指针,可以用于函数指针上
#include<iostream>
using namespace std;
int Pmax(int a,int b)
{
return a > b ? a : b;
}
int main()
{
int num = reinterpret_cast<int>(Pmax);
cout << hex << num << endl;//转换成16进制会发现一模一样,我们也可以将这个数字转换成函数指针
cout << Pmax << endl;//函数的地址
auto Pmax = reinterpret_cast<int(*)(int, int)>(num);
cout << Pmax(4, 5) << endl;//正常使用
}
|