C++学习记录———关于继承

目录

继承的介绍

一、单一继承

1. 继承中的构造函数

1.1 函数基础特性

1.2 派生类构造函数调用基类构造函数

1.2.1 透传构造(掌握)

1.2.2 委托构造(熟悉)

1.2.3 继承构造

1.3 对象的创建与销毁的过程

二、多重继承

1. 多重继承概念

2. 多重继承的二义性

2.1 作用域限定符

2.2 菱形继承


继承的介绍

一、单一继承

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

  • 新建立的类被称为“派生类”或“子类”。
  • 已经存在的类被称为“基类”或“父类”。

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

如果一个派生类只是继承一个基类,两个类的内容基本相同,这样的继承是没有意义的。通过派生类可以对继承来的成员做出必要修改和增加。(如果基类的属性是private,派生类无法直接修改或访问,但是确实继承了)

  • 对于继承的属性,通常可以修改值
#include <iostream>
using namespace std;
class Father{
public:
    string first_name="王";
void work(){
    cout<<"我是一名理发师"<<endl;
}
void show(){
    cout<<"姓氏:"<<first_name<<endl;
}
};
class Son:public Father{

};
int main()
{
    Son s;
    s.work();
    s.show();
}
  • 对于继承的函数,可以通过函数隐藏“屏蔽”基类的同名函数

        如果子类中给出了与父类同名的函数,会把父类的同名函数隐藏

#include <iostream>
using namespace std;
class Father{
public:
    string first_name="王";
void work(){
    cout<<"我是一名理发师"<<endl;
}
void show(){
    cout<<"姓氏:"<<first_name<<endl;
}
};
class Son:public Father{
public:
    int age=20;
    //子类中给出了与父类同名的函数,会把父类的同名函数隐藏
    void work(){
        cout<<"我是一名老师"<<endl;
    }
    void play_game(){
        cout<<"玩游戏"<<endl;
    }

};
int main()
{
    Son s;
    s.work(); // //我是一名老师
    s.show();
    s.play_game();
    cout<<s.first_name<<" "<<s.age<<endl;


}

需要注意的是:

  • 基类和派生类是相对的,一个基类可以派生出多个派生类,每一个派生类又可以派生出新的派生类。
  • 间接继承的类之间也具有继承关系,也可以称之为间接基类和间接派生类。直接继承的类之间称为直接基类和直接派生类。
  • 派生类是基类的具体化,而基类是派生类的抽象化。
  • 构造函数和析构函数不能被继承

1. 继承中的构造函数

1.1 函数基础特性

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

#include <iostream>

using namespace std;
/**
 * @brief 基类
 */
class Father
{
public:
    string first_name;
    Father(string first_name)
    {
        this->first_name = first_name;
    }
    string get_first_name()
    {
        return first_name;
    }
};
/**
 * @brief 派生类
 */
class Son:public Father
{

};

int main()
{
//    Son s("张"); 构造函数不能被继承
//    Son s1; 找不到构造函数

    return 0;
}

在继承中,任何一个派生类的任意一个构造函数都必须直接或间接调用基类的任意一个构造函数。因为在创建派生类对象时,需要调用基类的代码,使用基类的逻辑去开辟部分继承的空间。

在上一节的代码中,没写构造函数,编译器会为Father类和Son类增加无参构造函数,与此同时,还在Son的构造函数中尝试调用Father类的无参构造函数。

实际上,只要一个派生类没有手动调用基类的构造函数,编译器都会尝试调用基类的无参构造。

上面的代码有两种解决方案:

  • 给Father构造函数增加无参调用的接口:

        1. Father类增加一个重载的无参构造函数

Father()
   {
      first_name = "张"; 
   }

        2. 给Father已有的构造函数增加一个默认值,使其支持无参调用。

Father(string first_name="张")
    {
        this->first_name = first_name;
    }
  • 给Son类增加调用Father类构造函数的代码(派生类构造函数调用基类构造函数)

注意:

父类的构造函数不会继承下来

子类一定要直接或者间接的调用父类的构造函数。来完成从父类继承过来数据的初始化。如果子类不写明如何调用父类的构造函数,这时会调用父类无参的构造函数,如果父类中没有无参的构造函数,这时就会报错

