7、继承
1.继承
儿子继承父亲的遗产,儿子拥有父亲所有的成员变量和成员函数。儿子也可以拥有父亲没有的成员变量和成员函数。
子类与父类继承的关系,
也就是说:
(1)子类拥有父类所有的成员变量和成员函数(除了构造函数和析构函数)。
(2)子类的对象可以当作父类的对象使用(认为子类是特殊的一个父类)
(3)子类可以用父类没有的成员变量和成员函数。
class Parent { public: Parent(); ~Parent(); void pri(); private: int a; }; Parent::Parent() { a = 100; } Parent::~Parent() { } void Parent::pri() { cout << "a = " << a << endl; } class Child : public Parent // 继承父类 { }; int main() { Parent p1; p1.pri(); Child c1; c1.pri(); while (1); }
都可以打印出 a = 100;,这里就涉及到继承的方式的问题:
class 子类名 : 继承的方式 父类名 { }
当没有指定 继承的方式(public,private)的话,那么就是默认为私有的继承。
继承的权限:
继承的方式
public 父类在子类中,保持原有的访问的级别,子类可以访问父类的共有成员,私有成员不能被访问
private 父类在子类中,全部变为private的访问级别,那么子类不可以访问父类所有的成员
一般来说,我们都是 public 的方式进行继承,但是 private 成员可以被子类继承,但是不能被子类访问,所以,就有了了继承的关键字:protected
关键字 protected:
介于 private 和 public 之间,也就是说,被 protected 修饰的变量:
(1)可以在类内被访问
(2)可以被子类访问
(3)不可以被外接(通过对象,不管是父类还是子类都不可以被访问)访问
class Parent { public: Parent(); ~Parent(); void pri(); protected: // 被保护的,子类就可以访问 int a; }; Parent::Parent() { a = 100; } Parent::~Parent() { } void Parent::pri() { cout << "a = " << a << endl; } class Child : public Parent // 继承父类 { protected: // int b; public: void setDATA(int a, int b) { this->a = a; this->b = b; // b 是被保护的,所以可以被子类访问 } void cintData() { cout << "a = " << a <<" "<< "b = " << b << endl; } }; int main() { Parent p1; p1.pri(); Child c1; c1.setDATA(1, 2); c1.cintData(); while (1); }
如果不是被 protected 修饰,而是被 private 修饰的话,那么子类压根就没有权限去访问父类的 private 的成员变量,这个就不是我们想看到的,
权限的设置:
(1)需要被外接访问,则使用 public
(2)只能在类内被访问的成员的话,则是设置为 private
(3)只能在当前类内和子类中的访问的成员,则是设置为 protected
继承成员对外的访问的属性: MAX(继承的方式,父类的访问的级别)
继承访问级别设置的原则:
需要被外接(对象)访问的成员直接设置为: public
只能在类的内部被访问的成员设置为 : private
只能在当前类和子类中访问的成员设置为 : protected
继承中的构造和析构函数:
子类和父类的继承,那么子类已经是继承了父类所有的成员函数和成员变量(除了构造函数和析构函数、拷贝构造函数、operator函数、友元函数没有被继承、)。
子类的对象构造的时候,会调用父类的构造函数。父类的构造函数完成父类的构造,而子类的构造函数则是完成自己新增的成员进行初始化;因此析构的函数也是一样的,子类的就析构自己的就可以,继承的东西,就让父类的析构函数自己去完成清理的工作。
class Parent { public: Parent(); ~Parent(); private: }; Parent::Parent() { cout << "ia= am parent gouzao" << endl; } Parent::~Parent() { cout << "ia= am parent xigou" << endl; } class Child : public Parent { public: Child (); ~Child (); private: }; Child :: Child() { cout << "ia= am Child gouzao" << endl; } Child ::~Child() { cout << "ia= am Child xigou" << endl; } void run() { Child c1; } int main() { run(); while (1); } 输出的结果: ia= am parent gouzao ia= am Child gouzao ia= am Child xigou ia= am parent xigou
可见,子类对象创建的时候,是先自动调用父类的构造函数,执行完毕之后再指定子类的构造函数;虚构函数是先析构子类的构造函数,然后再执行父类的析构函数;同时可以看到,析构函数是构造函数的完全相反地执行。
继承与组合:
组合就是自身的成员是其他类的对象,这个就叫组合,那么构造函数和析构函数的执行的顺序,
class Obj { public: Obj(char *s) { cout << "obj" << " " << s << endl; } }; class Parent : public Obj { public: // 初始化列表 Parent(char *s) : Obj(s) { cout << "parent" << " " << s << endl; } }; class Child : public Parent { private: Obj o1; // 来气其他类的对象 Obj o2; // 两个对象,那么就是两次 public: Child() : o1("o1"), o2("o2"), Parent("paren from") { cout << "child" << endl; // 最后才执行自己 } }; int main() { Child c1; while (1); } 执行结果: obj paren from // 先父母的父母 parent paren from // 接着父母 obj o1 // 再别人 obj o2 // 再别人 child // 最后自己
构造函数执行的顺序: 先父母,在别人,最后自己;首先可以看到,child 继承 父母,而父母则是继承 obj,很显然必须是先执行 obj 的打印(构造函数),再执行 parent 的构造函数;接着是别人,child 里面有两个 对象作为自己的成员变量,所以就执行两次 obj 的构造函数;最后才是执行自己的 child 打印
子类和父类的成员同名的时候:
(1)子类的成员变量和父类的成员变量同名的时候:
A、子类依然继承父类同名的成员名
B、子类中通过作用域分别符 :: 来进行区分是哪一个成员
C.、同名的成员存储在内存中的不同的位置
class Parent { protected: int i; public: }; class Child : public Parent { protected: int i; public: Child(int i) { Parent::i = i; // 通过作用域进行指定同名的成员变量 Child::i = i + 1; } void pri() { // 打印父类和子类的 i 值 cout << "parent ::i = " << Parent::i << endl; cout << "Child ::i = " << Child::i << endl; cout << "parent i dizhi" << &(Parent::i) << endl; cout << "parent i dizhi" << &(Child::i )<< endl; } }; int main() { Child c1(5); c1.pri(); while (1); }
成功打印出: 5 和 6 ,而且 不同 i 的地址是不一样的,也就是说,i 是存储在不同的内存的位置;通过 类名 + ""(作用域操作符)进行区分;
(2)子类与父类的函数同名:
class Parent { public: void f() { cout << "f A" << endl; } }; class Child : public Parent { public: void f() { cout << "f B" << endl; } }; int main() { Child c1; c1.f(); while (1); }
while (1);
}
输出的结果是: f B,当子类与父类函数名相同的时候,那么子类是没有办法调用父类的同名函数的,因为父类的同名函数被编译器做隐藏的处理,