C++:继承性

发布于:2024-04-26 ⋅ 阅读:(22) ⋅ 点赞:(0)

一、基本概念

  • 一个基类可以派生多个派生类,一个派生类也可以由多个基类派生而成
  • 继承
    • 单一继承
    • 多重继承
  • 继承方式(缺省默认:private)
    • public
    • private
    • protected

公有继承

保护继承

私有继承

公有成员

public protected private
保护成员 protected protected private
私有成员 不可见 不可见 不可见
  • 基类的 private 成员不可以被继承

二、派生类的构造及析构

#include <iostream>
using namespace std;

class a1
{
public:
    a1()
    {
        cout << "a1 Constructor called" << endl;
    }
    ~a1()
    {
        cout << "a1 Destructor called" << endl;
    }
};

class a2
{
public:
    a2()
    {
        cout << "a2 Constructor called" << endl;
    }
    ~a2()
    {
        cout << "a2 Destructor called" << endl;
    }
};

class Derived : public a1, public a2
{
public:
    Derived()
    {
        cout << "Derived Constructor called" << endl;
    }
    ~Derived()
    {
        cout << "Derived Destructor called" << endl;
    }
};

int main()
{
    Derived obj;
    return 0;
}

输出结果:

a1 Constructor called
a2 Constructor called
Derived Constructor called
Derived Destructor called
a2 Destructor called
a1 Destructor called 

在定义一个派生类对象时,构造函数的调用顺序:

        基类 >>> 派生类对象成员(按定义顺序) >>> 派生类

析构函数调用顺序恰好相反

//将 Derived 修改如下
class Derived : public a2
{
private:
    a1 obj1;
public:
    Derived()
    {
        cout << "Derived Constructor called" << endl;
    }
    ~Derived()
    {
        cout << "Derived Destructor called" << endl;
    }
};

a2 Constructor called
a1 Constructor called
Derived Constructor called
Derived Destructor called
a1 Destructor called
a2 Destructor called

有参情况

  1. 派生类只需要负责直接基类构造函数的调用

  2. 如果基类构造函数不需要提供参数,则无需在初始化列表中给出

  3. 创建对象构造函数的调用顺序与声明顺序有关,而非在初始化列表中的顺序

  4. 其他初始化项包括对象成员,常成员和引用成员

示例

#include <iostream>
using namespace std;

class Base
{
private:
    static int count;
    int x;
public:
    Base(int i)
    {
        x=i;
        cout<<"Base constructor called"<<count++<<endl;
    }
    void display()
    {
        cout<<"x = "<<x<<endl;
    }
};

class Derived : public Base
{
private:
    Base b;
public:
    Derived (int i): Base(i),b(i)
    {
        cout<<"Derived constructor called"<<endl;
    }
};

int Base::count=0;

int main() 
{
    Derived d(3);
    d.display();
    return 0;
}

多重继承示例

#include <iostream>
using namespace std;

class Grand
{
private:
    int a;
public:
    Grand(int n):a(n)
    {
        cout << "Grand c,a=" << a << endl;
    }
    ~Grand()
    {
        cout << "Grand d" << endl;
    }
};

class Father:public Grand
{
private:
    int b;
public:
    Father(int n1,int n2):Grand(n1),b(n2)
    {
        cout << "Father c,b=" << b << endl;
    }
    ~Father()
    {
        cout << "Father d" << endl;
    }
};

class Mother
{
private:
    int c;
public:
    Mother(int n):c(n)
    {
        cout << "Mother c,c=" << c << endl;
    }
    ~Mother()
    {
        cout << "Mother d" << endl;
    }
};

class Child:public Father,public Mother
{
private:
    int d;
public:
    Child(int n1,int n2,int n3,int n4):Father(n4,n3),Mother(n2),d(n1)
    {
        cout << "Child d=" << d << endl;
    }
    ~Child()
    {
        cout << "Child d" << endl;
    }
};


int main()
{
    Child c(1,2,3,4);
    return 0;
}

Grand c,a=4
Father c,b=3
Mother c,c=2
Child d=1
Child d
Mother d
Father d
Grand d 

三、同名冲突

基类与派生类的同名冲突

同名覆盖原则:新成员名称与基类某个成员同名时,若未加任何特殊标识,访问派生类中新定义的同名成员

需要访问基类:使用 “基类名::” 进行限定

  • 通过派生类的指针或引用,访问的是派生类的同名成员(同名覆盖√)
  • 基类指针/引用,访问基类同名成员
#include <iostream>
using namespace std;