1.2 派生类构造函数调用基类构造函数

派生类的构造函数中调用基类的构造函数一共有三种方式:

  • 透传构造(直接调用)
  • 委托构造()
  • 继承构造

默认情况下,如果程序员在一个派生类中不写构造函数,编译器会自动给这个类添加一个无参构造函数且尝试调用街垒的无参构造函数。但是基类没有无参构造时,程序员必须手写派生类的构造函数并调用基类的构造函数。

1.2.1 透传构造(掌握)

透传构造指的是在派生类的构造函数中直接调用基类的构造函数

#include <iostream>
using namespace std;
class Father{
private:
string first_name;
    int age;
public:
Father(string first_name,int age){
    this->first_name=first_name;
    this->age=age;
}
void getFirstName(){
    cout<<"姓氏:"<<first_name<<endl;
}
void show(){
    cout<<"姓氏:"<<first_name<<" 年龄"<<age<<endl;
}
};
class Son:public Father{
private:
    int height; //子类新增的
public:
Son(string name,int age):Father(name,age){}
Son(string name,int age,int height):Father(name,age){ 
    this->height=height; //子类新增的属性,自己完成初始化
}
void son_show(){
    Father::show();
    cout<<"身高是:"<<height<<endl;
}

};
int main()
{
    Son s("小明",20);
    s.show();
    
    Son s2("小红",30,160);
    s2.son_show();
    
}

1.2.2 委托构造(熟悉)

间接的调用父类的构造函数。子类的构造函数调用子类另一个构造函数,另一个构造函数调用了父类的构造函数,相当于间接调用父类的构造函数

#include <iostream>
using namespace std;
class Father{
private:
    string first_name;
    int age;
public:
Father(string first_name,int age){
    this->first_name=first_name;
    this->age=age;
}
void getFirstName(){
    cout<<"姓氏:"<<first_name<<endl;
}
void show(){
    cout<<"姓氏:"<<first_name<<" 年龄"<<age<<endl;
}
};
class Son:public Father{
private:
    int height; //子类新增的
public:
    Son(string name):Son(name,18){}
    Son(string name,int age):Father(name,age){}
    Son(string name,int age,int height):Father(name,age){
        this->height=height; //子类新增的属性,自己完成初始化
    }
    void son_show(){
        Father::show();
        cout<<"身高是:"<<height<<endl;
    }

};
int main()
{
Son s("小明",20);
s.show();

Son s3("张三");
s3.show();

}

1.2.3 继承构造

C++11新增的写法,实际上就是让编译器自动生成结构与基类的构造函数相同的派生类构造函数,并逐一透传构造。

#include <iostream>

using namespace std;
/**
 * @brief 基类
 */
class Father
{
public:
    string first_name;
    Father(string first_name)
    {
        this->first_name = first_name;
    }
    string get_first_name()
    {
        return first_name;
    }
    Father():Father("张"){}
};
/**
 * @brief 派生类
 */
class Son:public Father
{
public:
    using Father::Father;
};

int main()
{
    Son s1;
    cout << s1.get_first_name() << endl; //王
    Son s2("张");
    cout << s2.get_first_name() << endl; //张

    return 0;
}

1.3 对象的创建与销毁的过程

类中有成员对象时构造和析构顺序
类中的数据成员也可以是个对象,称为成员对象
当类中有成员对象时,实例化对象先调用成员对象的构造函数,再调用对象自己的构造函数

#include <iostream>

using namespace std;
//作为其他类的变量
class Value
{
private:
    string name;
public:
    Value(string name):name(name)
    {
        cout << name << "成员变量构建了" << endl;
    }
    ~Value()
    {
        cout << name << "成员变量销毁了" << endl;
    }

};
class Father
{
public:
    Value value = Value("Father的");
    static Value s_value;
    Father():value(value)
    {
        cout << "Father类的构造函数" << endl;
    }
    ~Father()
    {
        cout << "Father类的析构函数" << endl;
    }
};
Value Father::s_value = Value("Father的静态");

class Son:public Father
{
public:
    Value value = Value("Son的");
    static Value s_value;
    Son():Father():value(value)
    {
        cout << "Son类的构造函数" << endl;
    }
    ~Son()
    {
        cout << "Son类的析构函数" << endl;
    }
};
Value Son::s_value = Value("Son的静态");

