【C++初识继承】

ops/2024/10/21 6:06:26/

图片名称

博主首页: 有趣的中国人
 

专栏首页: C++进阶
 


本篇文章主要讲解 继承 的相关内容


目录

1. 继承的概念和定义

1.1 继承的概念

 1.2 继承的定义

1.2.1 继承定义格式

1.2.2 继承方式与访问修饰限定符

2. 基类和派生类对象赋值转换

3. 继承中的作用域

3.1 全局域、局部域、类域、命名空间域

3.2 继承作用域

4. 派生类的默认成员函数

4.1 构造函数和拷贝构造

4.2 赋值重载和析构函数


1. 继承的概念和定义

1.1 继承的概念

在C++中,继承是一种机制,它允许一个类(称为子类或派生类)另一个类(称为父类或基类)获取成员变量和成员函数。子类可以继承父类的属性和行为,并且可以添加自己的属性和行为。这种继承关系可以用来建立类之间的层次结构,从而实现代码的重用和抽象。在C++中,继承有三种类型:公有继承、保护继承和私有继承,它们决定了父类的成员对子类的可见性。

using namespace std;
class Person
{
public:void Print(){cout << "name:" << name << endl;cout << "age:" << age << endl;}
protected:string name = "Jesicca";int age = 18;
};// 继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了
Student和Teacher复用了Person的成员。下面我们使用监视窗口查看Student和Teacher对象,可
以看到变量的复用。调用Print可以看到成员函数的复用。class Student : public Person
{
protected:int stu_id;
};class Teacher : public Person
{
protected:int job_id;
};int main()
{Student st;Teacher t;st.Print();t.Print();return 0;
}

 1.2 继承的定义

1.2.1 继承定义格式

1.2.2 继承方式与访问修饰限定符

 总的来说:

1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私
有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面
都不能去访问它

class Person
{
public:void Print(){cout << "name:" << name << endl;cout << "age:" << age << endl;}
protected:string name = "Jesicca";
// age 为private类型,继承之后无论在派生类内还是类外都不能被访问
private:int age = 18;
};class Student : public Person
{
public:// 这里会报错,age不能被访问void Stuage(){cout <<  "Student age:" << age << endl;}
protected:int stu_id = 20;
};

虽然不能被访问,但是实际上还是被继承了:

2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在
派生类中能访问
,就定义为protected。可以看出保护成员限定符是因继承才出现的。

class Person
{
public:void Print(){cout << "name:" << name << endl;cout << "age:" << age << endl;}
protected:string name = "Jesicca";// age 变成了protected类型了int age = 18;
};class Student : public Person
{
public:// 再类中能被访问void Stuage(){cout <<  "Student age:" << age << endl;}
protected:int stu_id = 20;
};

3. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过
最好显示的写出继承方式。

4. 在实际运用中一般使用都是public继承几乎很少使用protetced/private继承,也不提倡
使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里
面使用。
 


2. 基类和派生类对象赋值转换

派生类对象可以赋值给基类的对象、基类的指针、基类的引用。这个操作叫做切片,

对于内置类型是直接拷贝,自定义类型调用拷贝构造,且切片过程中会进行赋值兼容不会产生临时变量

必须是基类的指针或者引用指向派生类才是安全的!

例如以下示例:

class Person
{
public:void Print(){cout << "name:" << name << endl;cout << "age:" << age << endl;}string name = "Jesicca";
protected:int age = 18;
};class Student : public Person
{
public:void Stuage(){cout <<  "Student age:" << age << endl;}
protected:int stu_id = 20;
};
int main()
{Student st;// 派生类对象给基类对象Person p = st;// 派生类给基类指针Person* pst = &st;// 派生类给父类引用Person& ref = st;ref.name += "j";ref.Print();pst->name += "k";pst->Print();return 0;
}

3. 继承中的作用域

3.1 全局域、局部域、类域、命名空间域

先回顾以下C++中其他的域:

全局域、局部域、类域、命名空间域,

其中全局域和局部域会影响生命周期,但是类域和命名空间域不会影响生命周期,再同一个域中不能出现同名的成员变量,也不能出现非重载的两个同名函数

3.2 继承作用域

1. 在父类和派生类中可以定义两个同名的变量,因为父类和派生类是两个不同的作用域

2. 子类和父类如果有同名的变量,子类成员会屏蔽掉父类中同名的成员变量这叫做隐藏或者重定义,可以使用 基类::基类成员 进行显示访问

