程序员

二、继承

2020-05-09  本文已影响0人  __bba3

(1)继承

<1>语法
class 派生类:访问限定符 基类{
        结构体
};

如果不写访问限定符,默认是private继承。

(2)访问权限

  • 1.子类继承了父类所有的成元变量和成员函数。与访问限定符无关。访问限定符只是限制了访问。
  • 2.子类内部要想访问父类的private成员,可以把private改为public或者protected(常用)
<1>子类内部的访问权限
继承方式\父类成员 public protected private
public继承 public protected 不可见
protected继承 protected protected 不可见
private继承 private private 不可见

子类内部访问父类成员,只能访问publicprotected成员。

<2>子类对象的访问权限

注意:
(1)子类只有public继承父类的时候,才能访问父类的public成员,其他都不能访问。通常子类使用public继承父类。
(2)public继承,可以访问,不能直接修改基类里的private成员,可以在父类中用接口来修改其私有变量的值。

(3)继承关系的构造顺序

<1>派生类的构造顺序

注意:没有默认构造函数的基类在派生类的初始化,必须在初始化列表中初始化。比如在三角形实例中。

class Member{
public:
    Member(int i){ cout << __func__ << endl;}
    ~Member(){ cout << __func__ << endl;}
};
class Father{
public:
    Father(int i){ cout << __func__ << endl;}
    ~Father(){ cout << __func__ << endl;}
};
class Son:public Father{
    Member m;
public:
    //没有默认构造函数的父类在子类中初始化,必须使用初始化列表的方式
    Son():m(1),Father(1){ cout << __func__ << endl;}
    ~Son(){ cout << __func__ << endl;}
};
int main(){
    Son s;
}
结果:
Father
Member
Son
~Son
~Member
~Father

(4)同名隐藏规则

<1>概念

如果子类与父类成员函数同名,无论参数是否相同,子类会隐藏父类的成员函数。

<2>访问同名隐藏的父类
class Father{
public:
    void Test(){
        cout << "Father::Test()" << endl;
    }
    void Test(int i){
        cout << "Father::Test(" <<i <<")" <<endl;
    }
};
class Son:public Father{
public:
    using Father::Test;//(1)只能解决参数不同的同名隐藏
    void Test(){
        cout << "Son::Test()" << endl;
    }
};
int main(){
    //不采用任何情况
    Son s;
    s.Test();//访问的是子类的成员函数
    s.Test(3)//会报错!
    //如果要调用父类的方法:
//方法1:可以完全解决同名隐藏
    s.Father::Test(2);
    s.Father::Test();   
//法(2):使用using 。。。,只能解决同名不同参的问题
    s.Test(3);
    //s.Test();//不能调用父类的Test()  
}
<3>对象查找函数的规则

对象首先在子类中查找,如果没有在子类找到,会到父类中查找;如果子类中找到了同名的函数,并且参数相同就会执行该函数,如果参数不同就会报错,并不会再往父类中查找了。
注意:指针访问和.访问的规则一样。

(5)赋值兼容

<1>前提

必须是公有继承,才能谈赋值兼容。

