C++(学习)2024.9.25

目录

继承

概念

构造函数

1.派生类与基类构造函数的关系

2.解决方案

(1)补充基类的无参构造函数

(2)手动在派生类中调用基类构造函数

1.透传构造

2.委托构造

3.继承构造

3.对象的创建与销毁流程

4.多重继承

(1)概念

(2)可能出现的问题

1.重名问题

2.菱形继承

权限

1.权限修饰符

2.不同权限的继承

(1)公有继承

(2)保护继承

(3)私有继承


继承

概念

继承就是面向对象的三大特性之一,体现了代码复用的思想。

继承就是在一个已存在的类的基础上,建立一个新的类,并拥有其特性。

1.已存在的类被称为"基类"或者"父类"

2.新建立的类被称为"派生类"或者"子类"

cpp 复制代码
#include <iostream>
using namespace std;

// 基类
class Father
{
private:
    string name = "张";
public:
    void set_name(string name)
    {
        this->name = name;
    }
    string get_name()
    {
        return name;
    }
    void work()
    {
        cout << "我的工作是厨师,我负责炒菜" << endl;
    }
};

// 派生类
class Son:public Father
{
};

int main()
{
    Son son;
    cout << son.get_name() << endl;
    son.work();
    return 0;
}

上面的代码,Son类的功能几乎与Father类重叠,在实际的使用过程中,派生类会做出一些与基类的差异化。

●修改继承来的基类内容

属性:

1、公有属性可以直接更改。更改后基类中的属性也会改变,因为改的是同一份变量

2、私有属性,需要使用基类公有函数进行更改。

行为:函数隐藏,通过派生类实现一个同名同参数的函数,来隐藏基类的函数。

●新增派生类的内容

cpp 复制代码
#include <iostream>
using namespace std;

// 基类
class Father
{
private:
    string name = "张";
public:
    void set_name(string name)
    {
        this->name = name;
    }
    string get_name()
    {
        return name;
    }
    void work()
    {
        cout << "我的工作是厨师,我负责炒菜" << endl;
    }
};

// 派生类
class Son:public Father
{
public:
    void init()
    {
        set_name("王");
    }

    void work()
    {
        cout << "我的工作是司机" << endl;
    }

    void game()
    {
        cout << "开挖掘机" << endl;
    }
};


int main()
{
    Son son;
    son.init();

    cout << son.get_name() << endl;
    son.work(); 
    son.game();

    son.Father::work();
    return 0;
}

基类和派生类都是相对的,一个类可能存在又是基类又是派生类的情况,取决于那两个类进行比较。

构造函数

1.派生类与基类构造函数的关系

构造函数与析构函数不能被继承。

cpp 复制代码
#include <iostream>
using namespace std;

// 基类
class Father
{
private:
    string name;
public:
    Father(string name):name(name){}    // 有参构造函数
    string get_name()
    {
        return name;
    }
};

// 派生类
class Son:public Father
{
};

int main()
{
//    Son s;    // 找不到基类的无参构造函数
//    Son s("张"); // 找不派生类的有参构造函数
    return 0;
}

2.解决方案

(1)补充基类的无参构造函数
cpp 复制代码
#include <iostream>
using namespace std;

// 基类
class Father
{
private:
    string name;
public:
    // 无参构造函数
    Father():name("张"){}

    // 有参构造函数
    Father(string name):name(name){}

    string get_name()
    {
        return name;
    }
};

// 派生类
class Son:public Father
{
public:
};

int main()
{
    Son s;    
    cout << s.get_name() << endl;
    return 0;
}
(2)手动在派生类中调用基类构造函数
1.透传构造

在派生类的构造函数中,调用基类的构造函数,实际上编译器自动添加的派生类构造函数,调用基类无参构造函数时,就是采用的这种方式。

cpp 复制代码
#include <iostream>
using namespace std;

// 基类
class Father
{
private:
    string name;
public:
    // 有参构造函数
    Father(string name):name(name){}
    string get_name()
    {
        return name;
    }
};

// 派生类
class Son:public Father
{
public:
    // 编译器会自动添加构造函数,透传调用基类无参构造函数(透传构造)
    // Son():Father(){}
    // 手动添加构造函数,透传构造
    Son():Father("王"){}
    // 有参构造函数,调用基类有参构造函数
    Son(string fn):Father(fn){}
};

int main()
{
    Son s;
    cout << s.get_name() << endl;
    Son s1("张");
    cout << s1.get_name() << endl;
    return 0;
}
2.委托构造

一个类的构造函数可以调用这个类的另一个构造函数,但是需要避免循环委托。

