一、继承
1.1 继承中的类型兼容性原则
概念:类型兼容性原则是指在需要基类对象的地方,我们都可以直接使用公有派生类对象,通过公有继承,我
们可以得到基类中除了构造函数,析构函数外的所有成员,这样,凡是基类可以解决的问题,派生类都可以解
决。
(1)子类对象可以当成父类对象使用,子类对象是一种特殊的父类。
(2)父类指针可以直接指向子类对象
(3)父类引用可以直接引用派生类对象
#include <iostream> using namespace std; class Parent { protected: int m_a; int m_b; public: Parent() { cout<<"Parent的无参构造函数"<<endl; } Parent(const Parent &obj) { cout<<"Parent的拷贝构造函数"<<endl; m_a = obj.m_a; m_b = obj.m_b; } void SetAB(int a,int b) { m_a = a; m_b = b; } void print() { cout<<"m_a = "<<this->m_a<<endl; cout<<"m_b = "<<this->m_b<<endl; } }; class Child:public Parent { private: int m_c; public: void SetC(int c) { this->m_c = c; } void printC() { cout<<"c = "<<m_c<<endl; } }; int main(int argc, char const *argv[]) { /* Parent p1; p1.SetAB(1,2); p1.print(); */ Child c; c.SetAB(10,20); c.SetC(30); c.print(); c.printC(); // Parent &p = c; // p.printC();//不能使用 //Child *pc = &c; //Parent *p = &c; //p->SetAB(1,2); //p->SetC(20); return 0; }
1.2 多继承
(1) 概念
派生类中如果只有一个基类,称为单继承,除此之外,c++中还支持多继承,每一个派生类可以继承多个基
类。
(2)多继承的语法
用逗号将继承的基类隔开。
#include <iostream> using namespace std; class AirPlane { protected: int high; public: AirPlane() { cout<<"飞机的构造函数"<<endl; high = 100; } void show() { cout<<"飞机的高度"<<high<<endl; } }; class Ship { protected: int speed = 40; public: Ship() { cout<<"轮船的构造函数"<<endl; } void show() { cout<<"航行的速度:"<<speed<<endl; } }; class WaterPlane:public Ship,public AirPlane { public: WaterPlane() { cout<<"水上飞机的构造函数"<<endl; } }; int main(int argc, char const *argv[]) { WaterPlane w; cout<<sizeof(w)<<endl; w.AirPlane::show(); w.Ship::show(); return 0; }
(2)多继承的构造和析构
#include <iostream> using namespace std; class AirPlane { protected: int high; public: AirPlane(int h):high(h) { cout<<"飞机的构造函数"<<endl; } ~AirPlane() { cout<<"飞机的析构函数"<<endl; } }; class Ship { protected: int speed; public: Ship(int s):speed(s) { cout<<"轮船的构造函数"<<endl; } ~Ship() { cout<<"轮船的析构函数"<<endl; } }; class WaterPlane:public Ship,public AirPlane { public: WaterPlane(int h,int s):AirPlane(h),Ship(s) { cout<<"水上飞机的构造函数"<<endl; } ~WaterPlane() { cout<<"水上飞机的析构函数"<<endl; } }; int main(int argc, char const *argv[]) { WaterPlane w(100,40); return 0; }
(3)多继承的二义性
#include <iostream> using namespace std; class TestA { public: int a[7]; }; class TestB:public TestA { public: int b; }; class TestC:public TestA { public: int c; }; class TestD:public TestB,public TestC { public: int d; }; int main(int argc, char const *argv[]) { TestD td; cout<<sizeof(td)<<endl; //td.a;//会报错,不知道是哪个a return 0; }
(4) 虚继承
虚继承的目的是为了让某个类作出声明,承若愿意共享它的基类,这个被共享的基类称为虚基类,在这种情况
下,无论虚基类在继承体系中出现多少次,在派生类中只会为其开辟一次内存空间
#include <iostream> using namespace std; class TestA { public: int a; }; class TestB:virtual public TestA { public: int b; }; class TestC:virtual public TestA { public: int c; }; class TestD:public TestB,public TestC { public: int d; }; int main(int argc, char const *argv[]) { TestB tb; cout<<sizeof(tb)<<endl; cout<<&tb<<endl; cout<<&tb.a<<endl; cout<<&tb.b<<endl; TestD td; cout<<sizeof(td)<<endl; cout<<&td<<endl; cout<<&td.a<<endl; cout<<&td.b<<endl; cout<<&td.c<<endl; cout<<&td.d<<endl; return 0; }
二、多态
2.1 问题
#include <iostream> using namespace std; class Parent { public: void show() { cout<<"this is Parent"<<endl; } }; class Child:public Parent { public: void show() { cout<<"this is child"<<endl; } }; int main(int argc, char const *argv[]) { Parent *p1 = new Child; //静态联编,编译器会p1的类型,调用parent里面的show函数 p1->show(); return 0; }
2.2 多态的概念和使用
多态:多种形态,使用多态,当基类指针指向基类对象的时候,期待其按照基类的方法做事,当基类指针指向派生类对象的时候,就按照派生类的方法做事,实现同一个接口,多种方法,它有多种形态,这种现象称为多态。
多态的条件: 1.要有继承 2.要有虚函数重写(发生在不同的作用域,函数原型相同) 3.基类指针指向派生类对象
#include <iostream> using namespace std; class Parent { public: virtual void show() { cout<<"this is Parent"<<endl; } }; class Child:public Parent //1.要有继承 { public: void show() //2.要有虚函数重写 { cout<<"this is child"<<endl; } }; int main(int argc, char const *argv[]) { Parent *p1 = new Child; //3.基类指针指向不同的对象 p1->show(); delete p1; p1 = new Parent; p1->show(); delete p1; return 0; }
2.3 多态的原理
#include <iostream> using namespace std; class Parent { public: int a; virtual void show() { cout<<"this is Parent"<<endl; } }; class Child:public Parent //1.要有继承 { public: void show() //2.要有虚函数重写 { cout<<"this is child"<<endl; } }; int main(int argc, char const *argv[]) { Parent p; Child c; cout<<sizeof(p)<<endl; cout<<sizeof(c)<<endl; cout<<"Parent的起始地址:"<<&p<<endl; cout<<"成员变量a的起始地址:"<<&p.a<<endl; return 0; }
2.4 虚析构函数
根据析构的规则,只能从当前基类开始往上析构,而并不能调用到派生类中的析构函数,通过将基类的析构函数设置成虚函数,调用delete后,使其可以按照正确的顺序析构动态创建的对象。
#include <iostream> using namespace std; class Parent { public: int a; virtual void show() { cout<<"this is Parent"<<endl; } Parent() { cout<<"Parent的构造函数"<<endl; } virtual ~Parent() { cout<<"Parent的析构函数"<<endl; } }; class Child:public Parent //1.要有继承 { public: void show() //2.要有虚函数重写 { cout<<"this is child"<<endl; } Child() { cout<<"Child的构造函数"<<endl; } ~Child() { cout<<"child的析构函数"<<endl; } }; int main(int argc, char const *argv[]) { Parent *p = new Child; delete p; return 0; }
2.5 动态类型识别
dynamic_cast 1.dynamic_cast是c++新型关键词 2.dynamic_cast用于基类和派生类之间的转换 3.dynamic_cast要求使用的目标类型是多态的。 即要求所在类中至少有一个虚函数。 用于指针转换时,转换失败会返回NULL指针。 用于引用转换时,转换失败会引发bad_cast异常
优势: 1. 不需要显示的声明和定义虚函数 2. 不用为每个类分配类型ID 缺点: 1.只能用于有虚函数的类
#include <iostream> using namespace std; class Parent { private: int m_a; public: enum{ID = 0}; virtual int GetId() { return ID; } }; class Child:public Parent { public: int array[102400]; enum{ID = 1}; virtual int GetId() { return ID; } }; void func(Parent *p) { //Child *c = (Child *)p; if(p->GetId() == Child::ID) { Child *c = (Child *)p; c->array[102400 -1] = 100; cout<<"转换成功"<<endl; } else { cout<<"转换失败"<<endl; } } int main(int argc, char const *argv[]) { Parent *p = new Child; //Parent *p = new Parent; func(p); return 0; }
2.6 dynamic_cast
#include <iostream> using namespace std; class Parent { private: int m_a; public: virtual void show(){} }; class Child:public Parent { public: int array[102400]; public: virtual void show(){} }; void func(Parent *p) { Child *c = dynamic_cast<Child *>(p); //如果p指向的是基类对象,则转换失败,转换失败返回NULL if(NULL == c) { cout<<"转换失败"<<endl; } else { c->array[102400 -1] = 100; cout<<"转换成功"<<endl; } } int main(int argc, char const *argv[]) { Parent *p = new Child; //Parent *p = new Parent; func(p); return 0; }
2.7 typeid
typeid可以用于获取一个表达式的类型信息 typeid的操作对象既可以是表达式,也可以是数据类型 typeid(dataType); 或者: typeid(expression);
#include <iostream> #include <typeinfo> using namespace std; class Parent { private: int m_a; public: virtual void show(){} }; class Child:public Parent { public: int array[102400]; public: virtual void show(){} }; void func(Parent *p) { if(typeid(*p) == typeid(Child)) { Child *c = (Child *)p; c->array[102400 -1] = 100; cout<<"转换成功"<<endl; } else if(typeid(*p) == typeid(Parent)) { cout<<"转换失败"<<endl; } } int main(int argc, char const *argv[]) { int a; char ch; Parent p1; Child c1; const type_info &pa = typeid(a); const type_info &pch = typeid(ch); const type_info &pp1 = typeid(p1); const type_info &pc1 = typeid(c1); cout<<pa.name()<<endl; cout<<pch.name()<<endl; cout<<pp1.name()<<endl; cout<<pc1.name()<<endl; Parent *p = new Child; //Parent *p = new Parent; func(p); return 0; }
2.8 纯虚函数
virtual 返回值类型 函数名 (函数形参)= 0;
#include <iostream> using namespace std; class Parent //含有纯虚函数的类称为抽象类,抽象类不能实例化对象 { public: virtual void show() = 0; //纯虚函数,没有函数体 }; class Child:public Parent { public: virtual void show() { cout<<"this is child"<<endl; } }; int main(int argc, char const *argv[]) { Parent *p = new Child; p->show(); return 0; }