继承和派生

三种继承方式

  • 共有继承
  • 私有继承
  • 保护继承

公有继承(public)

  • 继承的访问控制
    基类的public成员和protected成员:访问属性在派生类中保持怖不变;
    基类的private成员:不可直接访问

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

    私有继承(private)

  • 继承的访问控制
    基类的public和protected成员:都以private身份出现在派生类中;
    基类的private成员:不可直接访问。

  • 访问权限
    派生类的成员函数:可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员;
    通过派生类的对象:不能直接访问从基类继承的任何成员。

    保护继承

  • 继承的访问控制
    基类的public和protected成员:都以protected身份出现在派生类中
    基类的private成员:不可以直接访问

  • 访问权限
    派生类的成员函数:可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员;
    通过派生类的对象:不能直接访问从基类继承的任何成员。

    protected成员的特点与作用

  • 对建立其所在类对象的模块来说,它与private成员的性质相似。

  • 对于其派生类来说,它与public成员的性质相同。

  • 既实现了数据隐藏,又方便继承,实现代码重用。

      class A {
      protected:
          int x;
      };
      A a;
      a.x = 5; // 错误
    
      class A {
      protected:
          int x;
      };
      class B :public A {
      public:
          void function();
      };
      void B::function() {
          x = 5; // 正确
      }

example:

#include <iostream>
#include <cmath>
#include "Rectangle.h"
using namespace std;

int main() {
    Rectangle rect;    //定义Rectangle类的对象
    rect.initRectangle(2, 3, 20, 10);    //设置矩形的数据
    rect.move(3,2);    //移动矩形位置
    cout << "The data of rect(x,y,w,h): " << endl;
    cout << rect.getX() <<", "    //输出矩形的特征参数
        << rect.getY() << ", "
        << rect.getW() << ", "
        << rect.getH() << endl;
    return 0;
}
//Rectangle.h
#ifndef _RECTANGLE_H
#define _RECTANGLE_H
#include "Point.h"

class Rectangle: public Point {    //派生类定义部分
public:    //新增公有函数成员
    void initRectangle(float x, float y, float w, float h) {
        initPoint(x, y); //调用基类公有成员函数
        this->w = w;
        this->h = h;
    }
    float getH() const { return h; }
    float getW() const { return w; }
private:    //新增私有数据成员
    float w, h;
};

#endif //_RECTANGLE_H
//Point.h
#ifndef _POINT_H
#define _POINT_H

class Point {    //基类Point类的定义
public:    //公有函数成员
    void initPoint(float x = 0, float y = 0) { this->x = x; this->y = y;}
    void move(float offX, float offY) { x += offX; y += offY; }
    float getX() const { return x; }
    float getY() const { return y; }
private:    //私有数据成员
    float x, y;
};

#endif //_POINT_H
class A {
public:
    void setA(int);
    void showA() const;
private:
    int a;
};

class B {
public:
    void setB(int);
    void showB() const;
private:
    int b;
};

class C :public A, private B {
public:
    void setC(int, int, int);
    void showC() const;
private:
    int c;
};

void A::setA(int x) {
    a = x;
}
void B::setB(int x) {
    b = x;
}
void C::setC(int x, int y, int z) {
    // 派生类成员直接访问基类的公有成员
    setA(x);
    setB(y);
    c = z;
}

int main()
{
    C obj;
    obj.setA(5);
    obj.showA();
    // obj.setB(6); 错误
    // obj.showB(); 错误
    obj.setC(6, 7, 9);
    obj.showC();

    return 0;
}

类型转换

  • 公有派生类对象可以被当作基类的对象使用,反之则不可。
    派生类的对象可以隐含转换为基类对象;
    派生类的对象可以初始化基类的引用;
    派生类的指针可以隐含转换为基类的指针。
  • 通过基类对象名、指针只能使用从基类继承的成员。
  • 不要重新定义继承而来的非虚函数*
#include <iostream>
using namespace std;

class Base1 {
public:
    void display() const {
        cout << "Base1::display()" << endl;
    }
};

class Base2 :public Base1 {
public:
    void display() const {
        cout << "Base2::display()" << endl;
    }
};

class Derived:public Base2 {
public:
    void display() const {
        cout << "Derived::display()" << endl;
    }
};

void fun(Base1* ptr) {  // 参数为指向基类对象的指针
    ptr->display();        // “对象指针->成员名”
}

int main()
{
    Base1 base1; // 声明Base1类对象
    Base2 base2; // 声明Base2类对象
    Derived derived; // 声明Derived对象

    fun(&base1); // 用Base1对象的指针调用fun函数
    fun(&base2); // 用Base2对象的指针调用fun函数
    fun(&derived); // 用Derived对象的指针调用fun函数

    return 0;
}
/*
Base1::display()
Base1::display()
Base1::display()
*/

默认情况下

  • 基类的构造函数不被继承;
  • 派生类需要定义自己的构造函数。

若不继承基类的构造函数

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

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

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

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

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

派生类与基类的构造函数

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

构造函数执行顺序

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

class B {
public:
    B();
    B(int i);
    ~B();
    void print() const;
private:
    int b;
};

B::B() {
    b = 0;
    cout << "B&#39;s default constructor called." << endl;
}