**委托构造的性能低于透传构造,但是代码的"维护性更好",因为通常一个类中构造函数都会委托给能力最强(参数最多)的构造函数。**代码重构时,只需要更改这个能力最强的构造函数即可。

cpp 复制代码
#include <iostream>
using namespace std;

// 基类
class Father
{
private:
    string name;
public:
    Father(string name):name(name){}    // 有参构造函数
    string get_name()
    {
        return name;
    }
};

// 派生类
class Son:public Father
{
public:
    Son():Son("李"){}    // 委托构造
    Son(string fn):Father(fn){}    // 有参构造函数,调用基类有参构造函数
};

int main()
{
    Son s;
    cout << s.get_name() << endl;

    Son s1("赵");
    cout << s1.get_name() << endl;

    return 0;
}
3.继承构造

C++11新增的写法,只需要一句话,就可以自动给派生类添加n(n个为基类构造函数的个数)个构造函数。并且每个派生类的构造函数格式都与基类相同,每个派生类的构造函数都通过透传构造调用对应格式的基类构造函数。

cpp 复制代码
#include <iostream>
using namespace std;

// 基类
class Father
{
private:
    string name;
public:
    Father():Father("张"){}
    Father(string name):name(name){}    // 有参构造函数
    string get_name()
    {
        return name;
    }
};

// 派生类
class Son:public Father
{
public:
    
    using Father::Father;
    // 只需要补充这一句话,编译器就会自动添加下面两种构造函数    
    // Son():Father(){}
    // Son(string fn):Father(fn){}
};

int main()
{
    Son s;
    cout << s.get_name() << endl;
    Son s1("王");
    cout << s1.get_name() << endl;
    return 0;
}

3.对象的创建与销毁流程

在继承中,构造函数与析构函数是有一定调用顺序的。

cpp 复制代码
#include <iostream>
using namespace std;

class Value
{
private:
    string str;
public:
    Value(string str):str(str)
    {
        cout << str<< " 调用Value构造函数" << endl;
    }
    
    ~Value()
    {
        cout << str << " 调用Value析构函数" << endl;
    }
};

class Father
{
public:
    static Value s_value;
    Value val = Value("Father 成员变量");
    Father()
    {
        cout << "Father 构造函数被调用了" << endl;
    }

    ~Father()
    {
        cout << "Father 析构函数被调用了" << endl;
    }
};
Value Father::s_value = Value("静态FatherValue");

class Son:public Father
{
public:
    static Value s_value;
    Value val = Value("Son成员变量");
    Son()
    {
        cout << "Son 构造函数被调用了" << endl;
    }

    ~Son()
    {
        cout << "Son 析构函数被调用了" << endl;
    }
};
Value Son::s_value = Value("静态SonValue");

int main()
{
    cout << "主函数被调用了" << endl;

    // 局部代码块
    {
        Son s;
        cout << "对象执行中" << endl;
    }

    cout << "主函数结束了" << endl;
    return 0;
}

上面的执行结果中,可以得到以下的规律:

(1)静态的创建早于非静态。
(2)在创建的过程中,同类型的内存空间基类先开辟,派生类先销毁。创建对象时:先基后派。销毁对象时:先派后基

(3)以"对象执行中为轴"上下对称

4.多重继承

(1)概念

C++支持多重继承,即一个派生类可以有多个基类,派生类对于每个基类的关系仍然可以看作是一个单继承。

cpp 复制代码
#include <iostream>
using namespace std;

class Sofa
{
public:
    void sit()
    {
        cout << "可以坐着" << endl;
    }
};

class Bed
{
public:
    void lay()
    {
        cout << "可以躺着" << endl;
    }
};

// 派生类
class SofaBed:public Sofa,public Bed
{
};

int main()
{
    SofaBed sobe;
    sobe.lay();
    sobe.sit();
    return 0;
}
(2)可能出现的问题
1.重名问题

当多个基类具有重名成员时,编译器在编译的时候会出现二义性问题。
解决方法:使用类名::方式调用

cpp 复制代码
#include <iostream>
using namespace std;

class Sofa
{
public:
    void sit()
    {
        cout << "可以坐着" << endl;
        }
    void clean()
    {
        cout << "打扫沙发" << endl;
        }
};
class Bed
{
public:
    void lay()
    {
        cout << "可以躺着" << endl;
        }
    void clean()
    {
        cout << "打扫床" << endl;
        }
};

// 派生类
class SofaBed:public Sofa,public Bed
{
};

int main()
{
    SofaBed sobe;
    sobe.lay();
    sobe.sit();
    sobe.Sofa::clean();
    sobe.Bed::clean();
    return 0;
}
2.菱形继承

当一个派生类有多个基类,且这些基类又有一个共同的基类,就会出现二义性的问题,这种现象被称为(钻石)菱形继承。

