【C++核心编程】继承详解

 🔥博客主页: 我要成为C++领域大神

🎥系列专栏【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】

❤️感谢大家点赞👍收藏⭐评论✍️

本博客致力于分享知识,欢迎大家共同学习和交流。

继承的概念

继承:子类继承父类,子类包含父类的成员,也可以直接使用父类的成员

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称[生类。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。

如何进行继承:在定义类时,在类名后加上 : 继承方式 父类。如下图所示

在定义子类对象,计算大小时,父类也要包含在内。

继承方式

描述了父类成员在子类中访问属性 (表现的属性)和 访问修饰符 共同决定了继承中父类成员所能使用的范围(权限控制)

class CFather {
public:
int m_pub=1;
protected:
int m_pro=2;
private:
int m_pri=3;
};
class CSon :private CFather{
public:
void funPublic() {
    //cout << m_pri << endl;  //父类中private 在子类中无法*直接*访问,但是可以通过父类提供一个公共接口访问
    //需要查看,但是不允许修改的情况
}
};

继承下的构造-析构顺序

内存排布情况:父类在前,子类在后

构造

在子类的构造函数中,我们不能对父类的成员属性进行初始化,但是可以在子类初始化参数列表加上父类类名()来调用父类的构造函数。

当父类的构造函数无默认参数时,在调用子类构造函数时,编译器会自动调用父类构造函数

当父类构造函数有默认参数时,编译器不会自动调用,必须手动显式的调用父类构造,才能完成父类成员的初始化

#include<iostream>
using namespace std;
class CFather {
public:
int m_a;
CFather(int a) :m_a(a) {
    cout << "CFather" << endl;
}
~CFather() {
    cout << "~CFather" << endl;
}
};
class CSon : public CFather{
public:
int m_b;
//CSon():m_b(5)/*m_a(10)* error*/{  //在子类中,不能直接对父类的变量进行初始化
CSon():m_b(5),CFather(10){
    //当父类的构造函数有默认参数时,编译器不会自动调用,必须手动显式的调用父类构造,才能完成父类成员的初始化
    //可以在初始化参数列表上加上 类名(),来调用父类的构造函数
    cout << "CSon" << endl;
}
CSon() :m_b(5){  //当父类的构造函数无默认参数时,编译器会自动调用
    cout << "CSon" << endl;
}
};

析构

调用析构函数时,与构造顺序相反原因:在定义子类对象的前提下,子类对象的生命周期结束,一定会匹配子类的析构函数,当子类的析构函数执行完毕后,开始回收对象的内存空间,由于和初始化的顺序相反先调用CHand 的析构 (组合类的析构) ,在调用父类的析构 (继承类的析构)

.#include<iostream>
using namespace std;
class CFather {
public:
int m_a;
CFather(int a) :m_a(a) {
    cout << "CFather" << endl;
}
~CFather() {
    cout << "~CFather" << endl;
}
};
class CHand {
public:
CHand (int a){}
~CHand() {
    cout << "~CHand" << endl;
}
};
class CSon : public CFather{
public:
CHand hand;
int m_b;
CSon() :m_b(5), CFather(8),hand(10) { 
    cout << "CSon" << endl;
}
~CSon() {
    cout << "~CSon" << endl;
}
};


int main() {
    CSon son;
    //内存排布情况: CFather hand m_b(先父后子,接着按照初始化顺序)
    //析构顺序:CSon hand CFather
    cout << "-------------------------" << endl;
    return 0;
}

继承的优点

优点: 将一些功能比较相近的类中的 公共属性、方法 ,抽离出来形成一个类,即做为一个父类这些子类继承这个父类即可,非公共的方法仍然保持在子类中,由子类单独维护。提高代码的灵活性,复用性,扩展性

#include<iostream>
using namespace std;
class CPeople {
public:
int m_money;
CPeople(int m):m_money(m) {}
void cost(int m) {
    m_money -= m;
}
void walk() {
    cout << "没事,出去走两步" << endl;
}
};
class CYellow :public CPeople {
public:
void eat() {
    cout << "馒头" << endl;
}
CYellow(int m) :CPeople(m) {}
};
class CWhite :public CPeople {
public:
void eat() {
    cout << "面包" << endl;
}
CWhite(int m):CPeople(m){}
};
class CBlack :public CPeople {
public:
void eat() {
    cout << "棉花" << endl;
}
CBlack(int m) :CPeople(m) {}
};
int main() {
    CYellow ye(200);
    ye.cost(50);
    ye.eat();
    ye.walk();
    CWhite  wh(100);
    CBlack  bl(50);
    return 0;
}
//将一些功能相同的类作为子类,将这些功能抽离出来放到父类中,子类继承父类

隐藏

隐藏是指当父类和子类中出现同名成员时,父类成员被隐藏。可以显式的指定类名作用域来匹配父类函数

#include<iostream>
using namespace std;
class CFather {
public:
void fun(/*CFather * const this*/) { cout << "fun()" << endl; }
};
class CSon:public CFather {
public:
void fun(int/*,CSon *const this*/) { cout << "fun(int)" << endl; } 
};

int main() {
    CSon son;
    //son.fun(); 当父类和子类中出现同名成员时,父类成员被隐藏
    son.fun(10);
    //父类函数参数this指针与子类不同,但是父类的this指针可以指向子类 (不需要强转) 保证了子类对象可以正确的调用父类的函数
    CFather* const pthis = &son;
    //CSon* son2 = new CFather; error 子类的指针不可直接指向父类的对象 (在访问子类成员时会越界)
    son.CFather::fun();//可以显式的指定类名作用域来匹配父类函数
    return 0;
}

//CSon* son2 = new CFather; //error 子类的指针不可直接指向父类的对象 (在访问子类成员时会越界)

子类的指针申请了父类大小的空间,当访问子类内容时,会越界。父类对象的内存布局不包含子类对象的部分。子类指针如果指向父类对象,会尝试访问子类特有的成员,这些成员在父类对象中是不存在的,导致内存访问错误和未定义行为。

继承与组合的区别

继承是匿名的,组合是有名的组合使得在成员在内存中的分布情况可控,而继承永远是父类在前子类在后

父类指针指向子类

父类的指针可以指向所有继承该类的指针,因此可以将父类指针作为全局函数的参数,传入不同的子类对象来实现对应的功能

#include<iostream>
using namespace std;
class CPeople {
public:
int m_money;
CPeople(int m) :m_money(m) {}
void cost(int m) {
    m_money -= m;
}
void walk() {
    cout << "没事,出去走两步" << endl;
}
};
class CYellow :public CPeople {
public:
void eat() {
    cout << "馒头" << endl;
}
CYellow(int m) :CPeople(m) {}
};
class CWhite :public CPeople {
public:
void eat() {
    cout << "面包" << endl;
}
CWhite(int m) :CPeople(m) {}
};
class CBlack :public CPeople {
public:
void eat() {
    cout << "巧克力" << endl;
}
CBlack(int m) :CPeople(m) {}
};
//父类指针指向多个子类对象,统一各个子类,提高了代码的灵活性,复用性,扩展性
void fun(CPeople * peo) {
    peo->cost(2);
    //peo->eat(); 待解决
    peo->walk();
}
class Family {
public:
CPeople* peo[2];//优化后的,只需一个父类数组
Family() :peo{new CYellow(1),new CWhite(2)} {}
};
int main() {
    CWhite whi(50);
    CYellow yel(100);
    CBlack bla(150);
    fun(&whi);
    return 0;
}

类成员函数指针

一般函数指针:

void fun() {
    cout << "fun" << endl;
}
int main() {
    fun();//直接调用
    void(*pfun)() = &fun;//通过函数指针间接调用
    (*pfun)();

类成员函数与一般函数的区别:

1.作用域不同2.全局函数没有this指针

类成员函数指针定义:在定义类函数指针时,要在指针的*前面显式的指定作用域。通过类函数指针调用函数时,要指定对象。

class Test {
public:
void fun() {
    cout << "CTest::fun" << endl;
}
};
int main() {
    Test tst;
    tst.fun();//直接调用
    //类成员函数指针,::* 是C++中的整体运算符, 定义类成员函数指针, .* ,->* 也是整体运算符,使用对象调用类成员函数指针使用函数
    void (Test::*m_pfun)() = & Test::fun;
    (tst.*m_pfun)();//通过类成员函数指针间接调用函数
    ((&tst)->*m_pfun)();
    cout << sizeof(Test) << endl;
    return 0;
}


类成员函数指针的应用:

#include<iostream>
using namespace std;

class CFather{
public:

};
class CSon:public CFather {
public:
void fun() {
    cout << "fun" << endl;
}
};
class CPeople {
public:
int m_money;
CPeople(int m) :m_money(m) {}
void cost(int m) {
    m_money -= m;
}
void walk() {
    cout << "没事,出去走两步" << endl;
}
};
class CYellow :public CPeople {
public:
void eat() {
    cout << "馒头" << endl;
}
CYellow(int m) :CPeople(m) {}
};
class CWhite :public CPeople {
public:
void eat() {
    cout << "面包" << endl;
}
CWhite(int m) :CPeople(m) {}
};
class CBlack :public CPeople {
public:
void eat() {
    cout << "巧克力" << endl;
}
CBlack(int m) :CPeople(m) {}
};
//父类指针指向多个子类对象,统一各个子类,提高了代码的灵活性,复用性,扩展性
typedef void (CPeople::* M_FUNEAT)();
void fun(CPeople* peo,M_FUNEAT funeat) {
    peo->cost(2);
    //peo->eat(); 待解决
    (peo->*funeat)();
    peo->walk();
}
int main() {
    CFather* tst = new CSon;
    typedef void (CFather::* M_PFUN)();
    void (CFather:: * m_pfun)() = (void (CFather:: *)()) & CSon::fun;//进行强制类型转换
    (tst->*m_pfun)();

    M_PFUN m_pfun1=(M_PFUN)&CSon::fun;

    CPeople yel(100);
    fun(&yel, (M_FUNEAT) & CYellow::eat);
    return 0;
}

使用类成员函数指针,可以将各个功能相似的子类功能封装起来

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值