一、继承
1、概念:通过一种机制表达类型之间共性和特性的方式,利用已有的数据类型来定义新的数据类型,这种机制就是继承.
2、语法:
class 子类:继承方式1 基类1,继承方式2 基类2,...{
...
};
继承方式:
-->public(公有继承)
-->protected(保护继承)
-->private(私有继承)
#include <iostream> using namespace std; //人类(基类) class Human{ public: Human(const string& name,int age) :m_name(name),m_age(age){} void eat(const string& food){ cout << "我在吃" << food << endl; } void sleep(int hour){ cout << "我睡了" << hour << "小时" << endl; } protected://保护成员可以在类的内部和子类中访问 string m_name; int m_age; }; //学生类(人类派生的一个子类) class Student:public Human{ public: //Human(...):指明基类部分的初始化方式 Student(const string& name,int age,int no) :Human(name,age),m_no(no){} void learn(const string& course){ cout << "我在学" << course << endl; } void who(void){ cout << "我叫" << m_name << ",今年" << m_age << "岁,学号是" << m_no << endl; } private: int m_no; }; //教师类(人类派生的另一个子类) class Teacher:public Human{ public: Teacher(const string& name,int age, int salary):Human(name,age), m_salary(salary){} void teach(const string& course){ cout << "我在讲" << course << endl; } void who(void)const{ cout << "我叫" << m_name << ",今年" << m_age << "岁,工资是" << m_salary << endl; } private: int m_salary; }; int main(void) { Student s("关羽",30,10011); s.who(); s.eat("牛肉拉面"); s.sleep(8); s.learn("孙武兵法"); Teacher t("孙悟空",35,80000); t.who(); t.eat("桃子"); t.sleep(6); t.teach("unix系统编程"); //Student*-->Human*:向上造型 Human* ph = &s; ph->eat("兰州拉面"); ph->sleep(7); //ph->who();//error //Human*-->Student*:向下造型(合理) Student* ps = static_cast<Student*>(ph); ps->who(); Human h("林黛玉",22); //Human*-->Student*:向下造型(不合理) Student* ps2 = static_cast<Student*>(&h); ps2->who();//危险 //int * pi = new int(100); //Student* ps3 = (Student*)pi; //ps3->who(); return 0; }
运行结果: 我叫关羽,今年30岁,学号是10011 我在吃牛肉拉面 我睡了8小时 我在学孙武兵法 我叫孙悟空,今年35岁,工资是80000 我在吃桃子 我睡了6小时 我在讲unix系统编程 我在吃兰州拉面 我睡了7小时 我叫关羽,今年30岁,学号是10011 我叫林黛玉,今年22岁,学号是-858993460
3、子类继承基类的成员:公有、保护、私有
通过子类,可以直接访问基类中的公有成员和保护成员,就如同它们被声明在子类中一样,但基类中的私有成员,受到访问控制属性的影响,继承后不能直接访问,但是可以让基类提供公有或保护的接口函数来间接访问.
注:基类构造函数和析构函数子类无法继承。
#include <iostream> using namespace std; class Base {//基类 public: void public_func(void) { cout << "基类的公有成员" << endl; } protected: void protected_func(void) { cout << "基类的保护成员" << endl; } public: void getPrivate(void) { private_func(); } private: void private_func(void) { cout << "基类的私有成员" << endl; } }; class Derived :public Base {//子类 public: void func(void) { public_func(); protected_func(); //private_func();//error getPrivate(); } }; int main(void) { Derived d; d.func(); } 运行结果: 基类的公有成员 基类的保护成员 基类的私有成员
4、子类隐藏基类的成员
如果子类和基类中定义了同名的成员函数,因为作用域不同,不能构成重载关系,而是一种隐藏关系,当通过子类访问该成员时,将优先访问子类自己的成员。如果需要通过子类访问基类中被隐藏的成员,可以显式的通过"类名::"来指明。
如果形成隐藏关系的成员函数满足不同参的重载条件,也可以通过using声明,将基类中成员函数引入子类作用域,让它们形成有效的重载关系,通过重载匹配来解决//不推荐
#include <iostream> using namespace std; class Base{ public: void func(int i){ cout << "Base::func(int)" << endl; } }; class Derived:public Base{ public: void func(void){ cout << "Derived::func(void)" << endl; } //将基类中的func声明到当前子类作用域,让 //它们构成函数重载关系 //using Base::func; }; int main(void) { Derived d; d.Base::func(10); d.func(); return 0; } 运行结果: Base::func(int) Derived::func(void)
5、访问控制限定符:影响访问该类的成员的位置
访问控制 访问控制 内部 子类 外部 友元
限定符 属性 访问 访问 访问 访问
public 公有成员 ok ok ok ok
protected 保护成员 ok ok no ok
private 私有成员 ok no no ok
#include <iostream> using namespace std; class A {//基类 public: int m_public;//公有成员 protected: int m_protected;//保护成员 private: int m_private;//私有成员 }; class B :public A {//公有继承的子类 void func(void) { m_public = 123; m_protected = 123; //m_private = 123; } }; class C :protected A {//保护继承的子类 void func(void) { m_public = 123; m_protected = 123; //m_private = 123; } }; class D :private A {//私有继承的子类 void func(void) { m_public = 123; m_protected = 123; //m_private = 123; } }; class X :public B { void foo(void) { m_public = 123; m_protected = 123; //m_private = 123; } }; class Y :public C { void foo(void) { m_public = 123; m_protected = 123; //m_private = 123; } }; class Z :public D { void foo(void) { //m_public = 123; //m_protected = 123; //m_private = 123; } }; int main(void) { B b; b.m_public = 123;//ok //b.m_protected = 123; //b.m_private = 123; C c; //c.m_public = 123; //c.m_protected = 123; //c.m_private = 123; D d; //d.m_public = 123; //d.m_protected = 123; //d.m_private = 123; return 0; }
6、子类的构造函数
如果子类的构造函数没有显式指明基类子对象的初始化方式,那么编译器将会自动调用基类的无参构造函数来初始化基类子对象。
如果希望基类子对象以有参的方式被初始化,则必须使用初始化列表。
#include <iostream> using namespace std; class Member{ public: Member(void):m_j(0){ cout << "Member(void)" << endl; } Member(int j):m_j(j){ cout << "Member(int)" << endl; } int m_j; }; class Base{ public: Base(void):m_i(0){ cout << "Base(void)" << endl; } Base(int i):m_i(i){ cout << "Base(int)" << endl; } int m_i; }; class Derived:public Base{ public: Derived(void){ cout << "Derived(void)" << endl; } //Base(i):指明基类子对象的初始化方式 //m_mem(i):指明成员子对象的初始化方式 Derived(int i):Base(i),m_mem(i){ cout << "Derived(int)" << endl; } Member m_mem;//成员子对象 }; int main(void) { Derived d; cout << d.m_i << endl;//0 Derived d2(123); cout << d2.m_i << endl;//123 return 0; }
7、子类的析构函数
子类的析构函数,无论自己定义的析构函数还是编译器缺省提供的,都会自动调用基类的析构函数,完成基类子对象的销毁。
子类对象的销毁过程:执行子类析构函数的代码,析构成员子对象(按声明逆序),析构基类子对象(按继承表逆序),释放内存
基类的析构函数不能自动调用子类的析构函数,所以对一个指向子类对象的基类指针使用delete操作符,实际被调用的仅是基类的析构函数,子类的析构函数执行不到,有内存泄漏的风险。
#include <iostream> using namespace std; class Member{ public: Member(void):m_j(0){ cout << "Member(void)" << endl; } Member(int j):m_j(j){ cout << "Member(int)" << endl; } ~Member(void){ cout << "~Member(void)" << endl; } int m_j; }; class Base{ public: Base(void):m_i(0){ cout << "Base(void)" << endl; } Base(int i):m_i(i){ cout << "Base(int)" << endl; } ~Base(void){ cout << "~Base(void)" << endl; } int m_i; }; class Derived:public Base{ public: Derived(void){ cout << "Derived(void)" << endl; } //Base(i):指明基类子对象的初始化方式 //m_mem(i):指明成员子对象的初始化方式 Derived(int i):Base(i),m_mem(i){ cout << "Derived(int)" << endl; } ~Derived(void){ cout << "~Derived(void)" << endl; } Member m_mem;//成员子对象 }; int main(void) { /* Derived d; cout << d.m_i << endl;//0 Derived d2(123); cout << d2.m_i << endl;//123 */ Base* pb = new Derived; //... //pb->析构函数 delete pb;//内存泄露问题 return 0; } 运行结果: Base(void) Member(void) Derived(void) ~Base(void)