int main()
{
    cout <<"主函数开始执行" << endl;
    //局部代码块
    {
        Son s;  //栈内存对象
        cout <<"-----对象使用中----" << endl;
    }
    cout <<"主函数结束执行" << endl;
    return 0;
}

以上运行结果符合以下规律:

  1. 构建和销毁呈现对称关系,基类的内容先构建,派生类的内容后构建;派生类的内容先销毁,基类的内容后销毁。
  2. 静态成员从程序开始就创建,程序执行结束再销毁,不与任何对象绑定。

二、多重继承

1. 多重继承概念

C++语言支持多重继承,即一个派生类有多个直接继承的基类,是单一继承的扩展,每一个基类和派生类之间的关系都可以看做是一个单继承。

#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
{
public:

};

int main()
{
    SofaBed sb;
    sb.lay();
    sb.sit();
    return 0;
}

2. 多重继承的二义性

2.1 作用域限定符

 多重继承的二义性有两类:

  • 当两个直接基类出现重名成员函数是时

        解决方法:使用作用域限定符进行区分

#include <iostream>

using namespace std;

class Sofa
{
public:
    void sit()
    {
        cout << "沙发可以坐着" << endl;
    }
    void position()
    {
        cout << "沙发放在客厅" << endl;
    }
};
class Bed
{
public:
    void lay()
    {
        cout << "床可以躺着" << endl;
    }
    void position()
    {
        cout << "床放在卧室" << endl;
    }
};
class SofaBed:public Sofa,public Bed
{
public:

};

int main()
{
    SofaBed sb;
    sb.lay();
    sb.sit();
    sb.Bed::position();
    sb.Sofa::position();
    return 0;
}

2.2 菱形继承

菱形继承通过virtual虚继承的方式,从公共基类中只得到一份成员,避免了二义性

解决方法:

  1. 作用域限定符
  2. 使用虚继承

#include <iostream>

using namespace std;

class Furniture
{
public:
    void function()
    {
        cout << "家具是家里的东西" << endl;
    }
};

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

class SofaBed:public Sofa,public Bed
{
public:

};

int main()
{
    SofaBed sb;
    //第二种:使用虚继承
    sb.function();
    //第一种:作用域限定符
//    sb.Bed::function();
//    sb.Sofa::function();

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会尽力回答你的问题。首先,我们来设计一个 Student 学生类,包括以下属性: - 学号(id) - 姓名(name) - 年龄(age) - 性别(gender) - 成绩(score) 首先,我们可以定义一个 Student 类,如下所示: ```cpp class Student { public: int id; string name; int age; string gender; double score; }; ``` 接下来,我们可以通过继承来创建一个更具体的类,比如说 Undergraduate 本科生类。我们可以从 Student 类中继承属性,并添加一些新的属性和方法,如下所示: ```cpp class Undergraduate : public Student { public: string major; int year; void display() { cout << "ID: " << id << endl; cout << "Name: " << name << endl; cout << "Age: " << age << endl; cout << "Gender: " << gender << endl; cout << "Score: " << score << endl; cout << "Major: " << major << endl; cout << "Year: " << year << endl; } }; ``` 在这个例子中,我们使用了 public 继承,使得 Undergraduate 类可以访问 Student 类中的公有成员。我们还定义了一个新的方法 display,用于打印出 Undergraduate 类中的所有属性。 最后,我们可以通过重写父类的方法来定制化 Undergraduate 类的行为。比如说,我们可以重写 Student 类中的 display 方法,如下所示: ```cpp class Undergraduate : public Student { public: string major; int year; void display() { cout << "ID: " << id << endl; cout << "Name: " << name << endl; cout << "Age: " << age << endl; cout << "Gender: " << gender << endl; cout << "Score: " << score << endl; cout << "Major: " << major << endl; cout << "Year: " << year << endl; cout << "This is an undergraduate student." << endl; } }; ``` 在这个例子中,我们重写了 display 方法,添加了一行文本,用于提示这是一个本科生对象。 希望这个例子能够帮助你理解 C++ 中的类设计、继承和重写。如果你有任何其他问题,可以随时问我。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值