C++类继承笔记

类继承

《C++ Primer Plus》笔记

类继承语法

在类名后面使用冒号表示继承关系

// 父类(基类)
class Base {
    ...
}// 子类(派生类)
class Derived : Base {
    ...
};

派生类的构造方法必须使用基类的构造方法

若未调用,则会被加上默认构造方法(若没有默认无参构造方法,则报错),下面两个等价

Derived::Derived() {
    ...
}

Derived::Derived() : Base() {
    ...
};

public, protected和private

派生类不能访问基类的private成员和方法,但是能访问public和private成员和方法

外部不能访问private和protected成员和方法,只能访问public成员和方法

protected对下开放,对外关闭

// 父类(基类)
class Base {
public:
    string b_str1;
    void b_fun1();

protected:
    string b_str2;
    void b_fun2();

private:
    string b_str3;
    void b_fun3();
};

// 子类(派生类)
class Derived : Base {
public:
    void d_fun();
};

Derived::d_fun() {
    cout << b_str1 << endl; // 能够访问
    cout << b_str2 << endl; // 能够访问
    cout << b_str3 << endl; // 报错, 不能访问
    b_fun1(); // 能够访问
    b_fun2(); // 能够访问
    b_fun3(); // 报错, 不能访问
}

fun() {
    Base b;
    cout << b.b_str1 << endl; // 能够访问
    cout << b.b_str2 << endl; // 报错, 不能访问
    cout << b.b_str3 << endl; // 报错, 不能访问
    b.b_fun1(); // 能够访问
    b.b_fun2(); // 报错, 不能访问
    b.b_fun3(); // 报错, 不能访问
}

多态

基类类型的指针和引用可以指向派生类的对象

Base* b = new Derived;

Derived derived;
Base& b = derived;

派生类能够定义基类中相同名称和参数的方法

派生类定义了相同方法时,基类的所有同名方法都将被隐藏,包括不同参数和返回值的同名方法

// 父类(基类)
class Base {
public:
    void fun() {
        cout << "Base fun" << endl;
    }
};

// 子类(派生类)
class Derived : Base {
    void fun() { // 定义基类中相同的方法
        cout << "Derived fun" << endl;
    }
};

使用基类指针或引用时,调用的时基类的对应方法

使用派生类指针或引用时,调用派生类的方法

Derived derived;
derived.fun(); // 输出Derived fun

Base& b = derived;
b.fun(); // 输出Base fun

Base* b2 = &derived;
b2->fun(); // 输出Base fun

虚函数

在基类方法前面加上virtual关键字,将一直调用派生类方法

// 父类(基类)
class Base {
public:
    virtual void fun() {
        cout << "Base fun" << endl;
    }
};

// 子类(派生类)
class Derived : Base {
    void fun() { // 定义基类中相同的方法
        cout << "Derived fun" << endl;
    }
};

Derived derived;
derived.fun(); // 输出Derived fun

Base& b = derived;
b.fun(); // 输出Derived fun

Base* b2 = &derived;
b2->fun(); // 输出Derived fun

抽象类

虚函数可以不被实现, 被称作纯虚函数

纯虚函数对应的类被称作抽象类,不能生成实例化对象

class Base {
public:
    virtual void fun() = 0; 
};

Base base; // 报错

静态联编和动态联编

虚函数将使用虚函数指针,指向虚函数表对应的地址,所以只有在运行时才能选择正确的方法, 被称为动态联编

普通方法在编译时期就能确定对应的函数是哪个,这杯成为静态联编

使用虚函数,存在以下问题:

  1. 对于每个类,都需要创建虚函数指针和虚函数表,每个对象的存储空间都更大
  2. 对于每个函数调用,都存在多余的寻址操作

虚函数与析构函数

析构函数最好为虚函数,当使用基类指针或引用指向派生类对象时,将不会调用派生类析构函数,无法释放派生类new操作的内存

// 父类(基类)
class Base {
    ...
};

// 子类(派生类)
class Derived : Base {
private:
    char* str;
public:
    Derived() {
        str = new char[10];
        ...
    }
    ~Derived() {
        delete[] str;
        ...
    }
};


Base* b2 = new Derived;
delete b2; // 不会调用派生类析构函数,str内存无法释放

继承类型

继承同样存在public,protected,private三种类型,继承是使用相对应的关键字

// 父类(基类)
class Base {
public:
    void fun() {
        cout << "Base fun" << endl;
    }
};

// 子类1(派生类)
class Derived1 : public Base {
    void fun() { // 定义基类中相同的方法
        cout << "Derived fun" << endl;
    }
};

// 子类2(派生类)
class Derived1 : protected Base {
    void fun() { // 定义基类中相同的方法
        cout << "Derived fun" << endl;
    }
};

// 子类3(派生类)
class Derived1 : private Base {
    void fun() { // 定义基类中相同的方法
        cout << "Derived fun" << endl;
    }
};
特征公有继承保护继承私有继承
公有成员变成公有成员保护成员私有成员
保护成员变成保护成员保护成员私有成员
私有成员变成无法访问无法访问无法访问
能否隐式向上转换是,只能在派生类中

虚基类

多重继承

派生类可以同时继承多个基类

// 父类(基类)
class Base1 {
    ...
};

class Base2 {
    ...
};

// 子类(派生类)
class Derived : Base1, Base2 {
    ...
};

菱形继承问题

当一个类继承多个基类,而这些基类又同时是某一个类的派生类时,该类内部有多个基类对象,若调用基类的相同名称的方法,将会产生二义性

可以使用作用域解析来避免二义性

// 父类(基类)
class Base {
public:
    fun();
};
class Base1 : Base {
    ...
};

class Base2 : Base {
    ...
};

// 子类(派生类)
class Derived : Base1, Base2 {
    ...
};

Derived derived;
// derived.fun(); 产生二义性,不知道调用来自Base1的方法还是Base2的方法
derived.Base1::fun();
derived.Base2::fun();

虚基类使用virtual关键字,使得派生类只产生一个基类对象副本

// 父类(基类)
class Base {
public:
    fun();
};

class Base1 : virtural public Base {
    ...
};

class Base2 : public virtural Base {
    .;..
}

// 子类(派生类)
class Derived : Base1, Base2 {
    ...
};

Derived derived;
derived.fun(); // 不再产生二义性

普通继承中,构造方法不能调用基类的基类的构造方法, 而在虚基类继承中,必须调用基类的基类的构造方法,若没有,则调用默认构造方法

class Base {
public:
    int m_a;
    Base(int a) : m_a(a) {};
};

class Base1 : public Base {
public:
    int m_b;
    Base1(int a, int b) : Base(a), m_b(b) {};
};

class Base2 : public Base {
public:
    int m_c;
    Base2(int a, int c) : Base(a), m_c(c) {};
};

// 子类(派生类)
class Derived : Base1, Base2 {
    Derived(int a, int b, int c) : Base1(a, b), Base2(a, c) {};
    // Derived(int a, int b, int c) : Base(a), Base1(a, b), Base2(a, c) {}; 非法
};
class Base {
public:
    int m_a;
    Base(int a) : m_a(a) {};
};

class Base1 : virtual public Base {
public:
    int m_b;
    Base1(int a, int b) : Base(a), m_b(b) {};
};

class Base2 : virtual public Base {
public:
    int m_c;
    Base2(int a, int c) : Base(a), m_c(c) {};
};

// 子类(派生类)
class Derived : Base1, Base2 {
    // 必须调用Base构造方法
    Derived(int a, int b, int c) : Base(a), Base1(a, b), Base2(a, c) {}; 
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值