class Person
{
public:void Print(){cout << "name:" << name << endl;cout << "age:" << age << endl;}string name = "Jesicca";
protected:int age = 20;
};class Student : public Person
{
public:void Stuage(){// 父类同名变量会被屏蔽,构成隐藏cout << "Stuage():" << age << endl;// 显示访问父类同名成员变量cout << "Personage:" << Person::age << endl;}
protected:int stu_id = 20;int age = 19;
};

 看以下这段代码:

class A
{
public:void fun(){cout << "func()" << endl;}
};
class B : public A
{
public:void fun(int i){A::fun();cout << "func(int i)->" << i << endl;}
};
void Test()
{B b;b.fun(10);
};

B中的fun和A中的fun不是构成重载,因为不是在同一作用域
B中的fun和A中的fun构成隐藏成员函数满足函数名相同就构成隐藏。
 


4. 派生类的默认成员函数

默认构造是自己不写,编译器也能自动生成的:构造函数、拷贝构造、析构函数、赋值重载、取地址重载这些都是默认构造(在类和对象的文章中讲过)。

4.1 构造函数和拷贝构造

在派生类中,成员对象大体可以分为三大类:基类中继承下来的、自定义类型、内置类型

对于自定义类型还是调用在身的构造函数,内置类型不做处理。

对于基类中继承下来的成员,必须调用基类的构造函数初始化基类中的部分成员,如果基类中没有默认构造函数,那么必须在派生类的构造函数中显示调用基类的构造函数,不然就会报错。

例如以下的例子:

class Person
{
public:// 没有默认构造函数Person(const char* name):name(name){}void Print(){cout << "name:" << name << endl;//cout << "age:" << age << endl;}
protected:string name;//int age = 20;
};class Student : public Person
{
public:// 由于基类中没有默认生成的构造函数,所以在派生类的构造函数必须显示调用基类的构造函数Student(const char* str, const char* name, int stu_id = 20, int age = 19)// 这边显示调用,类似于匿名对象的调用,要把基类看成一个整体进行调用:Person(name),stu_id(stu_id),age(age),str(str){}void Stuage(){cout << "Stuage():" << age << endl;//cout << "Personage:" << Person::age << endl;}
protected:string str;int stu_id;int age;
};

对于拷贝构造,派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化,例如:

// 父类
Person(const Person& p):name(p.name){}// 子类
Student(const Student& st):Person(st){str = st.str;stu_id = st.stu_id;age = st.age;}

4.2 赋值重载和析构函数

 派生类的operator=必须要调用基类的operator=完成基类的复制。

Person& operator=(const Person& p)
{if (this != &p){name = p.name;}return *this;
}Student& operator=(const Student& st)
{if (this != &st){// 这里如果写成operator=(st);会构成隐藏,会无穷递归Person::operator=(st);str = st.str;stu_id = st.stu_id;age = st.age;}return *this;
}

子类析构函数和父类析构函数构成隐藏关系。

派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能
保证派生类对象先清理派生类成员再清理基类成员的顺序。

~Person()
{cout << "~Person()" << endl;
}~Student()
{cout << "~Student()" << endl;// 会构成隐藏,因此如果写要显示写,// 但是为了保证析构的顺序是先子后父,编译器会在程序结束时自动调用父类的析构函数,不需要显示写,        // 因为在此时可能需要访问父类中的对象,如果析构就访问不了了//Person::~Person();
}


http://www.ppmy.cn/ops/5615.html

相关文章

ZooKeeper初步

1.概述 ZooKeeper 是一个开源的分布式协调服务&#xff0c;通常用于构建分布式系统中的可靠性和协调性。它提供了一个简单的接口&#xff0c;允许开发人员在分布式应用程序中实现常见的任务&#xff0c;如配置管理、命名服务、分布式同步、组成员关系等。 2.工作机制 Zookee…

‘language‘不能作为表名或字段名

今天写一个C#访问Access的程序&#xff0c;拼接SQL语句时一直出错&#xff0c; string sql "insert into dllinfos (dllname,dllfilename,type,functions,harm,repairmethod,issys, paths, ishorse, language, version, company) values (" textBox1.Text ",…

【python】ptthon函数自学

print("Hello Python world!") # 变量 message"Hello" print(message) message"Hello Python world!" print(message)# 引号&#xff1a;可以是单&#xff0c;可以是双 messageI told my friend, "Python is my favorite language!" p…

算法练习|Leetcode189轮转数组 ,Leetcode56合并区间,Leetcode21合并两个有序链表,Leetcode2两数相加,sql总结

目录 一、Leetcode189轮转数组题目描述解题思路方法:切片总结 二、Leetcode56合并区间题目描述解题思路方法:总结 三、Leetcode21合并两个有序链表题目描述解题思路方法:总结 四、Leetcode2两数相加题目描述解题思路方法:总结 sql总结: 一、Leetcode189轮转数组 题目描述 给定…

电脑不能上网,宽带调制解调器出现问题如何处理

目录 一、问题说明 二、解决方案 一、问题说明 内网的设备能互联&#xff0c;内网的各个设备无法连外网。 电脑在检测网络时&#xff0c;出现以下提示&#xff1a; 二、解决方案 首先重启光猫&#xff08;我们是电信宽带&#xff09;。 如果还是有问题&#xff0c;再重启…

有公网IP,如何设置端口映射实现访问?

很多中小型公司或个人会根据自身需求自建服务器&#xff0c;或者将自己内网的服务、应用发布到外网&#xff0c;实现异地访问&#xff0c;如远程桌面、网站、数据库、公司的管理系统、FTP、管家婆、监控系统等等。 没接触过的人可能会觉得这个很难&#xff0c;实际上使用快解析…

如何创建响应式HTML电子邮件模板

在这个适合初学者的指南中&#xff0c;你将学习如何创建一个响应式电子邮件模板。你将跟随逐步说明以及代码片段设计一个在任何设备上都看起来很棒的电子邮件模板。 这个项目非常适合渴望掌握电子邮件设计基础的新手&#xff01; &#xff08;本文视频讲解&#xff1a;java56…

LeetCode 面试经典150题 202.快乐数

题目&#xff1a; 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到 1。如果这个过程 结…