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

c1a0d196-7b43-4305-b501-eee813009900

继承成员对外的访问的属性: 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,当子类与父类函数名相同的时候,那么子类是没有办法调用父类的同名函数的,因为父类的同名函数被编译器做隐藏的处理,

原文地址:https://www.cnblogs.com/qxj511/p/5217361.html