首页 > 代码库 > is-a
is-a
-----------------siwuxie095
is-a
在 C++ 中,is-a (是一个)的概念就相当于 隐形眼镜也是眼镜
如果眼镜是基类的话,那隐性眼镜就是眼镜的派生类
再如:定义人类、工人类、士兵类,其中:工人类和士兵类分别继承
人类,就可以把每一个工人的对象称之为 人的对象,也可以把每一个
士兵的对象称之为 人的对象
基于这种理论,在程序中就有很多更加灵活的玩法了,如下:
先实例化 Soldier 对象 s1,当实例化 Person 对象 p1 时,让 p1 直接
接收 s1,即 用 s1 去实例化 p1,这样做,在语法上是正确的,因为一
个士兵也是一个人,用士兵去初始化人是 OK 的,再用 Person 的指针
p2 指向 Soldier 的对象 s1,显然也是 OK 的
但是,不能说一个人也是一个士兵,即 将人的对象 p1 赋值给士兵的对
象 s1 是有问题的,同时,用士兵的指针 s2 指向人的对象 p1 也是有问
题的
综上所述:
(1)派生类的对象可以赋值给基类 或 子类的对象可以赋值给父类
(2)基类的指针可以指向派生类的对象 或 父类的指针可以指向子类的对象
既然如此,就可以将基类的指针 或 基类的对象 或 基类的引用,
作为函数的参数,来使函数可以接收所传入的子类的指针 或 对
象(或者 基类的指针 或 对象)
如下:
存储结构
(1)将子类的对象赋值给父类的对象,或 用子类的对象初始化
父类的对象
如果父类含有 m_strName 和 m_iAge 两个数据成员,那么子类
在继承父类时,一定也含有 m_strName 和 m_iAge 这两个数据
成员,同时,子类还有自身的数据成员
当用子类的对象向父类的对象赋值 或 用子类的对象初始化父类的
对象时,其本质就是将子类当中从父类继承下来的数据成员赋值给
父类的对象,子类当中其它的数据此时就会被截断,即 丢失
因为,对于父类对象来说,它只能接收自己拥有的数据成员的数据,
而无法接收其它的数据
(2)用父类的指针指向一个子类对象
如果是用父类的指针指向一个子类的对象,那么父类的指针也
只能访问到父类所拥有的数据成员,而无法访问到子类所拥有
的数据成员,即 父类指针指向子类对象时,只能通过父类指针
去访问父类原有的数据成员和成员函数,无法访问子类独有的
数据成员和成员函数
程序 1:
Person.h:
#include <string> using namespace std;
class Person { public: Person(string name = "Jim"); virtual ~Person(); void play(); protected: string m_strName; }; |
Person.cpp:
#include "Person.h" #include <iostream> using namespace std;
Person::Person(string name) { m_strName = name; cout << "Person()" << endl; }
Person::~Person() { cout << "~Person()" << endl; }
void Person::play() { cout << "Person--play()" << endl; cout << m_strName << endl; } |
Soldier.h:
#include "Person.h"
class Soldier:public Person { public: Soldier(string name = "James", int age = 20); virtual ~Soldier(); void work(); protected: int m_iAge; }; |
Soldier.cpp:
#include "Soldier.h" #include <iostream> using namespace std;
Soldier::Soldier(string name,int age) { m_strName = name; m_iAge = age; cout << "Soldier()" << endl; }
Soldier::~Soldier() { cout << "~Soldier()" << endl; }
void Soldier::work() { cout << "Soldier--work()" << endl; cout << m_strName << "," << m_iAge<< endl;
} |
main.cpp:
#include<stdlib.h> #include "Soldier.h" #include <iostream> using namespace std;
int main(void) { Soldier soldier; soldier.work(); cout << endl; //通过父类的对象、指针、引用来指向子类的对象 //即用子类的对象来初始化父类的对象、指针、引用 // //而不能用父类对象去初始化子类 即is-a的关系 //在初始化时父类只会得到子类从父类继承来的数据成员 //而子类自己的数据成员则会被截断 丢失 // //假如改为: Person person1; person1=soldier; 这样就会多执行一次构造函数 Person person1 = soldier; person1.play(); //父类指针也只能指向内存中子类从父类继承来的数据成员所在的内存 Person *person2 = &soldier; person2->play(); Person &person3 = soldier; person3.play(); cout << endl;
//当Person类的指针从堆中指向Soldier类的对象时 使用虚析构函数 //如果不使用虚析构函数 那么堆中的内存就无法释放 导致内存泄露 //即 前面的 virtual 是为下面这段代码用的 Person *p = new Soldier; p->play(); //如果不使用虚析构函数,delete p 时就只会调用Person类的析构函数, //而指针 p 指向Soldier类对象时却依次执行了父类和子类的构造函数, //即 子类对象没有释放掉 delete p; p = NULL; system("pause"); return 0; }
//当使用父类的指针指向从堆中申请的子类的对象时 //又想要通过父类的指针释放这块从堆中申请的内存就必须使用虚析构函数 // //当给父类的析构函数加上关键字 virtual 后 这个关键字会被继承下去 //即子类的析构函数也是虚析构函数 即便不写关键字 virtual // 不过推荐子类的析构函数前也写上 virtual |
运行一览:
程序 2:
Person.h:
#include <string> using namespace std;
class Person { public: Person(string name = "Jim"); ~Person(); void play(); protected: string m_strName; }; |
Person.cpp:
#include "Person.h" #include <iostream> using namespace std;
Person::Person(string name) { m_strName = name; cout << "Person()" << endl; }
Person::~Person() { cout << "~Person()" << endl; }
void Person::play() { cout << "Person--play()" << endl; cout << m_strName << endl; } |
Soldier.h:
#include "Person.h"
class Soldier :public Person { public: Soldier(string name = "James", int age = 20); ~Soldier(); void work(); protected: int m_iAge; }; |
Soldier.cpp:
#include "Soldier.h" #include <iostream> using namespace std;
Soldier::Soldier(string name, int age) { m_strName = name; m_iAge = age; cout << "Soldier()" << endl; }
Soldier::~Soldier() { cout << "~Soldier()" << endl; }
void Soldier::work() { cout << "Soldier--work()" << endl; cout << m_strName << "," << m_iAge << endl; } |
main.cpp:
#include<stdlib.h> #include "Soldier.h" #include <iostream> using namespace std;
//参数是父类的对象 //因为这里是对象 在调用test1()时会实例化一个临时对象(在参数传进来时) //并通过这个临时对象来调用play()函数 //test1()执行完毕后临时对象被销毁 会执行析构函数 void test1(Person p) { p.play(); }
//参数是父类的引用 //不会产生新的临时变量 效率更高(推荐) void test2(Person &p) { p.play(); }
//参数是父类的指针 //不会产生新的临时变量 效率更高(推荐) void test3(Person *p) { p->play(); }
// 在公有继承中 is-a 的关系在函数参数传递时的体现 int main(void) { Soldier s; Person p; cout << endl; test1(s); test1(p); cout << endl; //使用父类的引用作参数也可以接收父类的对象以及子类的对象 test2(s); test2(p); cout << endl; test3(&s); test3(&p); cout << endl; system("pause"); return 0; } |
运行一览:
【made by siwuxie095】
is-a