一、虚函数和虚函数表
用virtual修饰的函数叫虚函数
虚函数表:函数指针,存储所有虚函数首地址
普通函数对类所占内存无影响,虚函数会影响,但无论有多少虚函数,只增加一个指针大小的字节(即四个字节),空的类或者结构体 占用一个字节作为标识 一旦有数据就不需要这一个字节
对于虚函数在类外实现就不需要virtual只需要类名限定即可
多态:同一个调用的不同结果,注意弄清什么时候调用哪个函数
多态的必要条件 1父类必须要有虚函数 2子类必须采用公有继承 3必须要有指针的使用
class Man
{
public:
void WC1()
{
cout << "manWC1" << endl;
}
virtual void WC2()//父类必须要有virtual
{
cout << "manWC2" << endl;
}
protected:
};
class Woman:public Man
{
public:
//要求子类也有相同函数,但函数可以不带virtual
void WC1()
{
cout << "woWC1" << endl;
}
void WC2()
{
cout << "woWC2" << endl;
}
protected:
};
void testVirtual()
{
//正常访问不存在多态 就近原则
//非正常赋值 子类对象初始化父类指针
Man* pt = new Woman;
//有virtual看对象类型 没有virtual看指针类型
pt->WC1();//父类
pt->WC2();//子类
pt = new Man;
pt->WC2();//父类
}
二、虚析构函数
用子类对象初始化父类指针,需要使用虚析构函数进行内存释放
class parent
{
public:
virtual ~parent()
{
cout << "父类析构函数" << endl;
}
void print() {}
protected:
};
class son :public parent
{
public:
~son()
{
cout << "子类析构函数" << endl;
}
};
final与override
class A
{
public:
//final禁止重写
//final子类不能子类不能存在同名函数
virtual void print() final
{
cout << "重写函数虚函数才有" << endl;
}
virtual void printData()
{}
};
class B :public A
{
void printData() override//强制重写 标识作用 检查父类中是否存在当前虚函数
{
cout << "当前函数是重写函数" << endl;
}
};
三、纯虚函数和ADT
纯虚函数:也是虚函数 只是没有函数体 virtual print()=0 要在类中这样写
抽象类:拥有至少一个出虚函数的类 不能构建对象 但可以构建对象指针
纯虚函数就是做ADT的过程(abstract data typpe 抽象数据类型)即搭建框架
纯虚函数没有被重写 无论被继承多少次都是纯虚函数
以栈为例
class stack
{
public:
//描述好父类中所有的属性
virtual void push(int data) = 0;
virtual void pop() = 0;
virtual int top() const = 0;
virtual bool empty() const = 0;
virtual int size() = 0;
};
//子类想创建对象,必须重写父类的纯虚函数
//ADT具有强迫性,所有子类重写函数必须和父类一样
class arrayStack :public stack
{
public:
void push(int data) {}
void pop() {}
int top() const {
return 1;
}
bool empty() {
return false;
}
int size() {
return 1;
}
//可以增加别的函数和成员
};
void testVirtual(stack* pStack)
{
pStack->push(1);
while (!pStack->empty())
{
cout << pStack->top();
pStack->pop();
}
}
四、类型转换
所谓的更为安全的转换方法
(1)const_cast
/*const_cast<要转换的类型>(要转换的目标)
1去掉const属性(提供一个可以修改的接口可以去操作const属性的变量
2加上const属性(用的比较少)
*/
class A
{
public://处理字符串常属性的问题 可以传变量或者常量
A(const char* str) :str(const_cast<char*>(str)) {}
protected:
char* str;
};
class B
{
public://处理字符串常属性的问题 可以传变量或者常量
B(int num) :num(num) {}
void print() {}
protected:
int num;
};
/*
const int num = 1;
//const int* pNum = #
int* pNum = const_cast<int*>(&num);
*pNum = 33;//num实际没变
cout << num;
//操作类指针
const B* pB = new B(0);
B* ppB = const_cast<B*>(pB);
ppB->print();
//操作常量引用
int num1;
const int& pNum1 = num1;
int& ppNum1 = const_cast<int&>(pNum1);
*/
(2)static_cast
//static_cast<要转换的类型>(要转换的目标)
// 1基本数据类型转换
// 2把空指针转换为目标类型
// 3把任何类型变量转换为void类型
// 4用在类上面的转换(基类和派生类的转换)
// 4,1上行转换(从子到父 指针或引用转换)安全
// 4.2上行转换(从父到子 指针或引用转换)
// 注意:不能转换const
/*
//1基本数据类型
char cNum = 'S';
int iNum = static_cast<int>(iNum);
//2空类型指针
double* p = new double;
void* pVoid = static_cast<void*>(p);
//3const类型转换
int x = 0;
const int constNum = static_cast<const int>(x);
//4错误用法
const int xx = 0;
//int* p = static_cast<int*>(&xx); 去掉const必须用const_cast
*/
(3)dynamic_cast
//dynamic_cast
// 1上行转换 和static_cast一样
// 2下行转换 dynamic_cast更安全
// 3交叉转换 多继承
class MM
{
public:
MM(string mmName = "父类") :mmName(mmName) {}
virtual void print() { cout << mmName; }
protected:
string mmName;
};
class Girl:public MM
{
public:
Girl(string gName="子类") :gName(gName) {}
void print() { cout << gName; }
void printData() {}
string gName;
protected:
};
/*
MM* pM = new MM;
Girl* pG = new Girl;
//1上行
MM* pSM = static_cast<MM*>(pG);
MM* pDM = dynamic_cast<MM*>(pG);
//2下行
Girl* pSG = static_cast<Girl*>(pM);//不存在virtual不会报错
Girl* pDG = dynamic_cast<Girl*>(pM);//检查父类中是否有virtual
//不存在 类型转换直接错误
//pSG->printData() 下行转换 调用子类中父类没有的函数报错
*/
//交叉转换 实际就是C继承了A B 然后A B间互相赋值
class A
{
public:
virtual void print()
{
cout << "A" << endl;
}
};
class B
{
public:
virtual void print()
{
cout << "B" << endl;
}
};
class C :public A,public B
{
public:
void print()
{
cout << "C" << endl;
}
};
//3交叉转换
//A* a = new C;
//B* b = dynamic_cast<B*>(a);
(4)reinterpret_cast
在哈希表会用,其余用的很少 ,用来转换函数指针和数字,
如int print(){}
int num=reinterpret_cast<int>(print);//num这个数字储存了函数指针print的地址