Human.h
#ifndef __HUMAN__ #define __HUMAN__ class HuMan { public: HuMan(char n[20], int a); //-- 一旦一个类 想要做父类,务必把这个类的析构函数写成虚函数。这样就能保证delete父类指针时能够调用正确的析构函数(即利用多态,先调用子类,再调用父类的析构函数) //-- 从而保证父类指针指向的子对象 内存被正确的释放。 virtual ~HuMan(); char name[20]; int age; int hanShuFuGaiTestFunc(); virtual void xuHanShuTestFunc(); virtual void xuHanShuTestFunc2(); //-- final关键字是用在父类中的虚函数的,父类中的虚函数用了final关键字就是让父类的虚函数不能被覆盖 virtual void xuHanShuTestFunc3() final; //-- 一旦有了纯虚函数,就不能实例化HuMan类型的对象了,纯虚函数也是虚函数 也支持多态的。含有纯虚函数的类叫做抽象类。 virtual void chunXuHanShuTestFunc() = 0; protected: private: }; #endif
HuMan.cpp
#include "stdafx.h" #include "Human.h" #include "iostream" using namespace std; HuMan::HuMan(char n[20], int a):age(a){ strcpy_s(name, n); // name = (n), cout << "HuMan的构造函数" << "name=" << name << " age=" << age<< endl; } HuMan::~HuMan(){ cout << "HuMan的析构函数" << endl; } int HuMan::hanShuFuGaiTestFunc(){ cout << "父类HuMan的hanShuFuGaiTestFunc函数" << endl; return 0; } void HuMan::xuHanShuTestFunc(){ cout << "父类HuMan的虚函数" << endl; } void HuMan::xuHanShuTestFunc2(){ cout << "父类HuMan的虚函数xuHanShuTestFunc2" << endl; } void HuMan::xuHanShuTestFunc3(){ cout << "父类HuMan的虚函数xuHanShuTestFunc3" << endl; }
Man.h
#ifndef __MAN__ #define __MAN__ #include "stdafx.h" #include "Human.h" #include "Wepon.h" //--Man 继承自 HuMan /* public protected private 三种继承方式 public protected private也专用于类中的成员变量和成员函数 public: 可以被任意实体来访问。(比如在main.cpp 的main函数内可以直接拿类对象或指向类对象的指针 直接访问类中公共的成员变量和函数) protected: 父子类之间的 成员函数 可以访问。 private: 本类的 成员函数 可以访问。 */ class Man :public HuMan { public: Man(char name[20], int age, char sex, wepon w1); virtual ~Man(); wepon w; void hanShuFuGaiTestFunc(int i); //-- 为了避免在子类中写错虚函数,C++11可在函数声明所在行末尾加 override关键字。(成员函数实现中不用加) //-- 这个关键字是用在子类中,并且是虚函数专用的, 与此关键字相对的还有一个叫final的关键字,final关键字是用在父类中的虚函数的,父类中的虚函数用了final关键字就是让父类的虚函数不能被覆盖。 virtual void xuHanShuTestFunc() override; //can not override final function 父类中的虚函数用了final关键字就是让父类的虚函数不能被覆盖 //virtual void xuHanShuTestFunc3() override; //-- 成员函数可以只有声明,没有定义,不会发生编译错误;但是虚函数有声明,必须要有定义,没有定义这会发生错误,是因为 父类指针在调用一个虚函数的时 执行的是动态绑定的虚函数。 virtual void chunXuHanShuTestFunc() override; protected: private: char sex; }; #endif
Man.cpp
#include "stdafx.h" #include "Man.h" #include "iostream" using namespace std; //-- 对于类 类型成员变量的初始化,能放在构造函数的初始化列表里进行的,千万不要放在函数体里来执行 Man::Man(char name[20], int age, char sex, wepon w1) :HuMan(name, age), w(w1), sex(sex){ cout << "执行Man的构造=" << " name=" << name << " age=" << age << "sex = " << this->sex << endl; } //-- 对象中的成员变量 并不是在析构函数的函数体里面销毁的,而是在函数执行完毕,由系统隐含销毁的。 Man::~Man(){ cout << "子类Man析构" << endl; } void Man::hanShuFuGaiTestFunc(int i){ cout << "子类Man的hanShuFuGaiTestFunc函数" << endl; } void Man::xuHanShuTestFunc(){ cout << "子类Man的虚函数" << endl; } void Man::chunXuHanShuTestFunc(){ cout << "子类Man纯虚函数的定义被调用,纯虚函数被定义才可以实例化子类" << endl; }
Wepon.h
#pragma once class wepon { public: wepon(char*n, int d); ~wepon(); wepon(const wepon& w); wepon& operator = (const wepon & w); char name[20]; int damage; protected: private: };
Wepon.cpp
#include "stdafx.h" #include "iostream" #include "Wepon.h" using namespace std; wepon::wepon(char*n, int d) :damage(d) { strcpy_s(name, n); cout << "执行wepon的构造=" << " name=" << name << " damage=" << damage << endl; } wepon::~wepon() { cout << "weopn的析构" << endl; } wepon::wepon(const wepon& w){ damage = w.damage; cout << "weopn的拷贝构造被调用" << endl; } wepon& wepon::operator = (const wepon & w){ damage = w.damage; cout << "weopn的赋值运算符重载" << endl; return *this; }
Main.cpp
// ConsoleApplicationC++jichu13.cpp : Defines the entry point for the console application. // #include "stdafx.h" //#include "Human.h" #include "Wepon.h" #include "Man.h" #include "iostream" using namespace std; void testXuHanShu(wepon * w1){ cout << "==============测试虚函数" << endl; //char x[20] = "xiaphua"; HuMan *p_Human = new Man("xiaphua", 22, 0, *w1); //-- 父类指针指向子类对象,父类写虚 子类重写,则它的表现是子类的,否则是父类的 p_Human->xuHanShuTestFunc(); //-- 只在父类声明并定义了xuHanShuTestFunc2函数 p_Human->xuHanShuTestFunc2(); //-- 显示调用父类的xuHanShuTestFunc p_Human->HuMan::xuHanShuTestFunc(); //-- 在delete的时候会调用,HuMan的析构函数, 因为我们还要让基类部分占用内存也要释放掉, 所以可以利用多态,将基类析构写成虚析构,这样delete p_Human 就会先调用父类 再调用子类的析构函数了。 delete p_Human; cout << "==============测试虚函数" << endl; } int _tmain(int argc, _TCHAR* argv[]) { char n[20] = "xiaoming"; /* 正常调用构造,析构函数。 new delete承对出现 */ //HuMan *h1 = new HuMan(n, 22); //HuMan* h2 = new HuMan("xiaoewang", 33); //delete h1; //delete h2; wepon * w1 = new wepon("d", 5); //--执行wepon的构造 Man * m1 = new Man(n, 20, 1, *w1); //-- 首先传入实参*w1给形参时,调用wepon的拷贝构造,然后再执行man的构造, //-- 在man构造函数的初始化列表执行human的构造, //-- 在man构造函数的初始化列表执行 w(w1),调用wepon的拷贝构造,用来初始化 *m1对象的wepon类型成员w,(当m1 指向的内存被销毁,*m1对象的wepon类型的成员变量w也会被销毁,会调用wepon的析构函数) //-- 在man构造函数的函数体执行完毕,执行完毕到 } 后执行wepon的析构,这个析构是函数的参数 wepon w1 局部变量 在函数执行完毕被销毁时出发的。 //-- 即:拷贝构造也属于构造函数,也存在对应的析构函数的调用。(自己总结:也许理解不精确,通常情况下 构造函数+拷贝构造数量 的调用次数 应该等于 析构函数的调用次数) cout << "w1->damage=" << w1->damage << endl; cout << "m1->w.damage=" << m1->w.damage << endl; w1->damage = 888; cout << "m1->w.damage=" << m1->w.damage << endl; //-- 在C++的继承中,子类会覆盖父类中的同名函数,不论此函数的返回值、参数。(父子类只要成员函数名相同,父类就会覆盖子类) m1->hanShuFuGaiTestFunc(1); //m1->hanShuFuGaiTestFunc(); NG m1->HuMan::hanShuFuGaiTestFunc(); //-- 因为函数是public 性质的,所以也可以这样显式访问该函数。 getchar(); testXuHanShu(w1); getchar(); delete w1; //-- 执行wepon的析构 delete m1; //-- 执行man的析构 ,因为m1对象有wepon类型成员,所以也要执行wepon的析构,最后执行父类的析构 getchar(); return 0; }