C++纯虚函数
1、纯虚函数
纯虚函数是在基类中声明但没有具体实现的虚函数。它的作用是为派生类提供一个统一的接口规范,要求派生类必须实现该函数,否则派生类也会成为抽象类。
virtual 返回类型 函数名(参数表)=0;
“=0”表示不定义该虚函数的实现,没有函数体,只有函数声明;函数声明是为了在虚表中保留一个位置,将指向该虚函数体的指针定义为nullptr。
2、抽象类
含有纯虚函数的类是抽象类。抽象类是无法实例化对象的,因为纯虚函数没有实现部分,所以含有纯虚函数的类不能实例化对象。
抽象类使用规则:
(1)抽象类只能做其他类的基类,不能创建抽象类的对象
(2)抽象类不能用作参数类型、函数返回类型或显示类型转换。
(3)可以定义抽象类的指针的引用,此指针可以指向它的派生类的对象,从而实现运行时多态。
3、四种类型转换
(1)static_cast 静态转换
static_cast <type_name> (表达式);
将表达式转换为 type_name 类型。
int a = 10;
char ch = 'x';
a = static_cast <int> (ch);
这里使用
static_cast<int>(ch)
把字符型变量ch
转换为整型。在进行转换时,实际上是把字符ch
对应的 ASCII 码值转换为整数。字符'x'
的 ASCII 码值是120
,所以这个转换会把ch
的 ASCII 码值120
赋给整型变量a
。经过这行代码的执行,变量a
的值就从原来的10
变成了120
。
1、基本数据类型转换:char short int long long long float double bool等;
2、指针转换:不能将常性指针给普通指针,
3、将void* 转换为其他类型
4、弃值表达:
5、左值转为右值 (无法将常性左值转换为右值)
6、用于类层次结构中基类和派生类之间指针或引用的转换。
上行转换:把派生类的指针或引用转换成基类表示是安全的。objx = static_cast<Object>(base);
下行转换:把基类指针或引用转换成派生类时,没有动态检查,是不安全的。
class Object
{
private:int value;
public:Object(int x= 0):value(x){}void func(){}
};
class Base :public Object {
private:int num;
public:Base(int x =0):Object(x+10),num(x){}void show()const{}
};class Test :public Object
{
private:int count;
public:Test(int x =0):Object(x), count(x + 10) {}void print()const{}
};
int main()
{Object objx;Test test;Base base;Object* op = &objx;Base* bp = static_cast<Base*>(op); //error //objx = static_cast<Object>(base);return 0;
}
不能把 基类指针或引用转换成派生类。bp指向objx时,bp要识别到Base类的num,而objx没有num。
(2)const_cast 去常性转换符
用来修改类型的const、volatile 属性。
内置类型:替换。
(3)reinterpret_cast 重新解释
reinterpret_cast <type_name> (expression);
type_name 必须是一个指针、引用、算数类型、函数指针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针。 不产生亡值。
(4)dynamic_cast 动态转换
dynamic_cast <type_name> (expression);
允许在运行时刻进行类型转换,(目的:去查虚表中的RTTI信息,动态转换其实就是去识别RTTI)从而使程序能够在一个类层次结构中安全的转换类型。
可以把基类指针转换成派生类指针,或把指向基类的左值转换成派生类的引用。向上转换。
必须是public继承,基类要有虚函数。
如果是针对指针类型,失败结果是nullptr;
如果是针对引用类型,失败结果会抛出一个异常;(没有空引用)
在类层次进行上行转换时,dynamic_cast 和 static_cast的效果一样。
进行下行转换时,dynamic_cast 具有类型检查功能,比static_cast更安全。
示例,加以理解!!!
typeid
运算符的工作原理
typeid(op)
:当对指针op
使用typeid
运算符时,typeid
会根据指针的静态类型来确定其类型信息。在这个例子中,op
的静态类型是Object*
,所以typeid(op).name()
输出Object*
。typeid(*op)
:当对指针解引用后使用typeid
运算符时,typeid
会在运行时根据对象的实际类型来确定其类型信息。具体步骤如下:
- 获取虚函数表指针:通过
op
指针找到对象的内存地址,进而获取对象的虚函数表指针(vptr)。- 查找
type_info
指针:从虚函数表中获取指向type_info
对象的指针。- 获取类型信息:通过
type_info
指针访问type_info
对象,从中获取对象的实际类型信息。
dynamic_cast
的工作机制
dynamic_cast
是 C++ 中用于在继承体系里进行类型转换的运算符,它会在运行时检查类型转换是否合法。当使用
dynamic_cast
进行指针类型转换时,若转换不合法会返回nullptr
;当进行引用类型转换时,若转换不合法会抛出
std::bad_cast
异常。
例.1:
1. Base* pb = dynamic_cast<Base*>(op);
非法的原因
op
是 Object
类型的指针,它实际指向的是 Test
类的对象 test
。Base
类和 Test
类虽然都继承自 Object
类,但它们是平行的派生类,彼此之间没有直接的继承关系。将指向 Test
对象的指针转换为 Base
类型的指针是不安全的,因为 Test
对象的内存布局和 Base
对象不同,强制转换会导致未定义行为。所以 dynamic_cast
在运行时检查到这种不合法的转换,会返回 nullptr
。
2. Test* tp = dynamic_cast<Test*>(op);
合法的原因
op
实际指向的就是 Test
类的对象 test
。将指向 Test
对象的指针转换为 Test
类型的指针是合法的,因为指针所指向的对象类型和要转换的目标类型是一致的。dynamic_cast
在运行时检查发现 op
实际指向的就是 Test
类型的对象,所以可以安全地进行转换,返回一个有效的 Test
类型指针。
例.2:
1. Base& rb = dynamic_cast<Base&>(robj);
不合法的原因
robj
实际引用的是 Test
类的对象 test
。Base
类和 Test
类是平行的派生类,它们之间没有直接的继承关系,且各自的内存布局和成员函数等实现可能不同。将 Test
对象的引用转换为 Base
对象的引用是不安全的,因为 Test
对象不具备 Base
对象特有的成员布局和语义。dynamic_cast
在运行时检查到这种不合法的转换后,会抛出 std::bad_cast
异常。
2. Test& rp = dynamic_cast<Test&>(robj);
合法的原因
robj
本身引用的就是 Test
类的对象 test
。将 robj
转换为 Test
类型的引用,是将一个对象的引用转换为该对象所属类的引用,这是完全合法且安全的操作。dynamic_cast
在运行时检查到 robj
实际引用的就是 Test
对象,所以可以顺利完成转换,不会抛出异常。