class Base
{
public:
    int a;
    Base(int x)
    {
        a = x;
    }
    void Print()
    {
        cout << "Base::a = " << a << endl;
    }
};

class Derived : public Base
{
public:
    int a;      //欸这里也有个a耶
    Derived(int x, int y) : Base(x)
    {
        a = y;
        Base::a *= 2;
    }
    void Print()
    {
        Base::Print();
        cout << "Derived::a = " << a << endl;
    }
};

void Test1(Base& b)
{
    b.Print();
}

void Test2(Derived& d)
{
    d.Print();
}

int main()
{
    Derived d(200, 300);
    d.Print();
    d.a = 400;
    d.Base::a = 500;
    d.Base::Print();
    Base* pb;
    pb = &d;
    pb->Print();
    Test1(d);
    Derived *pd;
    pd = &d;
    pd->Print();
    Test2(d);

    return 0;
}

多重继承中直接基类的同名冲突

通过域解析符解决

#include <iostream>
using namespace std;

class Base1
{
protected:
    int a;
    Base1(int x)
    {
        a = x;
        cout<<"Base1 a="<<a<<endl;
    }
    void Print()
    {
        cout << "Base::a = " << a << endl;
    }
};

class Base2
{
protected:
    int a;
public:
    Base2(int x)
    {
        a = x;
        cout<<"Base2 a="<<a<<endl;
    }   
};

class Derived:public Base1, public Base2
{
public:
    Derived(int x,int y):Base1(x),Base2(y)
    {
        Base1::a *=2;
        Base2::a *=2;
        cout<<"Derived from Base1::a="<<Base1::a<<endl;
        cout<<"Derived from Base2::a="<<Base2::a<<endl;
    }
};


int main()
{
    Derived d(10,20);
    return 0;
}

共同祖先基类引发的同名冲突

  1. 域解析符
  2. 虚基类

虚基类

virtual 确保虚基类最多被调用一次

#include <iostream>
using namespace std;

class Base 
{
protected:
    int a;
public:
    Base (int x):a(x)
    {
        cout<<"Base a="<<a<<endl;
    }
    ~Base ()
    {
        cout<<"Base destructor"<<endl;
    }
};

class Base1 : public virtual Base
{
protected:
    int b;
public:
    Base1(int x,int y):Base(y),b(x)
    {
        cout<<"Base1 from Base a="<<a<<endl;
        cout<<"Base1 b="<<b<<endl;
    } 
};

class Base2 : public virtual Base 
{
protected:
    int c;
public:
    Base2(int x,int y):Base(y),c(x)
    {
        cout<<"Base2 from Base a="<<a<<endl;
        cout<<"Base2 c="<<c<<endl;
    }
};

class Derived : public Base1, public Base2 
{
public:
    Derived(int x,int y):Base1(x,y),Base2(2*x,2*y),Base(3*x)
    {
        cout<<"a="<<a<<endl;
        cout<<"Base::a="<<Base::a<<endl;
        cout<<"Base1::a="<<Base1::a<<endl;
        cout<<"Base2::a="<<Base2::a<<endl;
        cout<<"b="<<b<<endl;
        cout<<"c="<<c<<endl;
    }
    ~Derived ()
    {
        cout<<"Derived destructor"<<endl;
    }
};

int main() 
{
    Derived d(10,20);
    return 0;
}

其中 Base 类只有一份复制

只有最后一层派生类对虚基类构造函数的调用发挥作用

创建一个对象,构造函数调用次序:

虚基类的构造函数

直接基类的构造函数

对象成员的构造函数

派生类自己的构造函数

 四、赋值兼容规则

使公有派生类可以当作基类来使用

1. 派生类对象 -> 基类对象

2. 派生类对象地址 -> 基类指针

3. 派生类对象指针 -> 基类指针

3.派生类对象 -> 基类引用        

#include <iostream>
using namespace std;

class Base
{
private:
    int b;
public:
    Base(int x):b(x)
    {}
    int getB()
    {
        return b;
    }
};

class Derived : public Base
{
private:
    int d;
public:
    Derived(int x, int y):Base(x), d(y)
    {}
    int getD()
    {
        return d;
    }
};

int main()
{
    Base b(11);
    Derived d(22, 33);
    
    b = d;
    cout << "b.getB() = " << b.getB() << endl;

    Base *bp = &d;
    cout << "bp->getB() = " << bp->getB() << endl;

    Derived *dp = &d;
    Base *bp2 = dp;
    cout << "bp2->getB() = " << bp2->getB() << endl;

    Base &rb = d;
    cout << "rb.getB() = " << rb.getB() << endl;
    
    return 0;
}