B::B(int i) {
    b = i;
    cout << "B&#39;s constructor called." << endl;
}

B::~B() {
    cout << "B&#39;s destructor called." << endl;
}

void B::print()const {
    cout << b << endl;
}

class C :public B {
public:
    C();
    C(int i, int j);
    ~C();
    void print() const;
private:
    int c;
};

C::C() {
    c = 0;
    cout << "C&#39;s default constructor called." << endl;
}
C::C(int i, int j) :B(i), c(j) {
    cout << "C&#39;s constructor called." << endl;
}

C::~C() {
    cout << "C&#39;s destrutor called." << endl;
}

void C::print()const {
    B::print();
    cout << c << endl;
}

int main()
{
    C obj(5, 6);
    obj.print();

    return 0;
}
/*
B&#39;s constructor called.
C&#39;s constructor called.
5
6
C&#39;s destrutor called.
B&#39;s destructor called.
*/
#include <iostream>
using namespace std;

class Base1 {
public:
    Base1(int i) { cout << "Constructing Base1 " << i << endl; }
};

class Base2 {
public:
    Base2(int j) { cout << "Construting Base2 " << j << endl; }
};

class Base3 {
public:
    Base3() { cout << "Construing Base3 *" << endl; }
};

class Derived :public Base2, public Base1, public Base3 {
public:
    Derived(int a, int b, int c,int d):Base1(a), member2(d), member1(c), Base2(b){}
private:
    Base1 member1;
    Base3 member3;
    Base2 member2;
};

int main()
{
    Derived obj(1, 2, 3, 4);

    return 0;
}
/*
Construting Base2 2
Constructing Base1 1
Construing Base3 *
Constructing Base1 3
Construing Base3 *
Construting Base2 4
*/

派生类的复制构造函数

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

  • 编译器会在需要时生成一个隐含的复制构造函数;
  • 先调用基类的复制构造函数;
  • 再为派生类新增的成员执行复制。
    若派生类定义复制构造函数
  • 一般都为基类的复制构造函数。
  • 复制构造函数只能接受一个参数,既用来初始化派生类定义的成员,也将被传递给基类的复制构造函数。
  • 基类的复制构造函数形参类型是基类对象的引用,实参可以是派生类对象的引用。
  • 例如:
    C::C(const C &c1):B(c1){...}

派生类的析构函数

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

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

  • 若未特别限定,则通过派生类对象使用的是派生类中的新定义的同名成员。
  • 如果通过派生类对象访问基类中被隐藏的同名成员,应使用基类名和作用域操作符(::)来限定
#include <iostream>
using namespace std;

class Base1 {    //定义基类Base1
public:
    int var;
    void fun() { cout << "Member of Base1" << endl; }
};

class Base2 {    //定义基类Base2
public:
    int var;
    void fun() { cout << "Member of Base2" << endl; }
};

class Derived : public Base1, public Base2 {    //定义派生类Derived
public:
    int var;    //同名数据成员
    void fun() { cout << "Member of Derived" << endl; }    //同名函数成员
};

int main() {
    Derived d;
    Derived* p = &d;

    d.var = 1;    //对象名.成员名标识
    d.fun();    //访问D1类成员

    d.Base1::var = 2;    //作用域分辨符标识
    d.Base1::fun();    //访问Base1基类成员

    p->Base2::var = 3;    //作用域分辨符标识
    p->Base2::fun();    //访问Base2基类成员

    return 0;
}
/*
Member of Derived
Member of Base1
Member of Base2
*/

二义性问题

  • 如果从不同基类继承了同名成员,但是在派生类中没有定义同名成员,“派生类对象或引用名.成员名”、“派生类指针->成员名”访问成员存在二义性问题
  • 解决方式:用类名限定。
  • 对象存储冗余了
    1.空间浪费
    2.不一致性
class A {
public:
    void f();
};
class B {
public:
    void f();
    void g();
};
class C :public A, public B {
public:
    void g();
    void h();
};

int main()
{
    C c1;
    c1.f(); // 有二义性
    c1.g(); // 无二义性(同名隐藏)

/*
解决方法一:用类名来限定
    c1.A::f() 或 c1.B::f()
解决方法二:同名隐藏
    在C中声明一个同名成员函数f(), f()再根据需要调用A::f() 或 B::f()
*/
}
#include <iostream>
using namespace std;

class Base0 {    //定义基类Base0
public:
    int var0;
    void fun0() { cout << "Member of Base0" << endl; }
};

class Base1 : public Base0 {    //定义派生类Base1
public:    //新增外部接口
    int var1;
};

class Base2 : public Base0 {    //定义派生类Base2
public:    //新增外部接口
    int var2;
};

class Derived : public Base1, public Base2 {    //定义派生类Derived
public:    //新增外部接口
    int var;
    void fun() { cout << "Member of Derived" << endl; }
};

int main() {    //程序主函数
    Derived d;            //定义Derived类对象d
    d.Base1::var0 = 2;    //使用直接基类
    d.Base1::fun0();
    d.Base2::var0 = 3;    //使用直接基类
    d.Base2::fun0();
    return 0;
}
/*
d.var0   d.Base0::var0  有二义性
*/
全部评论

相关推荐

点赞 评论 收藏
转发
2 收藏 评论
分享
牛客网
牛客企业服务