C++类的继承与派生

继承与派生概述

  • 继承与派生是同一过程从不同的角度看
    • 保持已有类的特征而构造新类的过程称为继承
    • 在已有类的基础上新增自己的特性而产生新类的过程称为派生
  • 被继承的已有类称为基类(或父类)
  • 派生出的新类称为派生类(或子类)
  • 直接参与派生出某类的基类称为直接基类
  • 基类的基类甚至更高层的基类称为间接基类

继承的目的

  • 实现设计与代码的重用

派生的目的

  • 当新的问题出现,原有程序无法解决(或不能完全解决)时,需对原有程序进行改造

单继承时派生类的定义
语法:

class 派生类名:继承方式 基类名
{
    成员声明;
}

例子:

#include<iostream>
using namespace std;
//定义一个点类  作为基类
class Point {
public:
    void get_x(int xx) {
        x = xx;
    }
    void get_y(int yy) {
        y = yy;
    }
    void show_point(){ 
        cout << "x is "<< x << endl;
        cout << "y is " << y << endl;
    }
private:
    int x,y;
};
//定义一个类B作为派生类
class B:public Point {
public:
    void show() { cout << "this is classB" << endl; }
};

//在主函数中,定义派生类对象func1,可直接调用基类成员
//但私有数据不能直接访问,只能通过基类中的外部接口访问
//(继承方式为public情况下)
int main()
{
    B func1;
    func1.show();
    func1.get_x(3);
    func1.get_y(6);
    func1.show_point();
    return 0;
}

程序运行结果:

this is classB
x is 3
y is 6
请按任意键继续. . .

多继承时派生类的定义
语法:

class 派生类名:继承方式1 基类名1,继承方式2 基类名2····
{
    成员声明;
}
注意:每一个“继承方式”,只用于限制对紧随其后之基类的继承

例子:

#include<iostream>
using namespace std;
//定义点类为基类
class Point {
public:
    void get_x(int xx) {
        x = xx;
    }
    void get_y(int yy) {
        y = yy;
    }
    void show_point(){ 
        cout << "x is "<< x << endl;
        cout << "y is " << y << endl;
    }
private:
    int x,y;
};
//类A为基类
class A{
public:
    void Show_A() { cout << "this is class A" << endl; }
};
//类B为点类 A类的派生类
class B:public Point, public A{
public:
    void Show_B() { cout << "this is class B"<<endl; }

};
//B类对象可直接访问基类public成员,不能访问private成员
//(继承方式为public情况下)
int main(void)
{
    B func1;
    func1.Show_B();
    func1.Show_A();
    func1.get_x(3);
    func1.get_y(6);
    func1.show_point();
    return 0;
}

派生类的构成

  • 吸收基类成员
    • 默认情况下派生类包含了全部基类中除构造和析构函数之外的所有成员C++11规定可以用using语句继承基类构造函数
  • 改造基类成员
    • 如果派生类声明了一个和某类成员同名的新成员,派生的新成员就隐藏或覆盖了外层同名成员
  • 添加新的成员

针对改造基类成员举例

#include<iostream>
using namespace std;
//基类A
class A{
public:
    void Show() { cout << "this is class A" << endl; }
};
//基类B
class B{
public:
    void Show() { cout << "this is class B"<<endl; }
};
//派生类Drived
class Drived:public A,public B {
public:
    void Show() { cout << "this is class Drived" << endl; }


};
//没个类中都有show()函数成员,但我们在调用派生类时
//打印为派生类中的show函数,可以通过一些方法按我们的意愿打印
//心目中的show函数,这里不说明
int main(void)
{
    Drived func1;
    func1.Show();
    return 0;
}

代码运行结果:

this is class Drived
请按任意键继续. . .

继承方式简介

不同继承方式的影响主要体现在:

  • 派生类成员对基类成员的访问权限
  • 通过派生类对象对基类成员的访问权限

三种继承方式:

  • 公有继承
  • 私有继承
  • 保护继承

公有继承

继承的访问控制

  • 基类的publicprotected成员:访问属性在派生类中保持不变
  • 基类的private成员:不能直接访问

访问权限

  • 派生类中的成员函数:可以直接访问基类中的publicprotected成员,但不能直接访问基类的private成员
  • 通过派生类的对象:只能访问public成员

公有继承示例代码:

#include<iostream>
using namespace std;

//定义基类
class Point {
public:
    void get_x(int xx) {
        x = xx;
    }
    void get_y(int yy) {
        y = yy;
    }
    void show_point(){ 
        cout << "x is "<< x << endl;
        cout << "y is " << y << endl;
    }
private:
    int x,y;
};