有两种解决方式:
1、和重名问题类似,使用基类的类名::方式调用

2、使用虚继承

当出现虚继承时,Furniture类中就会生成一张虚基类表,这个表不占用任何对象的存储空间,属于Furniture类持有,在程序启动时加载进内存空间,表中记录了Furniture函数的调用地址偏移量。

Bed和Sofa对象中会出现一个隐藏的成员变量指针,指向Furniture类中的虚基类表,占用对象的四个字节。

虚继承时,SofaBed类对象会同时拥有两个虚基类表指针成员,在调用时查表解决二义性。

cpp 复制代码
#include <iostream>
using namespace std;

// 家具厂
class Furniture
{
public:
    void func()
    {
        cout << "家具厂里有家具" << endl;
    }
};

class Sofa:virtual public Furniture
{
};
class Bed:virtual public Furniture
{
};

// 派生类
class SofaBed:public Sofa,public Bed
{
};
int main()
{
    SofaBed sobe;
    sobe.func();
    return 0;
}

权限

1.权限修饰符

|-----------|----|------|--------|
| | 类内 | 派生类内 | 全局(类外) |
| private | √ | × | × |
| protected | √ | √ | × |
| public | √ | √ | √ |

cpp 复制代码
#include <iostream>
using namespace std;

class Test
{
protected:
    string str="保护权限";
public:
    Test()
    {
        cout<<str<<endl;
    }
};

class Base:public Test
{
public:
    Base()
    {
        cout<<str<<endl;
    }

};

int main()
{
    Base b;
    //cout<<b.str<<endl;// 错误 b是保护权限

    return 0;
}

2.不同权限的继承

(1)公有继承

公有继承是使用最多的一种继承方式,在公有继承中,派生类可以继承基类的成员,但是不可以访问基类的私有成员,基类成员的权限在派生类中权限保持不变。

cpp 复制代码
#include <iostream>
using namespace std;

class Base
{
private:
    string str1 = "私有成员";
protected:
    string str2 = "保护成员";
public:
    string str3 = "公有成员";
};

class Son:public Base
{
public:
    Son()
    {
        //       cout << str1 << endl;      // 错误 str1为私有成员
        cout << str2 << endl;
        cout << str3 << endl;
    }
};

int main()
{
    Son s1;
    return 0;
}

(2)保护继承

在保护继承中,派生类可以继承基类的成员,不可以访问基类的私有成员,基类的公有成员与保护成员,在派生类的权限都是保护权限。

cpp 复制代码
#include <iostream>
using namespace std;

class Base
{
private:
    string str1 = "私有成员";
protected:
    string str2 = "保护成员";
public:
    string str3 = "公有成员";
};
class Son:protected Base
{
public:
    Son()
    {
        //cout << str1 << endl;     // 错误 str1为私有成员
        cout << str2 << endl;
        cout << str3 << endl;
        }
};

int main()
{
    Son s1;
    //    cout << s1.str3 << endl; // 保护成员调用失败
    return 0;
}

(3)私有继承

在私有继承中,派生类可以继承基类的成员,但是不可以访问基类的私有成员,基类的公有成员与保护成员在派生类中权限都是私有权限。

cpp 复制代码
#include <iostream>
using namespace std;

class Test
{
private:
    string str1="私有成员";
protected:
    string str2="保护成员";
public:
    string str3="公有成员";
};

class Base:private Test
{
public:
    Base()
    {
        //        cout<<str1<<endl;
        cout<<str2<<endl;
        cout<<str3<<endl;
    }
};
class Demo:public Base
{
public:
    Demo()
    {
        cout << str2 << endl;
        cout << str3 << endl;
    }
};


int main()
{
    Base b;
//    cout<<b.Test::str3<<endl;
    return 0;
}
相关推荐
转世成为计算机大神32 分钟前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
机器视觉知识推荐、就业指导41 分钟前
C++设计模式:建造者模式(Builder) 房屋建造案例
c++
宅小海1 小时前
scala String
大数据·开发语言·scala
朝九晚五ฺ1 小时前
【Linux探索学习】第十四弹——进程优先级:深入理解操作系统中的进程优先级
linux·运维·学习
qq_327342731 小时前
Java实现离线身份证号码OCR识别
java·开发语言
锅包肉的九珍1 小时前
Scala的Array数组
开发语言·后端·scala
心仪悦悦1 小时前
Scala的Array(2)
开发语言·后端·scala
yqcoder1 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
Swift社区2 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
baivfhpwxf20232 小时前
C# 5000 转16进制 字节(激光器串口通讯生成指定格式命令)
开发语言·c#