C++继承

基类与派生类

继承是指当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。

#include <iostream>

using namespace std;

class Shape
{
public:
    void setWidth(int w)
    {
        width = w;
    }
    void setHeight(int h)
    {
        height = h;
    }
protected:
    int width;
    int height;
};

class Rectangle: public Shape
{
public:
    int getArea()
    {
        return (width * height);
    }
};

int main(void)
{
    Rectangle Rect;
    Rect.setWidth(5);
    Rect.setHeight(7);
    cout << "Total area: " << Rect.getArea() << endl;
    return 0;
}

其中Shape为基类,Rectangle为派生类,Rectangle继承了Shape的成员变量和成员函数。并且一个派生类可以继承多个基类,继承形式如下:

class<派生类名>:<继承方式><基类名>,<继承方式><基类名>
{
<派生类新定义成员>
};

其中继承方式有三中:

  • public 表示公有继承;
  • protected 表示保护继承;
  • private 表示私有继承;

继承与访问控制权限

类的控制访问权限

在讲到继承的控制访问权限之前,我们需要先复习一下类的控制访问权限。C++的类有三种控制访问权限。

private能够被访问的情况:

  • 该类中的函数
  • 其友元函数访问。

不能访问情况:

不能被任何其他访问,该类的对象也不能访问。

protected可以被访问情况:

  • 该类中的函数
  • 子类的函数
  • 其友元函数访问。

不能被该类的对象访问。

public可以被访问情况

  • 该类中的函数
  • 子类的函数
  • 其友元函数访问。
  • 类的对象

如表格所示

控制权限成员函数友元函数子类成员函数类的对象
public
protected×
private××

继承类型

当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。继承类型是通过上面讲解的访问修饰符来指定的。但是在继承过程中权限会发生相关改变。

  • 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
  • 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
  • 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。

因此在实际使用中很少使用private或protected继承。

这里有一个盲区需要我们注意:class和struct的区别并不在于class既有成员函数又有成员变量,struct只有成员变量,而是访问权限的不同,class的默认访问权限为private,struct的默认访问权限为public

派生类的构造与析构函数

派生类会继承所有的数据,尽管有的数据无法访问,但是不会继承基类的构造函数和析构函数以及基类的友元函数。需要注意的一点是:基类必须定义一个虚析构函数,目的是为了内存泄露,当使用虚函数的时候,会告诉编译器具体执行哪一个析构函数。具体原因可以参开基类为什么要定义虚析构函数

当一个派生类对象被创建时:

  • 首先在栈或者堆上面为整个对象分配空间
  • 调用基类构造函数初始化基类数据
  • 调用派生类构造函数

当一个子类对象被显示delete或者生命周期j结束

  • 调用派生类析构函数
  • 调用基类析构函数
  • 回收该对象的内存资源

如下面一段代码

#include <iostream>

using namespace std;

class Shape
{
public:
    Shape();
    virtual ~Shape();
};

Shape::Shape()
{
    cout<<"基类构造函数被调用"<<endl;
}

Shape::~Shape()
{
    cout<<"基类析构函数被调用"<<endl;
}

class Rectangle:public Shape
{
public:
    Rectangle();
    ~Rectangle();
    int t=0;
private:
    int *a;
};

Rectangle::Rectangle()
{
    cout<<"派生类构造函数被调用"<<endl;
}

Rectangle::~Rectangle()
{
    cout<<"派生类析构函数被调用"<<endl;
}

int main(void)
{
    Shape *s=new Rectangle();
    cout<<s.t<<endl;
    delete s;
    return 0;
}

如果Shape的析构函数不为虚函数,那么在delete的时候只会调用基类析构函数而不会调用派生类构造函数。

多继承初始化和析构顺序

当派生类为多继承时,构造函数初始化顺序还是先初始化基类,然后初始化继承类。初始化的顺序,基类构造函数执行顺序与继承声明顺序有关,构造函数刚好与析构函数相反。

#include <iostream>

using namespace std;

class A
{
public:
    A();
    virtual ~A();
};

A::A()
{
    cout<<"Constructor A is called"<<endl;
}

A::~A()
{
    cout<<"Destructor A is called"<<endl;
}


class B
{
public:
    B();
    virtual ~B();
};

B::B()
{
    cout<<"Constructor B is called"<<endl;
}

B::~B()
{
    cout<<"Destructor B is called"<<endl;
}

class C
{
public:
    C();
    virtual ~C();
};

C::C()
{
    cout<<"Constructor C is called"<<endl;
}

C::~C()
{
    cout<<"Destructor C is called"<<endl;
}

class D:public B,public C,public A
{
public:
    D();
    ~D();
};

D::D()
{
    cout<<"Constructor D is called"<<endl;
}

D::~D()
{
    cout<<"Destructor D is called"<<endl;
}

int main(void)
{
    A *a = new D();
    delete a;
    return 0;
}

继承的类型转换

我们可以将基类的指针或者引用绑定到派生类对象中基类的部分,这种转换叫做派生类到基类的类型转换。

例如

Shape *s=new Rectangle();

但是这个过程是不可逆的,所以不存在从基类到派生类的类型转换。原因在于基类对象只有积累本身的成员,而派生类有自身成员或者从其他基类继承了成员,如果用基类给派生类复制,那么派生类就能够访问不存在的数据。例如:

class A
{
public:
    int x;
};

class B:public A
{
public:
    int y;
};

B *b=new A();
cout<<b.y<<endl;

此时通过基类A生成的对象时没有成员变量y,因此b.y是在访问一个不存在的变量,编译器为了消除这种错误,所以不允许从基类向派生类的转换。

继承与静态成员 :如果基类中定义了一个静态成员,那么整个继承体系中只有一个静态成员。

防止继承:对于某一些类,我们希望它被继承,这是需要利用final防止其被继承

形如:

class Shape final {/* code */};//此时shape不能够被继承
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值