//定义派生类
class A:public Point{
public:
    //派生类对象可以直接访问基类成员
    //对于private成员只能通过接口函数去改变,不能直接访问
    void Move_Point(int xx, int yy) {
        get_x(xx);
        get_y(yy);
    }
};
int main(void)
{
    //定义派生对象num
    A num;
    num.Move_Point(3, 4);
    //派生类对象直接访问基类成员
    num.show_point();
    return 0;
}

代码运行结果:

x is 3
y is 4
请按任意键继续. . .

私有继承

私有继承

  • 继承访问控制
    • 基类的publicprotected成员:都已private身份出现在派生类中;
    • 基类的private成员:不能直接访问
  • 访问权限
    • 派生类中的成员函数:可以直接访问类中的public和protected成员,但不能直接访问基类的private成员;
    • 通过派生类的对象:不能直接访问从基类集成的任何成员

私有继承示例代码:

#include<iostream>
using namespace std;

//定义基类
class Point {
public:
    void get_x(int xx) {
        x = xx;
    }
    void get_y(int yy) {
        y = yy;
    }
    void show_point(){ 
        cout << "x is "<< x << endl;
        cout << "y is " << y << endl;
    }
private:
    int x,y;
};

class A:private Point{
public:
    //派生类对象可以直接访问基类public、protected成员
    //对于private成员只能通过接口函数去改变,不能直接访问
    void Move_Point(int xx, int yy) {
        get_x(xx);
        get_y(yy);
    }
    void Show_Point() { 
        cout << "this is class A" << endl;
        show_point();
    }
};

int main(void)
{
    //定义派生对象num
    A num;
    num.Move_Point(3, 4);
    //私有继承里派生类对象不能直接访问基类成员
    num.Show_Point();
    return 0;
}

代码运行结果:

this is class A
x is 3
y is 4
请按任意键继续. . .

保护继承

保护继承

  • 继承的访问控制
    • 基类的publicprotected成员:都已protected身份出现在派生类中;
    • 基类的private成员:不可直接访问
  • 访问权限
    • 派生类的成员函数:可直接访问基类中的publicprotected成员,但不能直接访问基类的private成员;
    • 通过派生类的对象:不能直接访问从基类继承的任何成员

用的别比较少,不再举例


类型转换

  • 公有派生类对象可以被当做基类的对象使用,反之则不可以。
    • 派生类的对象可以隐含转换为基类对象
    • 派生类的对象可以初始化基类的引用
    • 派生类的指针可以隐含转换为基类的指针
  • 通过基类对象名、指针只能使用从基类继承的成员(派生类新增成员与基类重名的只能使用基类的)

派生类的构造函数

默认情况下

  • 基类的构造函数不被继承
  • 派生类需要定义自己的构造函数
  • C++11规定
    • 可用using语句继承基类构造函数
    • 但是只能初始化从基类集成的成员(至于新增的成员只能在类内初始化,不能使用构造函数进行初始化,在派生类构造对象时只能采用默认方式,而基类成员可以按照原来的构造形式进行初始化)
    • 语法形式
      usingB::B

若不继承基类的构造函数

  • 派生类新增成员:派生类定义构造函数初始化
  • 继承来的成员:自动调用基类构造函数进行初始化
  • 派生类的构造函数需要给基类的构造函数传递参数

派生类与基类的构造函数

  • 当基类有默认构造函数时
    • 派生类构造函数可以不向基类构造函数传递参数
    • 构造派生类对象时,基类默认构造函数将被调用
  • 如需执行基类中带参数的构造函数
    • 派生类构造函数应为基类构造函数提供参数

构造函数的执行顺序

  • 调用基类构造函数顺序
    • 按照它们被继承时声明的顺序(从左向右)
  • 对初始化列表中的成员进行初始化
    • 顺序按照它们在类中定义的顺序
    • 对象成员初始化时自动调用其所属类的构造函数
      • 由初始化列表提供参数
  • 执行派生类的构造函数体中的内容

单继承时构造函数的定义语法:

派生类名::派生类名(基类所需要的形参,本类成员所需要的形参):基类名(参数表),本类成员初始化列表
{
    //其它初始化
}

单继承构造函数举例:

#include<iostream>
using namespace std;

//定义Point基类
class Point {

public:
    Point() {};
    Point(int xx, int yy) {
        x = xx;
        y = yy;
    }
    void get_x(int xx) {
        x = xx;
    }
    void get_y(int yy) {
        y = yy;
    }
    void show_point() {
        cout << "this is class Point " << endl;
        cout << "x is : " << x << endl;
        cout << "y is : " << y << endl;
    }
private:
    int x, y;
};

