C++:继承性

简介: C++:继承性

一、基本概念

  • 一个基类可以派生多个派生类,一个派生类也可以由多个基类派生而成
  • 继承
  • 单一继承
  • 多重继承
  • 继承方式(缺省默认: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;
}
目录
相关文章
|
5月前
|
编译器 C++
【C++】详解C++的继承
【C++】详解C++的继承
|
2月前
|
安全 程序员 编译器
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
86 11
|
2月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
54 1
|
2月前
|
C++
C++番外篇——虚拟继承解决数据冗余和二义性的原理
C++番外篇——虚拟继承解决数据冗余和二义性的原理
42 1
|
2月前
|
安全 编译器 程序员
C++的忠实粉丝-继承的热情(1)
C++的忠实粉丝-继承的热情(1)
20 0
|
2月前
|
编译器 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
32 0
|
2月前
|
程序员 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
33 0
|
3月前
|
C++
C++(二十)继承
本文介绍了C++中的继承特性,包括公有、保护和私有继承,并解释了虚继承的作用。通过示例展示了派生类如何从基类继承属性和方法,并保持自身的独特性。此外,还详细说明了派生类构造函数的语法格式及构造顺序,提供了具体的代码示例帮助理解。
|
3月前
|
C++
c++继承层次结构实践
这篇文章通过多个示例代码,讲解了C++中继承层次结构的实践应用,包括多态、抽象类引用、基类调用派生类函数,以及基类指针引用派生类对象的情况,并提供了相关的参考链接。
|
4月前
|
安全 Java 编译器