<2>概念
<3>赋值兼容有三种情况:
Base base;
Derive derive;
base = derive;
class Base{
public:
    int  n;
};
class Derive:public Base{
public:
    int m;
};
void Func(Base b){}
Base Func(){
      Derive d;
      return d;
}
int main(){
    Base b;
    Derive d;
    d.n=10;
    d.m=100;
    b=d;//调用Base的赋值运算符重载
    //d=b;//错误,不可以这样赋值
    cout << b.n<<endl;//4
    //cout << b.m<<endl;//Base对象不能访问到子类的任何成员(对象切割)
    cout << sizeof(b)<<endl;//4
    cout << sizeof(d)<<endl;//8
    Base b2(d);//子类对象初始化父类对象(调用Base的拷贝构造函数)
子类对象给父类对象赋值的应用:
//1.函数参数对象传值  
    Func(d);//调用Base的拷贝构造函数
//2.函数返回对象
    Func();////调用Base的拷贝构造函数
Derive d;
Base& fb = d;//子类对象初始化父类的引用,类型决定大小和能否访问的成员
1.d的地址和fb的地址是一样的!
cout << &d <<endl;
cout << &fb <<endl;
2.初始化父类的对象是不能访问子类成员的。
cout << sizeof(d) <<endl;//8
cout << sizeof(fb)<<endl;//4
d.Test();//子类对象可以访问子类的成员
fb.Test();//父类引用虽然指向子类对象,但是不能访问子类成员,只能访问父类的成员
3. 应用:
void Func(Base& b){}//定义
Func(d)//调用.(这里是引用不会调用拷贝构造函数,值传才会调用拷贝构造)
Base* pb=&d;
1.d的地址和fb的地址是一样的!
cout << pb <<endl;
cout << &d <<endl;
2.初始化父类的对象是不能访问子类成员的。
pb->Test();//只能访问父类特有的成员 
pb->m;
3. 应用;
void Func(Base* b){}//定义
Func(&d);//调用

注意:
1.父类引用子类对象与父类指针指向子类对象,都只能访问子类所继承的那部分成员
2.即使父类和子类函数名和参数完全相同,也不能访问到子类的成员,要想访问与其同名的子类成员,就要实现多态

(6)多重继承

一个类可以同时继承多个父类的行为和特征功能。

<1>格式
class 类名 : public 基类1,public 基类2{};
<2>大小

多重继承的大小,等于继承的所有父类大小之和。

<3>多重继承基类构造顺序

父类在子类的继承顺序就是子类对象的初始化顺序,如果父类有继承先会初始化父类的继承。

class Base{
public:
    Base(){cout << __func__ <<endl;}
    ~Base(){cout << __func__ <<endl;}
    int n;
};
class A:public Base{
public:
    A(){cout << __func__ <<endl;}
    ~A(){cout << __func__ <<endl;}
};
class B:public Base{
public:
    B(){cout << __func__ <<endl;}
    ~B(){cout << __func__ <<endl;}
};
class C:public A,public B{
public:
    C(){cout << __func__ <<endl;}
    ~C(){cout << __func__ <<endl;}
}; 
int main(){
     C c;
     cout << "Base size:" << sizeof(Base) << endl;//4
     cout << "A size:" << sizeof(A) << endl;//4
     cout << "B size:" << sizeof(B) << endl;//4
     cout << "C size:" << sizeof(C) << endl;//4+4=8
执行结果:Base--->A--->Base--->B---->C---->~C---->~B---->~Base---->~A----->~Base
}
<4>多重继承的危害

钻石继承/菱形继承。(有两套相同的成员函数和成员变量,造成成员冲突)

(7)钻石继承/菱形继承

<1>概念

两个子类B,C继承同一个父类A,而又有子类D同时继承这两个子类B,C,继承关系画成一个图刚好是菱形。

<2>产生的问题
void Print()const{
//成员变量
        cout << "a:" << EqualTriangle::a <<endl;//指明成员变量属于那个类。
        cout << "b:" << EqualTriangle::b <<endl;
        cout << "c:" << EqualTriangle::c <<endl;
        cout << "a:" << RightTriangle::a <<endl;
        cout << "b:" << RightTriangle::b <<endl;
        cout << "c:" << RightTriangle::c <<endl;
//成员函数
         cout << er.EqualTriangle::Getlength() << endl;
      cout << er.RightTriangle::Getlength() << endl;

说明:等腰直角三角形同时继承了等腰三角形和直角三角形,当调用Getlength时,不知道调用的是等腰三角形的还是直角三角形的。

<3>虚继承

虚基类是一个相对概念,在虚继承关系中,父类相对与子类是虚基类

class 类名:public virtual 基类{
}
class EqualTriangle:public virtual Triangle{//添加虚继承
class RightTriangle:public virtual Triangle{//添加虚继承
class EqualRightTriangle:public EqualTriangle,public RightTriangle{
      EqualRightTriangle(floatright):EqualTriangle(sqrt(right*right*2),right),
RightTriangle(right,right),Triangle(right,right,sqrt(right*right*2)){}//所有的构造函数都必须初始化,但是如果父类和祖父类是默认的构造函数,则不用写。 
void Print()const{
        cout << "a:" << a <<endl;//虚继承后可以直接访问祖父类的成员变量
        cout << "b:" << b <<endl;
        cout << "c:" << c <<endl;
}
};
int main(){
cout << sizeof(EqualTriangle) <<endl;//12(3*4)  ---->24(8(一个指向虚继承的指针)+3*4+4(补齐))
cout << sizeof(RightTriangle) << endl;//12  ---->24
cout << sizeof(EqualRightTriangle) <<endl;//24(12+12)---->32(8*2(加了两个指针)+3*4+4(补齐))
cout << er.GetArea() << endl;//调用的是父类(RightTriangle)函数,不是祖父类的。
}

虚继承的构造和析构顺序:

class Base{
public:
    Base(){cout<<__func__<<endl;}
    ~Base(){cout<<__func__<<endl;}
};
class A:public virtual Base{
public:
    A(){cout<<__func__<<endl;}
    ~A(){cout<<__func__<<endl;}
};
class B:public virtual Base{
public:
    B(){cout<<__func__<<endl;}
    ~B(){cout<<__func__<<endl;}
};
class C:public A,public B{ // 父类在子类中的继承顺序就是子对象中父类的构造顺序
public:
    C(){cout<<__func__<<endl;}
    ~C(){cout<<__func__<<endl;}
};
class Emply{};
int main(){
    C c;
    cout << sizeof(Base) << endl;
    cout << sizeof(A) << endl;
    cout << sizeof(B) << endl;
    cout << sizeof(C) << endl;
    // 空类
    cout << sizeof(Emply) << endl;
}
结果:
Base
A
B
C
1
8
8
16
1
~C
~B
~A
~Base
<4>关于多重继承总结

(1)什么是多重继承?同时继承多个父类。
(2)多重继承有什么危害?菱形继承/钻石继承。
(3)什么是菱形继承/钻石继承?多重继承的两个或多个父类具有相同的祖先类。
(4)菱形继承/钻石继承有什么危害?因为多重继承的两个或多个父类具有相同的祖先类。所以会有完全相同的属性和方法。因此当前多重继承类有两份相同的属性和方法。使用时会出现冲突。
(5)如何解决菱形继承/钻石继承导致的冲突?使用虚继承
(6)什么是虚继承?父类在继承具有相同的祖先类时,加上virtual.

(8)对象构造顺序总结

上一篇下一篇

猜你喜欢

热点阅读