//定义派生类矩形类
class Rec :public Point {
public:
    Rec() {}
    Rec(int xx, int yy, int ww, int hh);
    void move_point(int xx,int yy) {
        get_x(xx);
        get_y(yy);
    }
    void show_rec() {
        cout << "this is class rec" << endl;
        cout << "start point is " << endl;
        show_point();
        cout << "width:" << w << endl;
        cout << "hight" << h << endl;
    }
private:
    int w, h;

};
Rec::Rec(int xx, int yy, int ww, int hh):Point(xx, yy), w(ww), h(hh) {
}

int main(void)
{
    Rec rec1(3, 4, 1, 2);
    rec1.show_rec();
    rec1.move_point(4, 4);
    rec1.show_rec();
    return 0;
}

代码运行效果:

this is class rec
start point is
this is class Point
x is : 3
y is : 4
width:1
hight2
this is class rec
start point is
this is class Point
x is : 4
y is : 4
width:1
hight2
请按任意键继续. . .

多继承时构造函数的定义语法:

派生类名::派生类名(参数表):
基类名1(基类1初始化参数表),
基类名2(基类2初始化参数表),
···
基类名n(基类n初始化参数表),
本类成员初始化列表
{
    //其它初始化
}

多继承构造函数示例:

#include<iostream>
using namespace std;
//基类1
class Base1 {
public:
    Base1() {}
    Base1(int xx, int yy):x(xx),y(yy) {}
    void show_base1() {
        cout << "x is : " << x << endl;
        cout << "y is : " << y << endl;
    }
private:
    int x, y;
};
//基类2
class Base2 {

public:
    Base2() {}
    Base2(int aa, int bb) :a(aa), b(bb) {}
    void show_base2() {
        cout << "a is : " << a << endl;
        cout << "b is : " << b << endl;
    }
private:
    int a, b;
};
//派生类
class Drived :public Base1, public Base2 {
public:
    Drived() {}
    Drived(int xx,int yy,int aa,int bb,int cc,int dd);
    void show_drived() {
        cout << "c is : " << c << endl;
        cout << "d is : " << d << endl;
    }
private:
    int c, d;
};
//派生类构造函数
Drived::Drived(int xx, int yy, int aa, int bb,int cc,int dd) :Base1(xx, yy),
             Base2(aa, bb),c(cc),d(dd) {

}

int main(void)
{
    Drived dri1(1, 2, 3, 4, 5, 6);
    dri1.show_base1();
    dri1.show_base2();
    dri1.show_drived();
    return 0;
}

程序运行结果:

x is : 1
y is : 2
a is : 3
b is : 4
c is : 5
d is : 6
请按任意键继续. . .

多继承且有对象成员时派生的构造函数定义语法:

派生类名::派生类名(形参表):基类名1(参数),···基类名(参数),本类成员(含对象成员)初始化列表
{
    //其它初始化
}

含有对象的构造函数例子:

#include<iostream>
using namespace std;
//基类1
class Base1 {

public:
    Base1() {}
    Base1(int xx, int yy):x(xx),y(yy) {}
    void show_base1() {
        cout << "x is : " << x << endl;
        cout << "y is : " << y << endl;
    }
private:
    int x, y;
};
//基类2
class Base2 {

public:
    Base2() {}
    Base2(int aa, int bb) :a(aa), b(bb) {}
    void show_base2() {
        cout << "a is : " << a << endl;
        cout << "b is : " << b << endl;
    }
private:
    int a, b;
};
//派生类
class Drived :public Base1, public Base2 {
public:
    Drived() {}
    Drived(int xx,int yy,int aa,int bb,int cc,int dd,int ee,int ff);
    void show_drived() {
        cout << "c is : " << c << endl;
        cout << "d is : " << d << endl;
        cout << "base1.x is : "  << endl;
        base1.show_base1();
    }
private:
    int c, d;
    Base1 base1;
};
//派生类初始化
Drived::Drived(int xx, int yy, int aa, int bb,int cc,int dd,int ee,int ff) :Base1(xx, yy),
             Base2(aa, bb),c(cc),d(dd),base1(ee,ff) {

}

int main(void)
{
    Drived dri1(1, 2, 3, 4, 5, 6,7,8);
    dri1.show_base1();
    dri1.show_base2();
    dri1.show_drived();
    return 0;
}

若派生类没有声明复制构造函数

  • 编译器会在需要时生成一个隐含的复制构造函数
  • 先调用基类的复制构造函数
  • 再为派生类新增的成员行复制

若派生类定义复制构造函数

  • 一般都要为基类的复制构造函数传递参数。
  • 复制构造函数只能接受一个参数,既用来初始化派生类定义的成员,也将被传递给基类的复制构造函数。
  • 基类的复制构造函数形参类型是基类对象的引用
  • 实参可以是派生类对象的引用

