1、纯虚函数和抽象类
纯虚函数在一个继承结构中就是只有声明没有实现的函数。拥有纯虚函数的类就叫做抽象类。
【举个栗子】
class Animal
{
public:
Animal(string name) :_name(name) {}
//纯虚函数
virtual void bark() = 0;
protected:
string _name;
};
//以下是动物实体类
class Cat :public Animal
{
public:
Cat(string name) :Animal(name) {}
void bark() { cout << _name << "bark:miao miao!" << endl; }
};
class Dog :public Animal
{
public:
Dog(string name) :Animal(name) {}
void bark() { cout << _name << "bark:wang wang!" << endl; }
};
class Pig :public Animal
{
public:
Pig(string name) :Animal(name) {}
void bark() { cout << _name << "bark:heng heng!" << endl; }
};
void bark(Animal* p)
{
p->bark();
}
int main()
{
Cat cat("喵咪");
Dog dog("二哈");
Pig pig("佩奇");
bark(cat);
bark(dog);
bark(pig);
return 0;
}
在上面这个Animal类的作用就是:
string_name
:让所有的动物实体类通过集成 Animal直接复用该属性bark纯虚函数
:在这里面对于bark这个方法,在基类里面因为不能明确是哪一个动物,所以不能具体的写对应的代码,这时我们把bark写成这样子virtual void bark() = 0;这就是纯虚函数。抽象类不能实例化对象
:比如下面这个程序就不能调用成功,因为调用这个行为是调动不了的,因为这个行为没有实现。所以抽象类只能当做指针或者引用来使用
int main()
{
Animal an("猫");
an.bark();
return 0;
}
练习:实现一个汽车类的抽象来,来计算汽车剩余油量还能跑的公里数
class Car
{
public:
Car(string name,double oil):_name(name),_oil(oil){}
//获取汽车剩余油量还能跑的公里数
double getLeftMiles()
{
//IL 10 * oil
return _oil * getMilesPerGallon();//发生动态绑定
}
string getName()const { return _name; }
protected:
string _name;
double _oil;
virtual double getMilesPerGallon() = 0;//存虚函数 因为对于不同的车他每公里所用的油量是不同的
};
class Bnze : public Car
{
public:
Bnze(string name, double oil):Car(name,oil){}
double getMilesPerGallon() { return 20.0; }
};
class Audi : public Car
{
public:
Audi(string name, double oil) :Car(name,oil) {}
double getMilesPerGallon() { return 18.0; }
};
class BMW : public Car
{
public:
BMW(string name, double oil) :Car(name,oil) {}
double getMilesPerGallon() { return 19.0; }
};
//给外部提供一个统一的获取汽车剩余路程数的API
void showCarLeftMiles(Car& car)
{
cout << car.getName()<< "left miles:" << car.getLeftMiles() << "公里" << endl;
}
int main()
{
Bnze b1("奔驰",20.0);
Audi a("奥迪",20.0);
BMW b2("宝马",20.0);
showCarLeftMiles(b1);
showCarLeftMiles(a);
showCarLeftMiles(b2);
return 0;
}
2、c++中的四种强制转换类型
1、const_cast
作用
:去掉(指针或者引用)常量属性的类型转换
【举个栗子】
当我们想将一个常量a的指针转成一个int*,在c语言中可以用如下的方式强制转换。
int main()
{
const int a = 10;
int* p = (int*)&a;
return 0;
}
但是在c++中,我们就有了const_cast,就可以使用如下的方式转换:
int main()
{
const int a = 10;
int* p2 = const_cast<int*>(&a);
return 0;
}
总结:
- 上述两种转换在底层(转成汇编指令过后)实现是一模一样的
char* p = (char*)&a
是可以的,但是char* p2 = const_cast<char*>(&a)
是不可以的。因为,const_cast在提供强转的时候,需要表达式(地址类型)与左边定义的类型,包括要转换的类型应该保持一致。防止了任意转换类型导致一些不可预期的问题。- const_cast<这里面必须是指针或引用类型 int* int&**>
2、static_cast
作用
:提供编译器认为安全的类型转换(没有任何联系的类型之间的转换就被否定了),也是我们最常用的一种转换方式。另外,基类类型和派生类类型之间也可以用static_cast转换
。
【举个栗子】
下面这种方式编译器就会直接报错
int *p = nullptr;
short* b = static_cast<short*>(p);
3、reinterpret_cast
他的作用就类似于c风格的强制类型转换。你想要怎样转换都可。
4、dynamic_cast
作用
:用在继承结构中,可以支持RTTI类型识别的上下转换
为了更好的解释,我们还是以具体的栗子来加以说明。
【举个栗子】
首先实现一个鸟的抽象类
#include<iostream>
class Bird
{
public:
Bird(std::string nm)
:mname(nm)
{}
virtual void FlyHigh() = 0;
protected:
std::string mname;
};
class Eagle:public Bird
{
public:
Eagle(std::string name)
:Bird(name)
{}
void FlyHigh()
{
std::cout << mname << "can fly 1000m!" << std::endl;
}
};
class chicken:public Bird
{
public:
chicken(std::string name)
:Bird(name)
{}
void FlyHigh()
{
std::cout << mname << "can fly 0m!" << std::endl;
}
};
int main()
{
Bird* pb1 = new Eagle("eagle");
pb1->FlyHigh();
Bird* pb2 = new chicken("chicken");
pb2->FlyHigh();
}
现在我们要在这基础上实现一个函数能打印出所有鸟的飞行行为。
方式一:用模板实现
具体实现如下:
template<typename Bird_Type>
void showFly(Bird_Type* pb)
{
pb->FlyHigh();
}
int main()
{
Bird* pb1 = new Eagle("eagle");
showFly(pb1);
Bird* pb2 = new chicken("chicken");
showFly(pb2);
}
方式二:用基类指针实现
因为允许所有的派生类对象用基类指针来指向,所以showFly函数还可以用以下的方式实现
void showFly(Bird* pb)
{
pb->FlyHigh();
}
改进:针对于不会飞的鸟进行特殊打印
我们可以通过以下两种方式知道pb这个指针是指向的chiken派生类对象。
1、方式一:typeid
借用typeid这个关键字来动态获取信息。通过pb找到虚函数指针,再通过虚函数指针找到虚函数表,从虚函数表的第一项拿到RTTI的信息就可以确定这个对象本质是一个什么样的类型。具体流程如下:
代码实现如下:
void showFly(Bird* pb)
{
if (typeid(*pb) == typeid(chicken))
{
std::cout << "chicken can't fly!" << std::endl;
}
else
{
pb->FlyHigh();
}
}
2、方式二:使用dynamic_cast
就是通过指针指向的对象找虚函数指针再通过虚函数指针找对应的虚函数表,从虚函数表中获取RTTI信息。再拿这个信息和你需要转换的类型进行比较,如果两个类型相同的话就是允许转换的。这个时候就转换成功。
具体实现如下:
void showFly(Bird* pb)
{
chicken* pc = dynamic_cast<chicken*>(pb);
if (pc != NULL)
{
std::cout << "chicken can not fly!" << std::endl;
}
else
{
pb->FlyHigh();
}
}