例如

C::C(const C &c1): B(c1){···}

派生类的复制构造函数写法举例:

#include<iostream>
using namespace std;
class Base1 {

public:
    Base1() {}
    Base1(int xx, int yy):x(xx),y(yy) {}
    void show_base1() {
        cout << "x is : " << x << endl;
        cout << "y is : " << y << endl;
    }
private:
    int x, y;
};

class Base2 {

public:
    Base2() {}
    Base2(int aa, int bb) :a(aa), b(bb) {}
    void show_base2() {
        cout << "a is : " << a << endl;
        cout << "b is : " << b << endl;
    }
private:
    int a, b;
};

class Drived :public Base1, public Base2 {
public:
    Drived() {}
    Drived(int xx,int yy,int aa,int bb,int cc,int dd,int ee,int ff);
    Drived(const Drived& s);
    void show_drived() {
        cout << "c is : " << c << endl;
        cout << "d is : " << d << endl;
        cout << "base1.x is : "  << endl;
        base1.show_base1();
    }
private:
    int c, d;
    Base1 base1;
};
Drived::Drived(int xx, int yy, int aa, int bb,int cc,int dd,int ee,int ff) :Base1(xx, yy),
             Base2(aa, bb),c(cc),d(dd),base1(ee,ff) {

}
Drived::Drived(const Drived& s) : Base1(s),Base2(s),base1(s.base1){
    c = s.c;
    d = s.d;
}

int main(void)
{
    Drived d(1, 2, 3, 4, 5, 6, 7, 8),e(d);
    Base1 a(d);
    Base2 c(d);
    a.show_base1();
    c.show_base2();
    e.show_drived();
    return 0;
}

代码运行效果:

x is : 1
y is : 2
a is : 3
b is : 4
c is : 5
d is : 6
base1.x is :
x is : 7
y is : 8
请按任意键继续. . .

派生类的析构函数

  • 析构函数不被继承,派生类如果需要要自行声明析构函数。
  • 声明方法与无继承关系时类的析构函数相同
  • 不需要显式地调用基类的析构函数,系统会自动隐式调用。
  • 先执行派生类析构函数的函数体,再调用基类的析构函数。

这部分不进行演示


访问从基类继承的成员

当派生类与基类中有相同成员

  • 若未特别限定,则通过派生类对象使用的是派生类中的同名成员。
  • 如要通过派生类对象访问基类中被隐藏的同名成员应使用基类名和作用域操作符(::)来限定。

重名示例代码:

#include<iostream>
using namespace std;
class Base1 {
public:
    void show() {
        cout << "this is Base1" << endl;
    }
};

class Base2 {

public:
    void show() {
        cout << "this is Base2" << endl;
    }
};

class Drived :public Base1, public Base2 {
public:
    void show() {
        cout << "this is Drived" << endl;
    }   
};
int main(void)
{
    Drived d;
    d.show();
    return 0;
}

代码运行效果:

this is Drived
请按任意键继续. . .

想指定某些类的相关函数怎样做?下面是示例代码:

int main(void)
{
    Drived d;
    d.show();
    d.Base1::show();
    d.Base2::show();
    return 0;
}

代码运行效果:

this is Drived
this is Base1
this is Base2
请按任意键继续. . .

二义性问题

  • 如果从不同基类继承了同名成员,但是在派生类中没有定义同名成员,“派生类对象名或引用名成员名”派生类指针->成员名”访可成员存在二义性问题
  • 解决方式:用类名限定。(解决方法同上面代码相似)

虚基类

  • 需要解决的问题
    • 当派生类从多个基类派生,而这些基类又有共同基类,则在访问此共同基类中的成员时,将产生元余,并有可能因元余带来不一致性。
  • 虚基类声明
    • virtual说明基类继承方式
      例: class B1: virtual public B
  • 作用
    • 主要用来解决多继承时可能发生的对同一基类继承多次而产生的二义性问题;
    • 为最远的派生类提供唯一的基类成员,而不重复产生多次复制
  • 注意
    • 在第一级继承时就要将共同基类设计为虚基类

虚基类及其派生类构造函数

  • 建立对象时所指定的类称为最远派生类。
  • 虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的
  • 在整个继承结构中,直接或间接继承虚基类的所有派生类,都必须在构造函数的成员初始化表中为虚基类的构造函数列出参数。如果未列出,则表示调用该虚基类的默认构造函数
  • 在建立对象时,只有最远派生类的构造函数调用虚基类的构
    造函数,其他类对虚基类构造函数的调用被忽略
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值